#define NFSDDBG_FACILITY NFSDDBG_PROC
/* Globals */
+static time_t lease_time = 90; /* default lease time */
+static time_t old_lease_time = 90; /* past incarnation lease time */
+static u32 nfs4_reclaim_init = 0;
time_t boot_time;
static time_t grace_end = 0;
static u32 current_clientid = 1;
* protects clientid_hashtbl[], clientstr_hashtbl[],
* unconfstr_hashtbl[], uncofid_hashtbl[].
*/
-static struct semaphore client_sema;
+static DECLARE_MUTEX(client_sema);
void
nfs4_lock_state(void)
((id) & CLIENT_HASH_MASK)
#define clientstr_hashval(name, namelen) \
(opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK)
-
-/* conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed
+/*
+ * reclaim_str_hashtbl[] holds known client info from previous reset/reboot
+ * used in reboot/reset lease grace period processing
+ *
+ * conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed
* setclientid_confirmed info.
*
* unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed
* close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time
* for last close replay.
*/
+static struct list_head reclaim_str_hashtbl[CLIENT_HASH_SIZE];
+static int reclaim_str_hashtbl_size;
static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE];
static struct list_head conf_str_hashtbl[CLIENT_HASH_SIZE];
static struct list_head unconf_str_hashtbl[CLIENT_HASH_SIZE];
create_client(struct xdr_netobj name) {
struct nfs4_client *clp;
- if(!(clp = alloc_client(name)))
+ if (!(clp = alloc_client(name)))
goto out;
INIT_LIST_HEAD(&clp->cl_idhash);
INIT_LIST_HEAD(&clp->cl_strhash);
static int
cmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) {
- if(!n1 || !n2)
+ if (!n1 || !n2)
return 0;
return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len));
}
renew_client(clp);
}
+
+/* a helper function for parse_callback */
+static int
+parse_octet(unsigned int *lenp, char **addrp)
+{
+ unsigned int len = *lenp;
+ char *p = *addrp;
+ int n = -1;
+ char c;
+
+ for (;;) {
+ if (!len)
+ break;
+ len--;
+ c = *p++;
+ if (c == '.')
+ break;
+ if ((c < '0') || (c > '9')) {
+ n = -1;
+ break;
+ }
+ if (n < 0)
+ n = 0;
+ n = (n * 10) + (c - '0');
+ if (n > 255) {
+ n = -1;
+ break;
+ }
+ }
+ *lenp = len;
+ *addrp = p;
+ return n;
+}
+
+/* parse and set the setclientid ipv4 callback address */
+int
+parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp)
+{
+ int temp = 0;
+ u32 cbaddr = 0;
+ u16 cbport = 0;
+ u32 addrlen = addr_len;
+ char *addr = addr_val;
+ int i, shift;
+
+ /* ipaddress */
+ shift = 24;
+ for(i = 4; i > 0 ; i--) {
+ if ((temp = parse_octet(&addrlen, &addr)) < 0) {
+ return 0;
+ }
+ cbaddr |= (temp << shift);
+ if (shift > 0)
+ shift -= 8;
+ }
+ *cbaddrp = cbaddr;
+
+ /* port */
+ shift = 8;
+ for(i = 2; i > 0 ; i--) {
+ if ((temp = parse_octet(&addrlen, &addr)) < 0) {
+ return 0;
+ }
+ cbport |= (temp << shift);
+ if (shift > 0)
+ shift -= 8;
+ }
+ *cbportp = cbport;
+ return 1;
+}
+
+void
+gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
+{
+ struct nfs4_callback *cb = &clp->cl_callback;
+
+ if ( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val,
+ &cb->cb_addr, &cb->cb_port))) {
+ printk(KERN_INFO "NFSD: BAD callback address. client will not receive delegations\n");
+ cb->cb_parsed = 0;
+ return;
+ }
+ cb->cb_netid.len = se->se_callback_netid_len;
+ cb->cb_netid.data = se->se_callback_netid_val;
+ cb->cb_prog = se->se_callback_prog;
+ cb->cb_ident = se->se_callback_ident;
+ cb->cb_parsed = 1;
+}
+
/*
* RFC 3010 has a complex implmentation description of processing a
* SETCLIENTID request consisting of 5 bullets, labeled as
copy_cred(&new->cl_cred,&rqstp->rq_cred);
gen_clid(new);
gen_confirm(new);
+ gen_callback(new, setclid);
add_to_unconfirmed(new, strhashval);
} else if (cmp_verf(&conf->cl_verifier, &clverifier)) {
/*
copy_cred(&new->cl_cred,&rqstp->rq_cred);
copy_clid(new, conf);
gen_confirm(new);
+ gen_callback(new, setclid);
add_to_unconfirmed(new,strhashval);
} else if (!unconf) {
/*
copy_cred(&new->cl_cred,&rqstp->rq_cred);
gen_clid(new);
gen_confirm(new);
+ gen_callback(new, setclid);
add_to_unconfirmed(new, strhashval);
- } else if (!cmp_clid(&conf->cl_clientid, &unconf->cl_clientid) &&
- !cmp_verf(&conf->cl_confirm, &unconf->cl_confirm)) {
+ } else if (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm)) {
/*
* CASE3:
* confirmed found (name, principal match)
* confirmed verifier does not match input clverifier
*
* unconfirmed found (name match)
- * confirmed->cl_clientid != unconfirmed->cl_clientid and
* confirmed->cl_confirm != unconfirmed->cl_confirm
*
* remove unconfirmed.
copy_cred(&new->cl_cred,&rqstp->rq_cred);
gen_clid(new);
gen_confirm(new);
+ gen_callback(new, setclid);
add_to_unconfirmed(new, strhashval);
} else {
/* No cases hit !!! */
setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
setclid->se_clientid.cl_id = new->cl_clientid.cl_id;
memcpy(setclid->se_confirm.data, new->cl_confirm.data, sizeof(setclid->se_confirm.data));
- printk(KERN_INFO "NFSD: this client will not receive delegations\n");
status = nfs_ok;
out:
nfs4_unlock_state();
alloc_file++;
return fp;
}
- return (struct nfs4_file *)NULL;
+ return NULL;
}
static void
while (!list_empty(&file_hashtbl[i])) {
fp = list_entry(file_hashtbl[i].next, struct nfs4_file, fi_hash);
/* this should never be more than once... */
- if(!list_empty(&fp->fi_perfile)) {
+ if (!list_empty(&fp->fi_perfile)) {
printk("ERROR: release_all_files: file %p is open, creating dangling state !!!\n",fp);
}
release_file(fp);
struct nfs4_stateowner *sop;
if ((sop = kmalloc(sizeof(struct nfs4_stateowner),GFP_KERNEL))) {
- if((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) {
+ if ((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) {
memcpy(sop->so_owner.data, owner->data, owner->len);
sop->so_owner.len = owner->len;
return sop;
}
kfree(sop);
}
- return (struct nfs4_stateowner *)NULL;
+ return NULL;
}
/* should use a slab cache */
static void
free_stateowner(struct nfs4_stateowner *sop) {
- if(sop) {
+ if (sop) {
kfree(sop->so_owner.data);
kfree(sop);
sop = NULL;
unsigned int idhashval;
if (!(sop = alloc_stateowner(&open->op_owner)))
- return (struct nfs4_stateowner *)NULL;
+ return NULL;
idhashval = ownerid_hashval(current_ownerid);
INIT_LIST_HEAD(&sop->so_idhash);
INIT_LIST_HEAD(&sop->so_strhash);
}
static void
-release_stateowner(struct nfs4_stateowner *sop)
+unhash_stateowner(struct nfs4_stateowner *sop)
{
struct nfs4_stateid *stp;
list_del(&sop->so_strhash);
list_del(&sop->so_perclient);
list_del(&sop->so_perlockowner);
- list_del(&sop->so_close_lru);
del_perclient++;
while (!list_empty(&sop->so_perfilestate)) {
stp = list_entry(sop->so_perfilestate.next,
struct nfs4_stateid, st_perfilestate);
- if(sop->so_is_open_owner)
+ if (sop->so_is_open_owner)
release_stateid(stp, OPEN_STATE);
else
release_stateid(stp, LOCK_STATE);
}
+}
+
+static void
+release_stateowner(struct nfs4_stateowner *sop)
+{
+ unhash_stateowner(sop);
+ list_del(&sop->so_close_lru);
free_stateowner(sop);
}
list_del_perfile++;
list_del(&stp->st_perfile);
list_del(&stp->st_perfilestate);
- if((stp->st_vfs_set) && (flags & OPEN_STATE)) {
+ if ((stp->st_vfs_set) && (flags & OPEN_STATE)) {
release_stateid_lockowner(stp);
- nfsd_close(&stp->st_vfs_file);
+ nfsd_close(stp->st_vfs_file);
vfsclose++;
- dput(stp->st_vfs_file.f_dentry);
- mntput(stp->st_vfs_file.f_vfsmnt);
} else if ((stp->st_vfs_set) && (flags & LOCK_STATE)) {
- struct file *filp = &stp->st_vfs_file;
+ struct file *filp = stp->st_vfs_file;
locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner);
}
move_to_close_lru(struct nfs4_stateowner *sop)
{
dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop);
- /* remove stateowner from all other hash lists except perclient */
- list_del_init(&sop->so_idhash);
- list_del_init(&sop->so_strhash);
- list_del_init(&sop->so_perlockowner);
- list_add_tail(&sop->so_close_lru, &close_lru);
- sop->so_time = get_seconds();
+ unhash_stateowner(sop);
+ list_add_tail(&sop->so_close_lru, &close_lru);
+ sop->so_time = get_seconds();
}
void
struct nfs4_stateowner *local = NULL;
list_for_each_entry(local, &ownerstr_hashtbl[hashval], so_strhash) {
- if(!cmp_owner_str(local, &open->op_owner, &open->op_clientid))
+ if (!cmp_owner_str(local, &open->op_owner, &open->op_clientid))
continue;
*op = local;
return(1);
*access = 0;
for (i = 1; i < 4; i++) {
- if(test_bit(i, &bmap))
+ if (test_bit(i, &bmap))
*access |= i;
}
}
*deny = 0;
for (i = 0; i < 4; i++) {
- if(test_bit(i, &bmap))
+ if (test_bit(i, &bmap))
*deny |= i ;
}
}
if (share_access & NFS4_SHARE_ACCESS_WRITE) {
status = get_write_access(filp->f_dentry->d_inode);
- if (!status)
- filp->f_mode = FMODE_WRITE;
- else
+ if (status)
return nfserrno(status);
+ filp->f_mode = (filp->f_mode | FMODE_WRITE) & ~FMODE_READ;
}
return nfs_ok;
}
{
if (share_access & NFS4_SHARE_ACCESS_WRITE) {
put_write_access(filp->f_dentry->d_inode);
- filp->f_mode = FMODE_READ;
+ filp->f_mode = (filp->f_mode | FMODE_READ) & ~FMODE_WRITE;
}
}
/* Search for conflicting share reservations */
status = nfserr_share_denied;
list_for_each_entry(stq, &fp->fi_perfile, st_perfile) {
- if(stq->st_stateowner == sop) {
+ if (stq->st_stateowner == sop) {
stp = stq;
continue;
}
goto out_free;
vfsopen++;
- dget(stp->st_vfs_file.f_dentry);
- mntget(stp->st_vfs_file.f_vfsmnt);
init_stateid(stp, fp, sop, open);
stp->st_vfs_set = 1;
share_access &= open->op_share_access;
/* update the struct file */
- if ((status = nfs4_file_upgrade(&stp->st_vfs_file, share_access)))
+ if ((status = nfs4_file_upgrade(stp->st_vfs_file, share_access)))
goto out;
/* remember the open */
set_bit(open->op_share_access, &stp->st_access_bmap);
}
dprintk("NFSD: purging unused open stateowner (so_id %d)\n",
sop->so_id);
- release_stateowner(sop);
+ list_del(&sop->so_close_lru);
+ free_stateowner(sop);
}
if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT)
clientid_val = NFSD_LAUNDROMAT_MINTIMEOUT;
dprintk("NFSD: find_openstateowner_id %d\n", st_id);
if (flags & CLOSE_STATE) {
list_for_each_entry(local, &close_lru, so_close_lru) {
- if(local->so_id == st_id)
+ if (local->so_id == st_id)
return local;
}
}
nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp)
{
return (stp->st_vfs_set == 0 ||
- fhp->fh_dentry->d_inode != stp->st_vfs_file.f_dentry->d_inode);
+ fhp->fh_dentry->d_inode != stp->st_vfs_file->f_dentry->d_inode);
}
static int
status = nfserr_bad_stateid;
- /* for new lock stateowners, check that the lock->v.new.open_stateid
- * refers to an open stateowner, and that the lockclid
- * (nfs4_lock->v.new.clientid) is the same as the
- * open_stateid->st_stateowner->so_client->clientid
+ /* for new lock stateowners:
+ * check that the lock->v.new.open_stateid
+ * refers to an open stateowner
+ *
+ * check that the lockclid (nfs4_lock->v.new.clientid) is the same
+ * as the open_stateid->st_stateowner->so_client->clientid
*/
if (lockclid) {
struct nfs4_stateowner *sop = stp->st_stateowner;
goto out;
}
+/*
+ * eventually, this will perform an upcall to the 'state daemon' as well as
+ * set the cl_first_state field.
+ */
+void
+first_state(struct nfs4_client *clp)
+{
+ if (!clp->cl_first_state)
+ clp->cl_first_state = get_seconds();
+}
+
/*
* nfs4_unlock_state(); called in encode
*/
stp->st_stateid.si_fileid,
stp->st_stateid.si_generation);
status = nfs_ok;
+ first_state(sop->so_client);
out:
return status;
}
goto out;
}
set_access(&share_access, stp->st_access_bmap);
- nfs4_file_downgrade(&stp->st_vfs_file,
+ nfs4_file_downgrade(stp->st_vfs_file,
share_access & ~od->od_share_access);
reset_union_bmap_access(od->od_share_access, &stp->st_access_bmap);
if ((flags & LOCK_STATE) || (flags & RDWR_STATE)) {
hashval = stateid_hashval(st_id, f_id);
list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) {
- if((local->st_stateid.si_stateownerid == st_id) &&
- (local->st_stateid.si_fileid == f_id))
+ if ((local->st_stateid.si_stateownerid == st_id) &&
+ (local->st_stateid.si_fileid == f_id))
return local;
}
}
if ((flags & OPEN_STATE) || (flags & RDWR_STATE)) {
hashval = stateid_hashval(st_id, f_id);
list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) {
- if((local->st_stateid.si_stateownerid == st_id) &&
- (local->st_stateid.si_fileid == f_id))
+ if ((local->st_stateid.si_stateownerid == st_id) &&
+ (local->st_stateid.si_fileid == f_id))
return local;
}
} else
nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny)
{
struct nfs4_stateowner *sop = (struct nfs4_stateowner *) fl->fl_owner;
+ unsigned int hval = lockownerid_hashval(sop->so_id);
deny->ld_sop = NULL;
- if (nfs4_verify_lock_stateowner(sop, fl->fl_pid))
+ if (nfs4_verify_lock_stateowner(sop, hval))
deny->ld_sop = sop;
deny->ld_start = fl->fl_start;
deny->ld_length = ~(u64)0;
deny->ld_type = NFS4_WRITE_LT;
}
+static struct nfs4_stateowner *
+find_lockstateowner(struct xdr_netobj *owner, clientid_t *clid)
+{
+ struct nfs4_stateowner *local = NULL;
+ int i;
+
+ for (i = 0; i < LOCK_HASH_SIZE; i++) {
+ list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) {
+ if (!cmp_owner_str(local, owner, clid))
+ continue;
+ return local;
+ }
+ }
+ return NULL;
+}
static int
find_lockstateowner_str(unsigned int hashval, struct xdr_netobj *owner, clientid_t *clid, struct nfs4_stateowner **op) {
struct nfs4_stateowner *local = NULL;
list_for_each_entry(local, &lock_ownerstr_hashtbl[hashval], so_strhash) {
- if(!cmp_owner_str(local, owner, clid))
+ if (!cmp_owner_str(local, owner, clid))
continue;
*op = local;
return(1);
unsigned int idhashval;
if (!(sop = alloc_stateowner(&lock->lk_new_owner)))
- return (struct nfs4_stateowner *)NULL;
+ return NULL;
idhashval = lockownerid_hashval(current_ownerid);
INIT_LIST_HEAD(&sop->so_idhash);
INIT_LIST_HEAD(&sop->so_strhash);
stp->st_stateid.si_stateownerid = sop->so_id;
stp->st_stateid.si_fileid = fp->fi_id;
stp->st_stateid.si_generation = 0;
- stp->st_vfs_file = open_stp->st_vfs_file;
+ stp->st_vfs_file = open_stp->st_vfs_file; /* FIXME refcount?? */
stp->st_vfs_set = open_stp->st_vfs_set;
stp->st_access_bmap = open_stp->st_access_bmap;
stp->st_deny_bmap = open_stp->st_deny_bmap;
check_lock_length(u64 offset, u64 length)
{
return ((length == 0) || ((length != ~(u64)0) &&
- LOFF_OVERFLOW(offset, length)));
+ LOFF_OVERFLOW(offset, length)));
}
/*
if (nfs4_in_grace() && !lock->lk_reclaim)
return nfserr_grace;
- if (nfs4_in_no_grace() && lock->lk_reclaim)
+ if (!nfs4_in_grace() && lock->lk_reclaim)
return nfserr_no_grace;
if (check_lock_length(lock->lk_offset, lock->lk_length))
printk("NFSD: nfsd4_lock: clientid is stale!\n");
goto out;
}
- /* does the clientid in the lock owner own the open stateid? */
+
+ /* is the new lock seqid presented by the client zero? */
+ status = nfserr_bad_seqid;
+ if (lock->v.new.lock_seqid != 0)
+ goto out;
/* validate and update open stateid and open seqid */
status = nfs4_preprocess_seqid_op(current_fh,
strhashval = lock_ownerstr_hashval(fp->fi_inode,
open_sop->so_client->cl_clientid.cl_id,
lock->v.new.owner);
-
/*
* If we already have this lock owner, the client is in
* error (or our bookeeping is wrong!)
* for asking for a 'new lock'.
*/
status = nfserr_bad_stateid;
- if (find_lockstateowner_str(strhashval, &lock->v.new.owner,
- &lock->v.new.clientid, &lock_sop))
+ lock_sop = find_lockstateowner(&lock->v.new.owner,
+ &lock->v.new.clientid);
+ if (lock_sop)
goto out;
status = nfserr_resource;
if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock)))
goto out;
if ((lock_stp = alloc_init_lock_stateid(lock->lk_stateowner,
- fp, open_stp)) == NULL)
+ fp, open_stp)) == NULL) {
+ release_stateowner(lock->lk_stateowner);
goto out;
+ }
/* bump the open seqid used to create the lock */
open_sop->so_seqid++;
} else {
goto out;
}
/* lock->lk_stateowner and lock_stp have been created or found */
- filp = &lock_stp->st_vfs_file;
+ filp = lock_stp->st_vfs_file;
if ((status = fh_verify(rqstp, current_fh, S_IFREG, MAY_LOCK))) {
printk("NFSD: nfsd4_lock: permission denied!\n");
goto out;
}
+ locks_init_lock(&file_lock);
switch (lock->lk_type) {
case NFS4_READ_LT:
case NFS4_READW_LT:
goto out;
}
file_lock.fl_owner = (fl_owner_t) lock->lk_stateowner;
- file_lock.fl_pid = lockownerid_hashval(lock->lk_stateowner->so_id);
+ file_lock.fl_pid = current->tgid;
file_lock.fl_file = filp;
file_lock.fl_flags = FL_POSIX;
- file_lock.fl_notify = NULL;
- file_lock.fl_insert = NULL;
- file_lock.fl_remove = NULL;
file_lock.fl_start = lock->lk_offset;
if ((lock->lk_length == ~(u64)0) ||
*/
status = posix_lock_file(filp, &file_lock);
- dprintk("NFSD: nfsd4_lock: posix_test_lock passed. posix_lock_file status %d\n",status);
+ if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
+ file_lock.fl_ops->fl_release_private(&file_lock);
+ dprintk("NFSD: nfsd4_lock: posix_lock_file status %d\n",status);
switch (-status) {
case 0: /* success! */
update_stateid(&lock_stp->st_stateid);
nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lockt *lockt)
{
struct inode *inode;
- struct nfs4_stateowner *sop;
struct file file;
struct file_lock file_lock;
struct file_lock *conflicting_lock;
}
inode = current_fh->fh_dentry->d_inode;
+ locks_init_lock(&file_lock);
switch (lockt->lt_type) {
case NFS4_READ_LT:
case NFS4_READW_LT:
find_lockstateowner_str(strhashval, &lockt->lt_owner,
&lockt->lt_clientid,
&lockt->lt_stateowner);
- sop = lockt->lt_stateowner;
- if (sop) {
- file_lock.fl_owner = (fl_owner_t) sop;
- file_lock.fl_pid = lockownerid_hashval(sop->so_id);
- } else {
- file_lock.fl_owner = NULL;
- file_lock.fl_pid = 0;
- }
+ if (lockt->lt_stateowner)
+ file_lock.fl_owner = (fl_owner_t)lockt->lt_stateowner;
+ file_lock.fl_pid = current->tgid;
file_lock.fl_flags = FL_POSIX;
file_lock.fl_start = lockt->lt_offset;
&locku->lu_stateowner, &stp, NULL)))
goto out;
- filp = &stp->st_vfs_file;
+ filp = stp->st_vfs_file;
BUG_ON(!filp);
+ locks_init_lock(&file_lock);
file_lock.fl_type = F_UNLCK;
file_lock.fl_owner = (fl_owner_t) locku->lu_stateowner;
- file_lock.fl_pid = lockownerid_hashval(locku->lu_stateowner->so_id);
+ file_lock.fl_pid = current->tgid;
file_lock.fl_file = filp;
file_lock.fl_flags = FL_POSIX;
- file_lock.fl_notify = NULL;
- file_lock.fl_insert = NULL;
- file_lock.fl_remove = NULL;
file_lock.fl_start = locku->lu_offset;
if ((locku->lu_length == ~(u64)0) || LOFF_OVERFLOW(locku->lu_offset, locku->lu_length))
* Try to unlock the file in the VFS.
*/
status = posix_lock_file(filp, &file_lock);
+ if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
+ file_lock.fl_ops->fl_release_private(&file_lock);
if (status) {
printk("NFSD: nfs4_locku: posix_lock_file failed!\n");
goto out_nfserr;
clientid_t *clid = &rlockowner->rl_clientid;
struct nfs4_stateowner *local = NULL;
struct xdr_netobj *owner = &rlockowner->rl_owner;
- int status, i;
+ int status;
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
clid->cl_boot, clid->cl_id);
nfs4_lock_state();
- /* find the lockowner */
- status = nfs_ok;
- for (i=0; i < LOCK_HASH_SIZE; i++) {
- list_for_each_entry(local, &lock_ownerstr_hashtbl[i], so_strhash) {
- if(cmp_owner_str(local, owner, clid))
- break;
- }
- }
+ status = nfs_ok;
+ local = find_lockstateowner(owner, clid);
if (local) {
struct nfs4_stateid *stp;
- /* check for any locks held by any stateid associated with the
- * (lock) stateowner */
+ /* check for any locks held by any stateid
+ * associated with the (lock) stateowner */
status = nfserr_locks_held;
- list_for_each_entry(stp, &local->so_perfilestate, st_perfilestate) {
- if(stp->st_vfs_set) {
- if (check_for_locks(&stp->st_vfs_file, local))
+ list_for_each_entry(stp, &local->so_perfilestate,
+ st_perfilestate) {
+ if (stp->st_vfs_set) {
+ if (check_for_locks(stp->st_vfs_file, local))
goto out;
}
}
return status;
}
+static inline struct nfs4_client_reclaim *
+alloc_reclaim(int namelen)
+{
+ struct nfs4_client_reclaim *crp = NULL;
+
+ crp = kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL);
+ if (!crp)
+ return NULL;
+ crp->cr_name.data = kmalloc(namelen, GFP_KERNEL);
+ if (!crp->cr_name.data) {
+ kfree(crp);
+ return NULL;
+ }
+ return crp;
+}
+
+/*
+ * failure => all reset bets are off, nfserr_no_grace...
+ */
+static int
+nfs4_client_to_reclaim(struct nfs4_client *clp)
+{
+ unsigned int strhashval;
+ struct nfs4_client_reclaim *crp = NULL;
+
+ crp = alloc_reclaim(clp->cl_name.len);
+ if (!crp)
+ return 0;
+ strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len);
+ INIT_LIST_HEAD(&crp->cr_strhash);
+ list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]);
+ memcpy(crp->cr_name.data, clp->cl_name.data, clp->cl_name.len);
+ crp->cr_name.len = clp->cl_name.len;
+ crp->cr_first_state = clp->cl_first_state;
+ crp->cr_expired = 0;
+ return 1;
+}
+
+static void
+nfs4_release_reclaim(void)
+{
+ struct nfs4_client_reclaim *crp = NULL;
+ int i;
+
+ BUG_ON(!nfs4_reclaim_init);
+ for (i = 0; i < CLIENT_HASH_SIZE; i++) {
+ while (!list_empty(&reclaim_str_hashtbl[i])) {
+ crp = list_entry(reclaim_str_hashtbl[i].next,
+ struct nfs4_client_reclaim, cr_strhash);
+ list_del(&crp->cr_strhash);
+ kfree(crp->cr_name.data);
+ kfree(crp);
+ reclaim_str_hashtbl_size--;
+ }
+ }
+ BUG_ON(reclaim_str_hashtbl_size);
+}
+
+/*
+ * called from OPEN, CLAIM_PREVIOUS with a new clientid. */
+struct nfs4_client_reclaim *
+nfs4_find_reclaim_client(clientid_t *clid)
+{
+ unsigned int idhashval = clientid_hashval(clid->cl_id);
+ unsigned int strhashval;
+ struct nfs4_client *clp, *client = NULL;
+ struct nfs4_client_reclaim *crp = NULL;
+
+
+ /* find clientid in conf_id_hashtbl */
+ list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
+ if (cmp_clid(&clp->cl_clientid, clid)) {
+ client = clp;
+ break;
+ }
+ }
+ if (!client)
+ return NULL;
+
+ /* find clp->cl_name in reclaim_str_hashtbl */
+ strhashval = clientstr_hashval(client->cl_name.data,
+ client->cl_name.len);
+ list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) {
+ if (cmp_name(&crp->cr_name, &client->cl_name)) {
+ return crp;
+ }
+ }
+ return NULL;
+}
+
+/*
+* Called from OPEN. Look for clientid in reclaim list.
+*/
+int
+nfs4_check_open_reclaim(clientid_t *clid)
+{
+ struct nfs4_client_reclaim *crp;
+
+ if ((crp = nfs4_find_reclaim_client(clid)) == NULL)
+ return nfserr_reclaim_bad;
+ if (crp->cr_expired)
+ return nfserr_no_grace;
+ return nfs_ok;
+}
+
+
/*
* Start and stop routines
*/
nfs4_state_init(void)
{
int i;
- time_t start = get_seconds();
+ time_t grace_time;
if (nfs4_init)
return;
+ if (!nfs4_reclaim_init) {
+ for (i = 0; i < CLIENT_HASH_SIZE; i++)
+ INIT_LIST_HEAD(&reclaim_str_hashtbl[i]);
+ reclaim_str_hashtbl_size = 0;
+ nfs4_reclaim_init = 1;
+ }
for (i = 0; i < CLIENT_HASH_SIZE; i++) {
INIT_LIST_HEAD(&conf_id_hashtbl[i]);
INIT_LIST_HEAD(&conf_str_hashtbl[i]);
INIT_LIST_HEAD(&close_lru);
INIT_LIST_HEAD(&client_lru);
- init_MUTEX(&client_sema);
- boot_time = start;
- grace_end = start + NFSD_LEASE_TIME;
+ boot_time = get_seconds();
+ grace_time = max(old_lease_time, lease_time);
+ if (reclaim_str_hashtbl_size == 0)
+ grace_time = 0;
+ if (grace_time)
+ printk("NFSD: starting %ld-second grace period\n", grace_time);
+ grace_end = boot_time + grace_time;
INIT_WORK(&laundromat_work,laundromat_main, NULL);
schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ);
nfs4_init = 1;
-
}
int
nfs4_in_grace(void)
{
- return time_before(get_seconds(), (unsigned long)grace_end);
+ return get_seconds() < grace_end;
}
-int
-nfs4_in_no_grace(void)
+void
+set_no_grace(void)
{
- return (grace_end < get_seconds());
+ printk("NFSD: ERROR in reboot recovery. State reclaims will fail.\n");
+ grace_end = get_seconds();
}
+time_t
+nfs4_lease_time(void)
+{
+ return lease_time;
+}
static void
__nfs4_state_shutdown(void)
nfs4_state_shutdown(void)
{
nfs4_lock_state();
+ nfs4_release_reclaim();
__nfs4_state_shutdown();
nfs4_unlock_state();
}
+
+/*
+ * Called when leasetime is changed.
+ *
+ * if nfsd is not started, simply set the global lease.
+ *
+ * if nfsd(s) are running, lease change requires nfsv4 state to be reset.
+ * e.g: boot_time is reset, existing nfs4_client structs are
+ * used to fill reclaim_str_hashtbl, then all state (except for the
+ * reclaim_str_hashtbl) is re-initialized.
+ *
+ * if the old lease time is greater than the new lease time, the grace
+ * period needs to be set to the old lease time to allow clients to reclaim
+ * their state. XXX - we may want to set the grace period == lease time
+ * after an initial grace period == old lease time
+ *
+ * if an error occurs in this process, the new lease is set, but the server
+ * will not honor OPEN or LOCK reclaims, and will return nfserr_no_grace
+ * which means OPEN/LOCK/READ/WRITE will fail during grace period.
+ *
+ * clients will attempt to reset all state with SETCLIENTID/CONFIRM, and
+ * OPEN and LOCK reclaims.
+ */
+void
+nfs4_reset_lease(time_t leasetime)
+{
+ struct nfs4_client *clp;
+ int i;
+
+ printk("NFSD: New leasetime %ld\n",leasetime);
+ if (!nfs4_init)
+ return;
+ nfs4_lock_state();
+ old_lease_time = lease_time;
+ lease_time = leasetime;
+
+ nfs4_release_reclaim();
+
+ /* populate reclaim_str_hashtbl with current confirmed nfs4_clientid */
+ for (i = 0; i < CLIENT_HASH_SIZE; i++) {
+ list_for_each_entry(clp, &conf_id_hashtbl[i], cl_idhash) {
+ if (!nfs4_client_to_reclaim(clp)) {
+ nfs4_release_reclaim();
+ goto init_state;
+ }
+ reclaim_str_hashtbl_size++;
+ }
+ }
+init_state:
+ __nfs4_state_shutdown();
+ nfs4_state_init();
+ nfs4_unlock_state();
+}
+