#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)
down(&client_sema);
}
-/*
- * nfs4_unlock_state(); called in encode
- */
void
nfs4_unlock_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);
}
}
+/* should use a slab cache */
+void
+nfs4_free_stateowner(struct kref *kref)
+{
+ struct nfs4_stateowner *sop =
+ container_of(kref, struct nfs4_stateowner, so_ref);
+ kfree(sop->so_owner.data);
+ kfree(sop);
+ free_sowner++;
+}
+
static inline struct nfs4_stateowner *
alloc_stateowner(struct xdr_netobj *owner)
{
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;
+ kref_init(&sop->so_ref);
return sop;
}
kfree(sop);
}
- return (struct nfs4_stateowner *)NULL;
-}
-
-/* should use a slab cache */
-static void
-free_stateowner(struct nfs4_stateowner *sop) {
- if(sop) {
- kfree(sop->so_owner.data);
- kfree(sop);
- sop = NULL;
- free_sowner++;
- }
+ return NULL;
}
static struct nfs4_stateowner *
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);
}
- free_stateowner(sop);
+}
+
+static void
+release_stateowner(struct nfs4_stateowner *sop)
+{
+ unhash_stateowner(sop);
+ list_del(&sop->so_close_lru);
+ nfs4_put_stateowner(sop);
}
static inline void
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 ;
}
}
return nfs_ok;
}
-static inline int
-nfs4_file_upgrade(struct file *filp, unsigned int share_access)
-{
-int status;
-
- if (share_access & NFS4_SHARE_ACCESS_WRITE) {
- status = get_write_access(filp->f_dentry->d_inode);
- if (!status)
- filp->f_mode = FMODE_WRITE;
- else
- return nfserrno(status);
- }
- return nfs_ok;
-}
-
static inline void
nfs4_file_downgrade(struct file *filp, unsigned int share_access)
{
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;
}
}
status = nfserr_reclaim_bad;
return status;
}
+
+static int
+nfs4_check_open(struct nfs4_file *fp, struct nfs4_stateowner *sop, struct nfsd4_open *open, struct nfs4_stateid **stpp)
+{
+ struct nfs4_stateid *local;
+ int status = nfserr_share_denied;
+
+ list_for_each_entry(local, &fp->fi_perfile, st_perfile) {
+ /* have we seen this open owner */
+ if (local->st_stateowner == sop) {
+ *stpp = local;
+ continue;
+ }
+ /* ignore lock owners */
+ if (local->st_stateowner->so_is_open_owner == 0)
+ continue;
+ /* check for conflicting share reservations */
+ if (!test_share(local, open))
+ goto out;
+ }
+ status = 0;
+out:
+ return status;
+}
+
+static int
+nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp,
+ struct svc_fh *cur_fh, int flags)
+{
+ struct nfs4_stateid *stp;
+ int status;
+
+ stp = kmalloc(sizeof(struct nfs4_stateid), GFP_KERNEL);
+ if (stp == NULL)
+ return nfserr_resource;
+
+ status = nfsd_open(rqstp, cur_fh, S_IFREG, flags, &stp->st_vfs_file);
+ if (status) {
+ if (status == nfserr_dropit)
+ status = nfserr_jukebox;
+ kfree(stp);
+ return status;
+ }
+ vfsopen++;
+ stp->st_vfs_set = 1;
+ *stpp = stp;
+ return 0;
+}
+
+static int
+nfs4_upgrade_open(struct svc_rqst *rqstp, struct svc_fh *cur_fh, struct nfs4_stateid *stp, struct nfsd4_open *open)
+{
+ struct file *filp = stp->st_vfs_file;
+ struct inode *inode = filp->f_dentry->d_inode;
+ unsigned int share_access;
+ int status;
+
+ set_access(&share_access, stp->st_access_bmap);
+ share_access = ~share_access;
+ share_access &= open->op_share_access;
+
+ /* update the struct file */
+ if (share_access & NFS4_SHARE_ACCESS_WRITE) {
+ status = get_write_access(inode);
+ if (status)
+ return nfserrno(status);
+ if (open->op_truncate) {
+ struct iattr iattr = {
+ .ia_valid = ATTR_SIZE,
+ .ia_size = 0,
+ };
+ status = nfsd_setattr(rqstp, cur_fh, &iattr, 0,
+ (time_t)0);
+ if (status) {
+ put_write_access(inode);
+ return status;
+ }
+ }
+
+ /* remember the open */
+ filp->f_mode = (filp->f_mode | FMODE_WRITE) & ~FMODE_READ;
+ set_bit(open->op_share_access, &stp->st_access_bmap);
+ set_bit(open->op_share_deny, &stp->st_deny_bmap);
+ }
+ return nfs_ok;
+}
+
+
+/* decrement seqid on successful reclaim, it will be bumped in encode_open */
+static void
+nfs4_set_claim_prev(struct nfsd4_open *open, int *status)
+{
+ if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) {
+ if (*status)
+ *status = nfserr_reclaim_bad;
+ else {
+ open->op_stateowner->so_confirmed = 1;
+ open->op_stateowner->so_seqid--;
+ }
+ }
+}
+
/*
* called with nfs4_lock_state() held.
*/
int
nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
- struct iattr iattr;
struct nfs4_stateowner *sop = open->op_stateowner;
struct nfs4_file *fp = NULL;
struct inode *ino;
unsigned int fi_hashval;
- struct nfs4_stateid *stq, *stp = NULL;
+ struct nfs4_stateid *stp = NULL;
int status;
status = nfserr_resource;
status = nfserr_inval;
if (!TEST_ACCESS(open->op_share_access) || !TEST_DENY(open->op_share_deny))
goto out;
-
+ /*
+ * Lookup file; if found, lookup stateid and check open request;
+ * not found, create
+ */
fi_hashval = file_hashval(ino);
if (find_file(fi_hashval, ino, &fp)) {
- /* Search for conflicting share reservations */
- status = nfserr_share_denied;
- list_for_each_entry(stq, &fp->fi_perfile, st_perfile) {
- if(stq->st_stateowner == sop) {
- stp = stq;
- continue;
- }
- /* ignore lock owners */
- if (stq->st_stateowner->so_is_open_owner == 0)
- continue;
- if (!test_share(stq,open))
- goto out;
- }
+ status = nfs4_check_open(fp, sop, open, &stp);
+ if (status)
+ goto out;
} else {
- /* No nfs4_file found; allocate and init a new one */
status = nfserr_resource;
if ((fp = alloc_init_file(fi_hashval, ino)) == NULL)
goto out;
}
- if (!stp) {
- int flags = 0;
-
- status = nfserr_resource;
- if ((stp = kmalloc(sizeof(struct nfs4_stateid),
- GFP_KERNEL)) == NULL)
+ /*
+ * OPEN the file, or upgrade an existing OPEN.
+ * If truncate fails, the OPEN fails.
+ */
+ if (stp) {
+ /* Stateid was found, this is an OPEN upgrade */
+ status = nfs4_upgrade_open(rqstp, current_fh, stp, open);
+ if (status)
goto out;
-
+ } else {
+ /* Stateid was not found, this is a new OPEN */
+ int flags = 0;
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
flags = MAY_WRITE;
else
flags = MAY_READ;
- if ((status = nfsd_open(rqstp, current_fh, S_IFREG,
- flags,
- &stp->st_vfs_file)) != 0)
- 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;
- } else {
- /* This is an upgrade of an existing OPEN.
- * OR the incoming share with the existing
- * nfs4_stateid share */
- unsigned int share_access;
-
- set_access(&share_access, stp->st_access_bmap);
- share_access = ~share_access;
- share_access &= open->op_share_access;
-
- /* update the struct file */
- if ((status = nfs4_file_upgrade(&stp->st_vfs_file, share_access)))
+ if ((status = nfs4_new_open(rqstp, &stp, current_fh, flags)))
goto out;
- /* remember the open */
- set_bit(open->op_share_access, &stp->st_access_bmap);
- set_bit(open->op_share_deny, &stp->st_deny_bmap);
- /* bump the stateid */
- update_stateid(&stp->st_stateid);
+ init_stateid(stp, fp, sop, open);
+ if (open->op_truncate) {
+ struct iattr iattr = {
+ .ia_valid = ATTR_SIZE,
+ .ia_size = 0,
+ };
+ status = nfsd_setattr(rqstp, current_fh, &iattr, 0,
+ (time_t)0);
+ if (status) {
+ release_stateid(stp, OPEN_STATE);
+ goto out;
+ }
+ }
}
- dprintk("nfs4_process_open2: stateid=(%08x/%08x/%08x/%08x)\n\n",
+ dprintk("nfs4_process_open2: stateid=(%08x/%08x/%08x/%08x)\n",
stp->st_stateid.si_boot, stp->st_stateid.si_stateownerid,
stp->st_stateid.si_fileid, stp->st_stateid.si_generation);
- if (open->op_truncate) {
- iattr.ia_valid = ATTR_SIZE;
- iattr.ia_size = 0;
- status = nfsd_setattr(rqstp, current_fh, &iattr, 0, (time_t)0);
- if (status)
- goto out;
- }
memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t));
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
status = nfs_ok;
out:
+ /* take the opportunity to clean up unused state */
if (fp && list_empty(&fp->fi_perfile))
release_file(fp);
- if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) {
- if (status)
- status = nfserr_reclaim_bad;
- else {
- /* successful reclaim. so_seqid is decremented because
- * it will be bumped in encode_open
- */
- open->op_stateowner->so_confirmed = 1;
- open->op_stateowner->so_seqid--;
- }
- }
+ /* CLAIM_PREVIOUS has different error returns */
+ nfs4_set_claim_prev(open, &status);
/*
* To finish the open response, we just need to set the rflags.
*/
- open->op_rflags = 0;
+ open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX;
if (!open->op_stateowner->so_confirmed)
open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM;
return status;
-out_free:
- kfree(stp);
- goto out;
}
static struct work_struct laundromat_work;
}
dprintk("NFSD: purging unused open stateowner (so_id %d)\n",
sop->so_id);
- release_stateowner(sop);
+ list_del(&sop->so_close_lru);
+ nfs4_put_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;
}
/*
- * nfs4_unlock_state(); called in encode
+ * 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();
+}
+
int
nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_confirm *oc)
{
if ((status = fh_verify(rqstp, current_fh, S_IFREG, 0)))
goto out;
- oc->oc_stateowner = NULL;
nfs4_lock_state();
if ((status = nfs4_preprocess_seqid_op(current_fh, oc->oc_seqid,
stp->st_stateid.si_fileid,
stp->st_stateid.si_generation);
status = nfs_ok;
+ first_state(sop->so_client);
out:
+ if (oc->oc_stateowner)
+ nfs4_get_stateowner(oc->oc_stateowner);
+ nfs4_unlock_state();
return status;
}
}
}
-/*
- * nfs4_unlock_state(); called in encode
- */
-
int
nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_downgrade *od)
{
(int)current_fh->fh_dentry->d_name.len,
current_fh->fh_dentry->d_name.name);
- od->od_stateowner = NULL;
- status = nfserr_inval;
if (!TEST_ACCESS(od->od_share_access) || !TEST_DENY(od->od_share_deny))
- goto out;
+ return nfserr_inval;
nfs4_lock_state();
if ((status = nfs4_preprocess_seqid_op(current_fh, od->od_seqid,
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);
memcpy(&od->od_stateid, &stp->st_stateid, sizeof(stateid_t));
status = nfs_ok;
out:
+ if (od->od_stateowner)
+ nfs4_get_stateowner(od->od_stateowner);
+ nfs4_unlock_state();
return status;
}
(int)current_fh->fh_dentry->d_name.len,
current_fh->fh_dentry->d_name.name);
- close->cl_stateowner = NULL;
nfs4_lock_state();
/* check close_lru for replay */
if ((status = nfs4_preprocess_seqid_op(current_fh, close->cl_seqid,
/* release_state_owner() calls nfsd_close() if needed */
release_state_owner(stp, &close->cl_stateowner, OPEN_STATE);
out:
+ if (close->cl_stateowner)
+ nfs4_get_stateowner(close->cl_stateowner);
+ nfs4_unlock_state();
return status;
}
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)) {
+ kref_get(&sop->so_ref);
deny->ld_sop = sop;
+ deny->ld_clientid = sop->so_client->cl_clientid;
+ }
deny->ld_start = fl->fl_start;
deny->ld_length = ~(u64)0;
if (fl->fl_end != ~(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)));
}
/*
* LOCK operation
- *
- * nfs4_unlock_state(); called in encode
*/
int
nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock *lock)
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))
return nfserr_inval;
- lock->lk_stateowner = NULL;
nfs4_lock_state();
if (lock->lk_is_new) {
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);
+ lock->lk_stateowner = NULL;
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);
release_state_owner(lock_stp, &lock->lk_stateowner, LOCK_STATE);
}
out:
+ if (lock->lk_stateowner)
+ nfs4_get_stateowner(lock->lk_stateowner);
+ nfs4_unlock_state();
return status;
}
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;
if (check_lock_length(locku->lu_offset, locku->lu_length))
return nfserr_inval;
- locku->lu_stateowner = NULL;
nfs4_lock_state();
if ((status = nfs4_preprocess_seqid_op(current_fh,
&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;
memcpy(&locku->lu_stateid, &stp->st_stateid, sizeof(stateid_t));
out:
+ if (locku->lu_stateowner)
+ nfs4_get_stateowner(locku->lu_stateowner);
+ nfs4_unlock_state();
return status;
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();
}
+