blob: d4a2107e3b854e5f58fdb4f03a019938f3eb71b4 [file] [log] [blame]
/*
* vim:noexpandtab:shiftwidth=8:tabstop=8:
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* ---------------------------------------
*/
/**
* @file nfs_reaper_thread.c
* @brief check for expired clients and whack them.
*/
#include "config.h"
#include <pthread.h>
#include <unistd.h>
#include "log.h"
#include "nfs4.h"
#include "sal_functions.h"
#include "nfs_proto_functions.h"
#include "nfs_core.h"
#include "log.h"
#include "fridgethr.h"
#define REAPER_DELAY 10
unsigned int reaper_delay = REAPER_DELAY;
static struct fridgethr *reaper_fridge;
static int reap_hash_table(hash_table_t *ht_reap)
{
struct rbt_head *head_rbt;
struct hash_data *addr = NULL;
uint32_t i;
struct rbt_node *pn;
nfs_client_id_t *client_id;
nfs_client_record_t *client_rec;
int count = 0;
/* For each bucket of the requested hashtable */
for (i = 0; i < ht_reap->parameter.index_size; i++) {
head_rbt = &ht_reap->partitions[i].rbt;
restart:
/* acquire mutex */
PTHREAD_RWLOCK_wrlock(&ht_reap->partitions[i].lock);
/* go through all entries in the red-black-tree */
RBT_LOOP(head_rbt, pn) {
char str[LOG_BUFF_LEN];
struct display_buffer dspbuf = {sizeof(str), str, str};
bool str_valid = false;
addr = RBT_OPAQ(pn);
client_id = addr->val.addr;
count++;
PTHREAD_MUTEX_lock(&client_id->cid_mutex);
if (valid_lease(client_id)) {
PTHREAD_MUTEX_unlock(&client_id->cid_mutex);
RBT_INCREMENT(pn);
continue;
}
if (isDebug(COMPONENT_CLIENTID)) {
display_client_id_rec(&dspbuf, client_id);
LogFullDebug(COMPONENT_CLIENTID,
"Expire index %d %s", i, str);
str_valid = true;
}
/* Get the client record */
client_rec = client_id->cid_client_record;
/* get a ref to client_id as we might drop the
* last reference with expiring.
*/
inc_client_id_ref(client_id);
/* if record is STALE, the linkage to client_record is
* removed already. Acquire a ref on client record
* before we drop the mutex on clientid
*/
if (client_rec != NULL)
inc_client_record_ref(client_rec);
PTHREAD_MUTEX_unlock(&client_id->cid_mutex);
if (client_rec != NULL)
PTHREAD_MUTEX_lock(&client_rec->cr_mutex);
PTHREAD_RWLOCK_unlock(&ht_reap->partitions[i].lock);
nfs_client_id_expire(client_id, false);
if (client_rec != NULL) {
PTHREAD_MUTEX_unlock(&client_rec->cr_mutex);
dec_client_record_ref(client_rec);
}
if (isFullDebug(COMPONENT_CLIENTID)) {
if (!str_valid)
display_printf(&dspbuf, "clientid %p",
client_id);
LogFullDebug(COMPONENT_CLIENTID,
"Reaper done, expired {%s}", str);
}
/* drop our reference to the client_id */
dec_client_id_ref(client_id);
goto restart;
}
PTHREAD_RWLOCK_unlock(&ht_reap->partitions[i].lock);
}
return count;
}
static int reap_expired_open_owners(void)
{
int count = 0;
time_t tnow = time(NULL);
time_t texpire;
state_owner_t *owner;
PTHREAD_MUTEX_lock(&cached_open_owners_lock);
/* Walk the list of cached NFS 4 open owners. Because we hold
* the mutex while walking this list, it is impossible for another
* thread to get a primary reference to these owners while we
* process, and thus prevent them from expiring.
*/
owner = glist_first_entry(&cached_open_owners,
state_owner_t,
so_owner.so_nfs4_owner.so_state_list);
while (owner != NULL) {
struct state_nfs4_owner_t *nfs4_owner;
nfs4_owner = &owner->so_owner.so_nfs4_owner;
texpire = atomic_fetch_time_t(&nfs4_owner->cache_expire);
if (texpire > tnow) {
/* This owner has not yet expired. */
if (isFullDebug(COMPONENT_STATE)) {
char str[LOG_BUFF_LEN];
struct display_buffer dspbuf = {
sizeof(str), str, str};
display_owner(&dspbuf, owner);
LogFullDebug(COMPONENT_STATE,
"Did not release CLOSE_PENDING %d seconds left for {%s}",
(int) (texpire - tnow),
str);
}
/* Because entries are not moved on this list, and
* they are added when they first become eligible,
* the entries are in order of expiration time, and
* thus once we hit one that is not expired yet, the
* rest are also not expired.
*/
break;
} else {
/* This cached owner has expired, uncache it. */
uncache_nfs4_owner(nfs4_owner);
count++;
/* Get the next owner to examine. */
owner = glist_first_entry(
&cached_open_owners,
state_owner_t,
so_owner.so_nfs4_owner.so_state_list);
}
}
PTHREAD_MUTEX_unlock(&cached_open_owners_lock);
return count;
}
struct reaper_state {
bool old_state_cleaned;
size_t count;
bool logged;
bool in_grace;
};
static struct reaper_state reaper_state = {
.old_state_cleaned = false,
.count = 0,
.logged = false,
.in_grace = false
};
static void reaper_run(struct fridgethr_context *ctx)
{
struct reaper_state *rst = ctx->arg;
SetNameFunction("reaper");
rst->in_grace = nfs_in_grace();
if (!rst->old_state_cleaned) {
/* if not in grace period, clean up the old state */
if (!rst->in_grace) {
nfs4_clean_old_recov_dir(v4_old_dir);
rst->old_state_cleaned = true;
}
}
if (isDebug(COMPONENT_CLIENTID) && ((rst->count > 0) || !rst->logged)) {
LogDebug(COMPONENT_CLIENTID,
"Now checking NFS4 clients for expiration");
rst->logged = (rst->count == 0);
#ifdef DEBUG_SAL
if (rst->count == 0) {
dump_all_states();
dump_all_owners();
}
#endif
}
rst->count =
(reap_hash_table(ht_confirmed_client_id) +
reap_hash_table(ht_unconfirmed_client_id));
rst->count += reap_expired_open_owners();
}
int reaper_init(void)
{
struct fridgethr_params frp;
int rc = 0;
if (nfs_param.nfsv4_param.lease_lifetime < (2 * REAPER_DELAY))
reaper_delay = nfs_param.nfsv4_param.lease_lifetime / 2;
memset(&frp, 0, sizeof(struct fridgethr_params));
frp.thr_max = 1;
frp.thr_min = 1;
frp.thread_delay = reaper_delay;
frp.flavor = fridgethr_flavor_looper;
rc = fridgethr_init(&reaper_fridge, "reaper", &frp);
if (rc != 0) {
LogMajor(COMPONENT_CLIENTID,
"Unable to initialize reaper fridge, error code %d.",
rc);
return rc;
}
rc = fridgethr_submit(reaper_fridge, reaper_run, &reaper_state);
if (rc != 0) {
LogMajor(COMPONENT_CLIENTID,
"Unable to start reaper thread, error code %d.", rc);
return rc;
}
return 0;
}
int reaper_shutdown(void)
{
int rc = fridgethr_sync_command(reaper_fridge,
fridgethr_comm_stop,
120);
if (rc == ETIMEDOUT) {
LogMajor(COMPONENT_CLIENTID,
"Shutdown timed out, cancelling threads.");
fridgethr_cancel(reaper_fridge);
} else if (rc != 0) {
LogMajor(COMPONENT_CLIENTID,
"Failed shutting down reaper thread: %d", rc);
}
return rc;
}