From: Bryan Schumaker <[email protected]>
These patches add FREE_STATEID and TEST_STATEID to the NFS server. I
appreciate any comments, so there might be a better way of doing things.
Thanks!
- Bryan
On 05/09/2011 08:49 PM, J. Bruce Fields wrote:
> On Fri, May 06, 2011 at 02:57:35PM -0400, [email protected] wrote:
>> From: Bryan Schumaker <[email protected]>
>>
>> This operation is used by the client to check the validity of a list of
>> stateids.
>>
>> Signed-off-by: Bryan Schumaker <[email protected]>
>> ---
>> fs/nfsd/nfs4proc.c | 5 ++
>> fs/nfsd/nfs4state.c | 66 ++++++++++++++++++++++++++
>> fs/nfsd/nfs4xdr.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++-
>> fs/nfsd/xdr4.h | 21 ++++++++
>> 4 files changed, 218 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
>> index 7e00116..a856f30 100644
>> --- a/fs/nfsd/nfs4proc.c
>> +++ b/fs/nfsd/nfs4proc.c
>> @@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
>> .op_flags = OP_HANDLES_WRONGSEC,
>> .op_name = "OP_SECINFO_NO_NAME",
>> },
>> + [OP_TEST_STATEID] = {
>> + .op_func = (nfsd4op_func)nfsd4_test_stateid,
>> + .op_flags = ALLOWED_WITHOUT_FH,
>> + .op_name = "OP_TEST_STATEID",
>> + },
>> [OP_FREE_STATEID] = {
>> .op_func = (nfsd4op_func)nfsd4_free_stateid,
>> .op_flags = ALLOWED_WITHOUT_FH,
>> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
>> index 6beec13..c7bb3ce 100644
>> --- a/fs/nfsd/nfs4state.c
>> +++ b/fs/nfsd/nfs4state.c
>> @@ -37,6 +37,7 @@
>> #include <linux/slab.h>
>> #include <linux/namei.h>
>> #include <linux/swap.h>
>> +#include <linux/pagemap.h>
>> #include <linux/sunrpc/svcauth_gss.h>
>> #include <linux/sunrpc/clnt.h>
>> #include "xdr4.h"
>> @@ -3140,6 +3141,35 @@ static int is_delegation_stateid(stateid_t *stateid)
>> return stateid->si_fileid == 0;
>> }
>>
>> +static __be32 nfs4_validate_stateid(stateid_t *stateid, int flags, struct file **filpp, struct svc_fh *current_fh)
>> +{
>> + struct nfs4_stateid *stp = NULL;
>> + __be32 status = nfserr_stale_stateid;
>> +
>> + if (STALE_STATEID(stateid))
>> + goto out;
>> +
>> + status = nfserr_expired;
>> + stp = search_for_stateid(stateid);
>> + if (!stp)
>> + goto out;
>> + status = nfserr_bad_stateid;
>> +
>> + if (!stp->st_stateowner->so_confirmed)
>> + goto out;
>> +
>> + status = check_stateid_generation(stateid, &stp->st_stateid, flags);
>> + if (status)
>> + goto out;
>> +
>> + status = nfs4_check_openmode(stp, flags);
>
> It doesn't make sense to be doing openmode checks here, since
> test_stateid doesn't check whether a stateid could be used for read or
> write. (And OPENMODE isn't a legal return value for test_stateid.)
Ok, I'll remove this check.
>
>> + if (status)
>> + goto out;
>> + status = nfs_ok;
>> +out:
>> + return status;
>> +}
>> +
>> /*
>> * Checks for stateid operations
>> */
>> @@ -3220,6 +3250,42 @@ out:
>> }
>>
>> /*
>> + * Test if the stateid is valid
>> + */
>> +__be32
>> +nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>> + struct nfsd4_test_stateid *test_stateid)
>> +{
>> + stateid_t *stateid = NULL;
>> + int *resp = NULL;
>> + struct svc_fh *current_fh;
>> + unsigned int cur_id_page = 0;
>> + unsigned int cur_resp_page = 0;
>> + int i;
>> + unsigned int lock_flags = LOCK_STATE | RD_STATE | WR_STATE;
>> + unsigned int open_flags = OPEN_STATE | RD_STATE | WR_STATE;
>> +
>> + current_fh = &cstate->current_fh;
>> +
>> + for (i = 0; i < test_stateid->ts_num_ids; i++) {
>> + if ((i % test_stateid->ts_stateids->items_per_page) == 0) {
>> + stateid = (stateid_t *)test_stateid->ts_stateids->items[cur_id_page].first_item;
>> + cur_id_page++;
>> + }
>> + if ((i % test_stateid->ts_valid->items_per_page) == 0) {
>> + resp = (int *)test_stateid->ts_valid->items[cur_resp_page].first_item;
>> + cur_resp_page++;
>> + }
>> + *resp = nfs4_validate_stateid(stateid, lock_flags, NULL, current_fh);
>> + if (!*resp)
>> + *resp = nfs4_validate_stateid(stateid, open_flags, NULL, current_fh);
>> + stateid++;
>> + resp++;
>> + }
>> + return nfs_ok;
>> +}
>> +
>> +/*
>> * Free a state id
>> */
>> __be32
>> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
>> index 5da6874..c92c4d2 100644
>> --- a/fs/nfsd/nfs4xdr.c
>> +++ b/fs/nfsd/nfs4xdr.c
>> @@ -44,6 +44,7 @@
>> #include <linux/namei.h>
>> #include <linux/statfs.h>
>> #include <linux/utsname.h>
>> +#include <linux/pagemap.h>
>> #include <linux/sunrpc/svcauth_gss.h>
>>
>> #include "idmap.h"
>> @@ -1274,6 +1275,98 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
>> DECODE_TAIL;
>> }
>>
>> +static void
>> +nfsd4_free_ts_pagearray(struct nfsd4_ts_pagearray *array)
>> +{
>> + unsigned int i;
>> + struct page *page;
>> + for (i = 0; i < array->num_pages; i++) {
>> + kunmap(array->items[i].page);
>> + put_page(array->items[i].page);
>> + }
>> + page = array->page_ptr;
>> + put_page(page);
>> +}
>> +
>> +static struct nfsd4_ts_pagearray *
>> +nfsd4_alloc_ts_pagearray(struct nfsd4_test_stateid *test_stateid, unsigned int item_size)
>> +{
>> + unsigned int num_pages;
>> + struct nfsd4_ts_pagearray *array;
>> + struct page *page;
>> + int i = 0;
>> +
>> + page = alloc_page(GFP_KERNEL);
>> + if (page == NULL)
>> + goto out_nomem;
>> +
>> + array = kmap(page);
>> + array->num_pages = 0;
>> + array->items_per_page = PAGE_SIZE / item_size;
>> + array->page_ptr = page;
>> +
>> + num_pages = ((test_stateid->ts_num_ids * item_size) / PAGE_SIZE) + 1;
>> +
>> + for (i = 0; i < num_pages; i++) {
>> + page = alloc_page(GFP_KERNEL);
>> + if (page == NULL)
>> + goto out_freepages;
>> + array->items[i].page = page;
>> + array->items[i].first_item = kmap(page);
>> + array->num_pages += 1;
>> + }
>> + return array;
>> +
>> +out_freepages:
>> + nfsd4_free_ts_pagearray(array);
>> +out_nomem:
>> + return ERR_PTR(-ENOMEM);
>> +}
>> +
>> +static __be32
>> +nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
>> +{
>> + stateid_t * si = NULL;
>> + unsigned int cur_page = 0;
>> + struct page *page;
>> + int i;
>> +
>> + DECODE_HEAD;
>> + READ_BUF(4);
>> + READ32(test_stateid->ts_num_ids);
>
> Surely we need some sort of sanity-check on the size of that thing?
> Otherwise the num_pages calculation above can overflow and weird stuff
> happens.
Good point. I'll add this in.
>
>> +
>> + test_stateid->ts_stateids = nfsd4_alloc_ts_pagearray(test_stateid, sizeof(stateid_t));
>> + if (IS_ERR(test_stateid->ts_stateids))
>> + return PTR_ERR(test_stateid->ts_stateids);
>> +
>> + test_stateid->ts_valid = nfsd4_alloc_ts_pagearray(test_stateid, sizeof(int));
>
> Will this be freed in every case? I guess you're assuming the encode
> function will be called whenever the decode succeeds. Off the top of my
> head, I don't think that's true if xdr decoding of a later op fails.
That was what I was assuming. What is the best way to free this if a later decode fails? something in nfsd4_proc_compound()?
>
> --b.
>
>> + if (IS_ERR(test_stateid->ts_valid)) {
>> + status = PTR_ERR(test_stateid->ts_valid);
>> + goto out_free_pages;
>> + }
>> +
>> + /* Read in a list of stateids provided by the client */
>> + for (i = 0; i < test_stateid->ts_num_ids; i++) {
>> + if ((i % test_stateid->ts_stateids->items_per_page) == 0) {
>> + si = test_stateid->ts_stateids->items[cur_page].first_item;
>> + cur_page++;
>> + }
>> + nfsd4_decode_stateid(argp, si);
>> + si++;
>> + }
>> + kunmap(page);
>> + status = 0;
>> +out:
>> + return status;
>> +xdr_error:
>> + dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
>> + status = nfserr_bad_xdr;
>> + nfsd4_free_ts_pagearray(test_stateid->ts_valid);
>> +out_free_pages:
>> + nfsd4_free_ts_pagearray(test_stateid->ts_stateids);
>> + goto out;
>> +}
>> +
>> static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
>> {
>> DECODE_HEAD;
>> @@ -1393,7 +1486,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
>> [OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_secinfo_no_name,
>> [OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence,
>> [OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp,
>> - [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
>> + [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_test_stateid,
>> [OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
>> [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp,
>> [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete,
>> @@ -3166,6 +3259,37 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
>> return 0;
>> }
>>
>> +__be32
>> +nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
>> + struct nfsd4_test_stateid *test_stateid)
>> +{
>> + __be32 *p;
>> + unsigned int i;
>> + unsigned int cur_page = 0;
>> + int *valid = NULL;
>> +
>> + if (!nfserr) {
>> + RESERVE_SPACE(4);
>> + WRITE32(test_stateid->ts_num_ids);
>> + ADJUST_ARGS();
>> +
>> + for (i = 0; i < test_stateid->ts_num_ids; i++) {
>> + if ((i % test_stateid->ts_valid->items_per_page) == 0) {
>> + valid = (int *)test_stateid->ts_valid->items[cur_page].first_item;
>> + cur_page++;
>> + }
>> + RESERVE_SPACE(4);
>> + WRITE32(*valid);
>> + ADJUST_ARGS();
>> + valid++;
>> + }
>> + }
>> +
>> + nfsd4_free_ts_pagearray(test_stateid->ts_valid);
>> + nfsd4_free_ts_pagearray(test_stateid->ts_stateids);
>> + return nfserr;
>> +}
>> +
>> static __be32
>> nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
>> {
>> @@ -3234,7 +3358,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
>> [OP_SECINFO_NO_NAME] = (nfsd4_enc)nfsd4_encode_secinfo_no_name,
>> [OP_SEQUENCE] = (nfsd4_enc)nfsd4_encode_sequence,
>> [OP_SET_SSV] = (nfsd4_enc)nfsd4_encode_noop,
>> - [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_noop,
>> + [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_test_stateid,
>> [OP_WANT_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
>> [OP_DESTROY_CLIENTID] = (nfsd4_enc)nfsd4_encode_noop,
>> [OP_RECLAIM_COMPLETE] = (nfsd4_enc)nfsd4_encode_noop,
>> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
>> index ed1784d..0455f55 100644
>> --- a/fs/nfsd/xdr4.h
>> +++ b/fs/nfsd/xdr4.h
>> @@ -342,6 +342,24 @@ struct nfsd4_setclientid_confirm {
>> nfs4_verifier sc_confirm;
>> };
>>
>> +struct nfsd4_ts_pagearray_item {
>> + struct page *page;
>> + void *first_item;
>> +};
>> +
>> +struct nfsd4_ts_pagearray {
>> + unsigned int num_pages;
>> + unsigned int items_per_page;
>> + struct page *page_ptr;
>> + struct nfsd4_ts_pagearray_item items[0];
>> +};
>> +
>> +struct nfsd4_test_stateid {
>> + __be32 ts_num_ids; /* request */
>> + struct nfsd4_ts_pagearray *ts_stateids; /* request */
>> + struct nfsd4_ts_pagearray *ts_valid; /* response */
>> +};
>> +
>> struct nfsd4_free_stateid {
>> stateid_t fr_stateid; /* request */
>> __be32 fr_status; /* response */
>> @@ -437,6 +455,7 @@ struct nfsd4_op {
>> struct nfsd4_destroy_session destroy_session;
>> struct nfsd4_sequence sequence;
>> struct nfsd4_reclaim_complete reclaim_complete;
>> + struct nfsd4_test_stateid test_stateid;
>> struct nfsd4_free_stateid free_stateid;
>> } u;
>> struct nfs4_replay * replay;
>> @@ -570,6 +589,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
>> struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
>> extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
>> struct nfsd4_compound_state *, clientid_t *clid);
>> +extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
>> + struct nfsd4_compound_state *, struct nfsd4_test_stateid *test_stateid);
>> extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
>> struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
>> #endif
>> --
>> 1.7.5.1
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, May 11, 2011 at 10:41:01AM -0400, Bryan Schumaker wrote:
> On 05/09/2011 08:36 PM, J. Bruce Fields wrote:
> > On Fri, May 06, 2011 at 02:57:34PM -0400, [email protected] wrote:
> >> + release_open_stateid(stp);
> >
> > OK, but it might not be a lock stateid. (I assume free_stateid can be
> > called on *any* kind of stateid?)
>
> I thought release_open_stateid() freed all stateids, not just the lock stateid...
Lock stateid's are released by release_lock_stateid(), delegation
stateid's (actually embedded in a struct nfs4_delegation) by
unhash_delegation().
--b.
>
> >
> > --b.
> >
> >> + return nfs_ok;
> >> +}
> >> +
> >> static inline int
> >> setlkflg (int type)
> >> {
> >> @@ -3619,6 +3653,28 @@ find_stateid(stateid_t *stid, int flags)
> >> return NULL;
> >> }
> >>
> >> +static struct nfs4_stateid *
> >> +search_for_stateid(stateid_t *stid)
> >> +{
> >> + struct nfs4_stateid *local;
> >> + u32 st_id = stid->si_stateownerid;
> >> + u32 f_id = stid->si_fileid;
> >> + unsigned int 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))
> >> + return local;
> >> + }
> >> +
> >> + 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))
> >> + return local;
> >> + }
> >> + return NULL;
> >> +}
> >> +
> >> static struct nfs4_delegation *
> >> find_delegation_stateid(struct inode *ino, stateid_t *stid)
> >> {
> >> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> >> index 195a91d..5da6874 100644
> >> --- a/fs/nfsd/nfs4xdr.c
> >> +++ b/fs/nfsd/nfs4xdr.c
> >> @@ -1246,6 +1246,19 @@ nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
> >> }
> >>
> >> static __be32
> >> +nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
> >> + struct nfsd4_free_stateid *free_stateid)
> >> +{
> >> + DECODE_HEAD;
> >> +
> >> + READ_BUF(sizeof(stateid_t));
> >> + READ32(free_stateid->fr_stateid.si_generation);
> >> + COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
> >> +
> >> + DECODE_TAIL;
> >> +}
> >> +
> >> +static __be32
> >> nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
> >> struct nfsd4_sequence *seq)
> >> {
> >> @@ -1370,7 +1383,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
> >> [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id,
> >> [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session,
> >> [OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session,
> >> - [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
> >> + [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_free_stateid,
> >> [OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
> >> [OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_notsupp,
> >> [OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp,
> >> @@ -3115,6 +3128,21 @@ nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
> >> return nfserr;
> >> }
> >>
> >> +static __be32
> >> +nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
> >> + struct nfsd4_free_stateid *free_stateid)
> >> +{
> >> + __be32 *p;
> >> +
> >> + if (nfserr)
> >> + return nfserr;
> >> +
> >> + RESERVE_SPACE(4);
> >> + WRITE32(nfserr);
> >> + ADJUST_ARGS();
> >> + return nfserr;
> >> +}
> >> +
> >> __be32
> >> nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
> >> struct nfsd4_sequence *seq)
> >> @@ -3196,7 +3224,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
> >> [OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id,
> >> [OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session,
> >> [OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_destroy_session,
> >> - [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_noop,
> >> + [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_free_stateid,
> >> [OP_GET_DIR_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
> >> [OP_GETDEVICEINFO] = (nfsd4_enc)nfsd4_encode_noop,
> >> [OP_GETDEVICELIST] = (nfsd4_enc)nfsd4_encode_noop,
> >> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> >> index 366401e..ed1784d 100644
> >> --- a/fs/nfsd/xdr4.h
> >> +++ b/fs/nfsd/xdr4.h
> >> @@ -342,6 +342,11 @@ struct nfsd4_setclientid_confirm {
> >> nfs4_verifier sc_confirm;
> >> };
> >>
> >> +struct nfsd4_free_stateid {
> >> + stateid_t fr_stateid; /* request */
> >> + __be32 fr_status; /* response */
> >> +};
> >> +
> >> /* also used for NVERIFY */
> >> struct nfsd4_verify {
> >> u32 ve_bmval[3]; /* request */
> >> @@ -432,6 +437,7 @@ struct nfsd4_op {
> >> struct nfsd4_destroy_session destroy_session;
> >> struct nfsd4_sequence sequence;
> >> struct nfsd4_reclaim_complete reclaim_complete;
> >> + struct nfsd4_free_stateid free_stateid;
> >> } u;
> >> struct nfs4_replay * replay;
> >> };
> >> @@ -564,6 +570,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
> >> struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
> >> extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
> >> struct nfsd4_compound_state *, clientid_t *clid);
> >> +extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
> >> + struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
> >> #endif
> >>
> >> /*
> >> --
> >> 1.7.5.1
> >>
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
On Wed, May 11, 2011 at 12:20:11PM -0400, Bryan Schumaker wrote:
> On 05/09/2011 08:49 PM, J. Bruce Fields wrote:
> > On Fri, May 06, 2011 at 02:57:35PM -0400, [email protected] wrote:
> >> +
> >> + test_stateid->ts_stateids = nfsd4_alloc_ts_pagearray(test_stateid, sizeof(stateid_t));
> >> + if (IS_ERR(test_stateid->ts_stateids))
> >> + return PTR_ERR(test_stateid->ts_stateids);
> >> +
> >> + test_stateid->ts_valid = nfsd4_alloc_ts_pagearray(test_stateid, sizeof(int));
> >
> > Will this be freed in every case? I guess you're assuming the encode
> > function will be called whenever the decode succeeds. Off the top of my
> > head, I don't think that's true if xdr decoding of a later op fails.
>
> That was what I was assuming. What is the best way to free this if a later decode fails? something in nfsd4_proc_compound()?
I had to look at it again and remind myself.... See defer_free() in
fs/nfsd/nfs4xdr.c.
--b.
On 05/09/2011 08:36 PM, J. Bruce Fields wrote:
> On Fri, May 06, 2011 at 02:57:34PM -0400, [email protected] wrote:
>> From: Bryan Schumaker <[email protected]>
>>
>> This operation is used by the client to tell the server to free a
>> stateid.
>>
>> Signed-off-by: Bryan Schumaker <[email protected]>
>> ---
>> fs/nfsd/nfs4proc.c | 5 ++++
>> fs/nfsd/nfs4state.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++
>> fs/nfsd/nfs4xdr.c | 32 +++++++++++++++++++++++++++-
>> fs/nfsd/xdr4.h | 8 +++++++
>> 4 files changed, 99 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
>> index 3a6dbd7..7e00116 100644
>> --- a/fs/nfsd/nfs4proc.c
>> +++ b/fs/nfsd/nfs4proc.c
>> @@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
>> .op_flags = OP_HANDLES_WRONGSEC,
>> .op_name = "OP_SECINFO_NO_NAME",
>> },
>> + [OP_FREE_STATEID] = {
>> + .op_func = (nfsd4op_func)nfsd4_free_stateid,
>> + .op_flags = ALLOWED_WITHOUT_FH,
>> + .op_name = "OP_FREE_STATEID",
>> + },
>> };
>>
>> static const char *nfsd4_op_name(unsigned opnum)
>> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
>> index a2ea14f..6beec13 100644
>> --- a/fs/nfsd/nfs4state.c
>> +++ b/fs/nfsd/nfs4state.c
>> @@ -60,6 +60,7 @@ static u64 current_sessionid = 1;
>>
>> /* forward declarations */
>> static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
>> +static struct nfs4_stateid * search_for_stateid(stateid_t *stid);
>> static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid);
>> static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
>> static void nfs4_set_recdir(char *recdir);
>> @@ -316,6 +317,12 @@ static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE];
>> static struct list_head client_lru;
>> static struct list_head close_lru;
>>
>> +static int
>> +stateid_has_locks(struct nfs4_stateid *stp)
>> +{
>> + return list_empty(&stp->st_lockowners);
>> +}
>
> There may be lockowners that aren't associated with any current locks.
>
> The only way we have to check this currently is to walk through the
> i_flock list for the given inode, like check_for_locks() does. That may
> be the expedient thing to do for now.
Ok, I'll change it to use check_for_locks() for now.
>
> (It would be simpler if we could, say, keep a count of the locks
> associated with each lock stateid. That's not as easy as it sounds,
> thanks to merging and splitting of byte-range locks. But we could use
> callbacks from locks.c to do it. I think that's what I'd really like to
> do.)
>
>> +
>> /*
>> * We store the NONE, READ, WRITE, and BOTH bits separately in the
>> * st_{access,deny}_bmap field of the stateid, in order to track not
>> @@ -3212,6 +3219,33 @@ out:
>> return status;
>> }
>>
>> +/*
>> + * Free a state id
>> + */
>> +__be32
>> +nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>> + struct nfsd4_free_stateid *free_stateid)
>> +{
>> + struct nfs4_stateid *stp = NULL;
>> + stateid_t *stateid = &free_stateid->fr_stateid;
>> +
>> + stp = search_for_stateid(stateid);
>> + if (!stp)
>> + return nfserr_bad_stateid;
>> + if (stateid->si_generation != 0) {
>> + if (stateid->si_generation < stp->st_stateid.si_generation)
>> + return nfserr_old_stateid;
>> + if (stateid->si_generation > stp->st_stateid.si_generation)
>> + return nfserr_bad_stateid;
>> + }
>> +
>> + if (stateid_has_locks(stp))
>> + return nfserr_locks_held;
>> + if (stp)
>
> Already checked this above.
Good catch, I'll remove the extra check.
>
>> + release_open_stateid(stp);
>
> OK, but it might not be a lock stateid. (I assume free_stateid can be
> called on *any* kind of stateid?)
I thought release_open_stateid() freed all stateids, not just the lock stateid...
>
> --b.
>
>> + return nfs_ok;
>> +}
>> +
>> static inline int
>> setlkflg (int type)
>> {
>> @@ -3619,6 +3653,28 @@ find_stateid(stateid_t *stid, int flags)
>> return NULL;
>> }
>>
>> +static struct nfs4_stateid *
>> +search_for_stateid(stateid_t *stid)
>> +{
>> + struct nfs4_stateid *local;
>> + u32 st_id = stid->si_stateownerid;
>> + u32 f_id = stid->si_fileid;
>> + unsigned int 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))
>> + return local;
>> + }
>> +
>> + 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))
>> + return local;
>> + }
>> + return NULL;
>> +}
>> +
>> static struct nfs4_delegation *
>> find_delegation_stateid(struct inode *ino, stateid_t *stid)
>> {
>> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
>> index 195a91d..5da6874 100644
>> --- a/fs/nfsd/nfs4xdr.c
>> +++ b/fs/nfsd/nfs4xdr.c
>> @@ -1246,6 +1246,19 @@ nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
>> }
>>
>> static __be32
>> +nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
>> + struct nfsd4_free_stateid *free_stateid)
>> +{
>> + DECODE_HEAD;
>> +
>> + READ_BUF(sizeof(stateid_t));
>> + READ32(free_stateid->fr_stateid.si_generation);
>> + COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
>> +
>> + DECODE_TAIL;
>> +}
>> +
>> +static __be32
>> nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
>> struct nfsd4_sequence *seq)
>> {
>> @@ -1370,7 +1383,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
>> [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id,
>> [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session,
>> [OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session,
>> - [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
>> + [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_free_stateid,
>> [OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
>> [OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_notsupp,
>> [OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp,
>> @@ -3115,6 +3128,21 @@ nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
>> return nfserr;
>> }
>>
>> +static __be32
>> +nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
>> + struct nfsd4_free_stateid *free_stateid)
>> +{
>> + __be32 *p;
>> +
>> + if (nfserr)
>> + return nfserr;
>> +
>> + RESERVE_SPACE(4);
>> + WRITE32(nfserr);
>> + ADJUST_ARGS();
>> + return nfserr;
>> +}
>> +
>> __be32
>> nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
>> struct nfsd4_sequence *seq)
>> @@ -3196,7 +3224,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
>> [OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id,
>> [OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session,
>> [OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_destroy_session,
>> - [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_noop,
>> + [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_free_stateid,
>> [OP_GET_DIR_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
>> [OP_GETDEVICEINFO] = (nfsd4_enc)nfsd4_encode_noop,
>> [OP_GETDEVICELIST] = (nfsd4_enc)nfsd4_encode_noop,
>> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
>> index 366401e..ed1784d 100644
>> --- a/fs/nfsd/xdr4.h
>> +++ b/fs/nfsd/xdr4.h
>> @@ -342,6 +342,11 @@ struct nfsd4_setclientid_confirm {
>> nfs4_verifier sc_confirm;
>> };
>>
>> +struct nfsd4_free_stateid {
>> + stateid_t fr_stateid; /* request */
>> + __be32 fr_status; /* response */
>> +};
>> +
>> /* also used for NVERIFY */
>> struct nfsd4_verify {
>> u32 ve_bmval[3]; /* request */
>> @@ -432,6 +437,7 @@ struct nfsd4_op {
>> struct nfsd4_destroy_session destroy_session;
>> struct nfsd4_sequence sequence;
>> struct nfsd4_reclaim_complete reclaim_complete;
>> + struct nfsd4_free_stateid free_stateid;
>> } u;
>> struct nfs4_replay * replay;
>> };
>> @@ -564,6 +570,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
>> struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
>> extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
>> struct nfsd4_compound_state *, clientid_t *clid);
>> +extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
>> + struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
>> #endif
>>
>> /*
>> --
>> 1.7.5.1
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, May 06, 2011 at 02:57:34PM -0400, [email protected] wrote:
> From: Bryan Schumaker <[email protected]>
>
> This operation is used by the client to tell the server to free a
> stateid.
>
> Signed-off-by: Bryan Schumaker <[email protected]>
> ---
> fs/nfsd/nfs4proc.c | 5 ++++
> fs/nfsd/nfs4state.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++
> fs/nfsd/nfs4xdr.c | 32 +++++++++++++++++++++++++++-
> fs/nfsd/xdr4.h | 8 +++++++
> 4 files changed, 99 insertions(+), 2 deletions(-)
>
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index 3a6dbd7..7e00116 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
> .op_flags = OP_HANDLES_WRONGSEC,
> .op_name = "OP_SECINFO_NO_NAME",
> },
> + [OP_FREE_STATEID] = {
> + .op_func = (nfsd4op_func)nfsd4_free_stateid,
> + .op_flags = ALLOWED_WITHOUT_FH,
> + .op_name = "OP_FREE_STATEID",
> + },
> };
>
> static const char *nfsd4_op_name(unsigned opnum)
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index a2ea14f..6beec13 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -60,6 +60,7 @@ static u64 current_sessionid = 1;
>
> /* forward declarations */
> static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
> +static struct nfs4_stateid * search_for_stateid(stateid_t *stid);
> static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid);
> static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
> static void nfs4_set_recdir(char *recdir);
> @@ -316,6 +317,12 @@ static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE];
> static struct list_head client_lru;
> static struct list_head close_lru;
>
> +static int
> +stateid_has_locks(struct nfs4_stateid *stp)
> +{
> + return list_empty(&stp->st_lockowners);
> +}
There may be lockowners that aren't associated with any current locks.
The only way we have to check this currently is to walk through the
i_flock list for the given inode, like check_for_locks() does. That may
be the expedient thing to do for now.
(It would be simpler if we could, say, keep a count of the locks
associated with each lock stateid. That's not as easy as it sounds,
thanks to merging and splitting of byte-range locks. But we could use
callbacks from locks.c to do it. I think that's what I'd really like to
do.)
> +
> /*
> * We store the NONE, READ, WRITE, and BOTH bits separately in the
> * st_{access,deny}_bmap field of the stateid, in order to track not
> @@ -3212,6 +3219,33 @@ out:
> return status;
> }
>
> +/*
> + * Free a state id
> + */
> +__be32
> +nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> + struct nfsd4_free_stateid *free_stateid)
> +{
> + struct nfs4_stateid *stp = NULL;
> + stateid_t *stateid = &free_stateid->fr_stateid;
> +
> + stp = search_for_stateid(stateid);
> + if (!stp)
> + return nfserr_bad_stateid;
> + if (stateid->si_generation != 0) {
> + if (stateid->si_generation < stp->st_stateid.si_generation)
> + return nfserr_old_stateid;
> + if (stateid->si_generation > stp->st_stateid.si_generation)
> + return nfserr_bad_stateid;
> + }
> +
> + if (stateid_has_locks(stp))
> + return nfserr_locks_held;
> + if (stp)
Already checked this above.
> + release_open_stateid(stp);
OK, but it might not be a lock stateid. (I assume free_stateid can be
called on *any* kind of stateid?)
--b.
> + return nfs_ok;
> +}
> +
> static inline int
> setlkflg (int type)
> {
> @@ -3619,6 +3653,28 @@ find_stateid(stateid_t *stid, int flags)
> return NULL;
> }
>
> +static struct nfs4_stateid *
> +search_for_stateid(stateid_t *stid)
> +{
> + struct nfs4_stateid *local;
> + u32 st_id = stid->si_stateownerid;
> + u32 f_id = stid->si_fileid;
> + unsigned int 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))
> + return local;
> + }
> +
> + 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))
> + return local;
> + }
> + return NULL;
> +}
> +
> static struct nfs4_delegation *
> find_delegation_stateid(struct inode *ino, stateid_t *stid)
> {
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 195a91d..5da6874 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -1246,6 +1246,19 @@ nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
> }
>
> static __be32
> +nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
> + struct nfsd4_free_stateid *free_stateid)
> +{
> + DECODE_HEAD;
> +
> + READ_BUF(sizeof(stateid_t));
> + READ32(free_stateid->fr_stateid.si_generation);
> + COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
> +
> + DECODE_TAIL;
> +}
> +
> +static __be32
> nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
> struct nfsd4_sequence *seq)
> {
> @@ -1370,7 +1383,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
> [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id,
> [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session,
> [OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session,
> - [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
> + [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_free_stateid,
> [OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
> [OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_notsupp,
> [OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp,
> @@ -3115,6 +3128,21 @@ nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
> return nfserr;
> }
>
> +static __be32
> +nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
> + struct nfsd4_free_stateid *free_stateid)
> +{
> + __be32 *p;
> +
> + if (nfserr)
> + return nfserr;
> +
> + RESERVE_SPACE(4);
> + WRITE32(nfserr);
> + ADJUST_ARGS();
> + return nfserr;
> +}
> +
> __be32
> nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
> struct nfsd4_sequence *seq)
> @@ -3196,7 +3224,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
> [OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id,
> [OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session,
> [OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_destroy_session,
> - [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_noop,
> + [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_free_stateid,
> [OP_GET_DIR_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
> [OP_GETDEVICEINFO] = (nfsd4_enc)nfsd4_encode_noop,
> [OP_GETDEVICELIST] = (nfsd4_enc)nfsd4_encode_noop,
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index 366401e..ed1784d 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -342,6 +342,11 @@ struct nfsd4_setclientid_confirm {
> nfs4_verifier sc_confirm;
> };
>
> +struct nfsd4_free_stateid {
> + stateid_t fr_stateid; /* request */
> + __be32 fr_status; /* response */
> +};
> +
> /* also used for NVERIFY */
> struct nfsd4_verify {
> u32 ve_bmval[3]; /* request */
> @@ -432,6 +437,7 @@ struct nfsd4_op {
> struct nfsd4_destroy_session destroy_session;
> struct nfsd4_sequence sequence;
> struct nfsd4_reclaim_complete reclaim_complete;
> + struct nfsd4_free_stateid free_stateid;
> } u;
> struct nfs4_replay * replay;
> };
> @@ -564,6 +570,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
> struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
> extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
> struct nfsd4_compound_state *, clientid_t *clid);
> +extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
> + struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
> #endif
>
> /*
> --
> 1.7.5.1
>
From: Bryan Schumaker <[email protected]>
This operation is used by the client to check the validity of a list of
stateids.
Signed-off-by: Bryan Schumaker <[email protected]>
---
fs/nfsd/nfs4proc.c | 5 ++
fs/nfsd/nfs4state.c | 66 ++++++++++++++++++++++++++
fs/nfsd/nfs4xdr.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/nfsd/xdr4.h | 21 ++++++++
4 files changed, 218 insertions(+), 2 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 7e00116..a856f30 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
.op_flags = OP_HANDLES_WRONGSEC,
.op_name = "OP_SECINFO_NO_NAME",
},
+ [OP_TEST_STATEID] = {
+ .op_func = (nfsd4op_func)nfsd4_test_stateid,
+ .op_flags = ALLOWED_WITHOUT_FH,
+ .op_name = "OP_TEST_STATEID",
+ },
[OP_FREE_STATEID] = {
.op_func = (nfsd4op_func)nfsd4_free_stateid,
.op_flags = ALLOWED_WITHOUT_FH,
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6beec13..c7bb3ce 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -37,6 +37,7 @@
#include <linux/slab.h>
#include <linux/namei.h>
#include <linux/swap.h>
+#include <linux/pagemap.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/clnt.h>
#include "xdr4.h"
@@ -3140,6 +3141,35 @@ static int is_delegation_stateid(stateid_t *stateid)
return stateid->si_fileid == 0;
}
+static __be32 nfs4_validate_stateid(stateid_t *stateid, int flags, struct file **filpp, struct svc_fh *current_fh)
+{
+ struct nfs4_stateid *stp = NULL;
+ __be32 status = nfserr_stale_stateid;
+
+ if (STALE_STATEID(stateid))
+ goto out;
+
+ status = nfserr_expired;
+ stp = search_for_stateid(stateid);
+ if (!stp)
+ goto out;
+ status = nfserr_bad_stateid;
+
+ if (!stp->st_stateowner->so_confirmed)
+ goto out;
+
+ status = check_stateid_generation(stateid, &stp->st_stateid, flags);
+ if (status)
+ goto out;
+
+ status = nfs4_check_openmode(stp, flags);
+ if (status)
+ goto out;
+ status = nfs_ok;
+out:
+ return status;
+}
+
/*
* Checks for stateid operations
*/
@@ -3220,6 +3250,42 @@ out:
}
/*
+ * Test if the stateid is valid
+ */
+__be32
+nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ struct nfsd4_test_stateid *test_stateid)
+{
+ stateid_t *stateid = NULL;
+ int *resp = NULL;
+ struct svc_fh *current_fh;
+ unsigned int cur_id_page = 0;
+ unsigned int cur_resp_page = 0;
+ int i;
+ unsigned int lock_flags = LOCK_STATE | RD_STATE | WR_STATE;
+ unsigned int open_flags = OPEN_STATE | RD_STATE | WR_STATE;
+
+ current_fh = &cstate->current_fh;
+
+ for (i = 0; i < test_stateid->ts_num_ids; i++) {
+ if ((i % test_stateid->ts_stateids->items_per_page) == 0) {
+ stateid = (stateid_t *)test_stateid->ts_stateids->items[cur_id_page].first_item;
+ cur_id_page++;
+ }
+ if ((i % test_stateid->ts_valid->items_per_page) == 0) {
+ resp = (int *)test_stateid->ts_valid->items[cur_resp_page].first_item;
+ cur_resp_page++;
+ }
+ *resp = nfs4_validate_stateid(stateid, lock_flags, NULL, current_fh);
+ if (!*resp)
+ *resp = nfs4_validate_stateid(stateid, open_flags, NULL, current_fh);
+ stateid++;
+ resp++;
+ }
+ return nfs_ok;
+}
+
+/*
* Free a state id
*/
__be32
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5da6874..c92c4d2 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -44,6 +44,7 @@
#include <linux/namei.h>
#include <linux/statfs.h>
#include <linux/utsname.h>
+#include <linux/pagemap.h>
#include <linux/sunrpc/svcauth_gss.h>
#include "idmap.h"
@@ -1274,6 +1275,98 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
DECODE_TAIL;
}
+static void
+nfsd4_free_ts_pagearray(struct nfsd4_ts_pagearray *array)
+{
+ unsigned int i;
+ struct page *page;
+ for (i = 0; i < array->num_pages; i++) {
+ kunmap(array->items[i].page);
+ put_page(array->items[i].page);
+ }
+ page = array->page_ptr;
+ put_page(page);
+}
+
+static struct nfsd4_ts_pagearray *
+nfsd4_alloc_ts_pagearray(struct nfsd4_test_stateid *test_stateid, unsigned int item_size)
+{
+ unsigned int num_pages;
+ struct nfsd4_ts_pagearray *array;
+ struct page *page;
+ int i = 0;
+
+ page = alloc_page(GFP_KERNEL);
+ if (page == NULL)
+ goto out_nomem;
+
+ array = kmap(page);
+ array->num_pages = 0;
+ array->items_per_page = PAGE_SIZE / item_size;
+ array->page_ptr = page;
+
+ num_pages = ((test_stateid->ts_num_ids * item_size) / PAGE_SIZE) + 1;
+
+ for (i = 0; i < num_pages; i++) {
+ page = alloc_page(GFP_KERNEL);
+ if (page == NULL)
+ goto out_freepages;
+ array->items[i].page = page;
+ array->items[i].first_item = kmap(page);
+ array->num_pages += 1;
+ }
+ return array;
+
+out_freepages:
+ nfsd4_free_ts_pagearray(array);
+out_nomem:
+ return ERR_PTR(-ENOMEM);
+}
+
+static __be32
+nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
+{
+ stateid_t * si = NULL;
+ unsigned int cur_page = 0;
+ struct page *page;
+ int i;
+
+ DECODE_HEAD;
+ READ_BUF(4);
+ READ32(test_stateid->ts_num_ids);
+
+ test_stateid->ts_stateids = nfsd4_alloc_ts_pagearray(test_stateid, sizeof(stateid_t));
+ if (IS_ERR(test_stateid->ts_stateids))
+ return PTR_ERR(test_stateid->ts_stateids);
+
+ test_stateid->ts_valid = nfsd4_alloc_ts_pagearray(test_stateid, sizeof(int));
+ if (IS_ERR(test_stateid->ts_valid)) {
+ status = PTR_ERR(test_stateid->ts_valid);
+ goto out_free_pages;
+ }
+
+ /* Read in a list of stateids provided by the client */
+ for (i = 0; i < test_stateid->ts_num_ids; i++) {
+ if ((i % test_stateid->ts_stateids->items_per_page) == 0) {
+ si = test_stateid->ts_stateids->items[cur_page].first_item;
+ cur_page++;
+ }
+ nfsd4_decode_stateid(argp, si);
+ si++;
+ }
+ kunmap(page);
+ status = 0;
+out:
+ return status;
+xdr_error:
+ dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
+ status = nfserr_bad_xdr;
+ nfsd4_free_ts_pagearray(test_stateid->ts_valid);
+out_free_pages:
+ nfsd4_free_ts_pagearray(test_stateid->ts_stateids);
+ goto out;
+}
+
static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
{
DECODE_HEAD;
@@ -1393,7 +1486,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
[OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_secinfo_no_name,
[OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence,
[OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp,
- [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
+ [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_test_stateid,
[OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete,
@@ -3166,6 +3259,37 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
return 0;
}
+__be32
+nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
+ struct nfsd4_test_stateid *test_stateid)
+{
+ __be32 *p;
+ unsigned int i;
+ unsigned int cur_page = 0;
+ int *valid = NULL;
+
+ if (!nfserr) {
+ RESERVE_SPACE(4);
+ WRITE32(test_stateid->ts_num_ids);
+ ADJUST_ARGS();
+
+ for (i = 0; i < test_stateid->ts_num_ids; i++) {
+ if ((i % test_stateid->ts_valid->items_per_page) == 0) {
+ valid = (int *)test_stateid->ts_valid->items[cur_page].first_item;
+ cur_page++;
+ }
+ RESERVE_SPACE(4);
+ WRITE32(*valid);
+ ADJUST_ARGS();
+ valid++;
+ }
+ }
+
+ nfsd4_free_ts_pagearray(test_stateid->ts_valid);
+ nfsd4_free_ts_pagearray(test_stateid->ts_stateids);
+ return nfserr;
+}
+
static __be32
nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
{
@@ -3234,7 +3358,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
[OP_SECINFO_NO_NAME] = (nfsd4_enc)nfsd4_encode_secinfo_no_name,
[OP_SEQUENCE] = (nfsd4_enc)nfsd4_encode_sequence,
[OP_SET_SSV] = (nfsd4_enc)nfsd4_encode_noop,
- [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_noop,
+ [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_test_stateid,
[OP_WANT_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
[OP_DESTROY_CLIENTID] = (nfsd4_enc)nfsd4_encode_noop,
[OP_RECLAIM_COMPLETE] = (nfsd4_enc)nfsd4_encode_noop,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index ed1784d..0455f55 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -342,6 +342,24 @@ struct nfsd4_setclientid_confirm {
nfs4_verifier sc_confirm;
};
+struct nfsd4_ts_pagearray_item {
+ struct page *page;
+ void *first_item;
+};
+
+struct nfsd4_ts_pagearray {
+ unsigned int num_pages;
+ unsigned int items_per_page;
+ struct page *page_ptr;
+ struct nfsd4_ts_pagearray_item items[0];
+};
+
+struct nfsd4_test_stateid {
+ __be32 ts_num_ids; /* request */
+ struct nfsd4_ts_pagearray *ts_stateids; /* request */
+ struct nfsd4_ts_pagearray *ts_valid; /* response */
+};
+
struct nfsd4_free_stateid {
stateid_t fr_stateid; /* request */
__be32 fr_status; /* response */
@@ -437,6 +455,7 @@ struct nfsd4_op {
struct nfsd4_destroy_session destroy_session;
struct nfsd4_sequence sequence;
struct nfsd4_reclaim_complete reclaim_complete;
+ struct nfsd4_test_stateid test_stateid;
struct nfsd4_free_stateid free_stateid;
} u;
struct nfs4_replay * replay;
@@ -570,6 +589,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, clientid_t *clid);
+extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
+ struct nfsd4_compound_state *, struct nfsd4_test_stateid *test_stateid);
extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
#endif
--
1.7.5.1
On Fri, May 06, 2011 at 02:57:35PM -0400, [email protected] wrote:
> From: Bryan Schumaker <[email protected]>
>
> This operation is used by the client to check the validity of a list of
> stateids.
>
> Signed-off-by: Bryan Schumaker <[email protected]>
> ---
> fs/nfsd/nfs4proc.c | 5 ++
> fs/nfsd/nfs4state.c | 66 ++++++++++++++++++++++++++
> fs/nfsd/nfs4xdr.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++-
> fs/nfsd/xdr4.h | 21 ++++++++
> 4 files changed, 218 insertions(+), 2 deletions(-)
>
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index 7e00116..a856f30 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
> .op_flags = OP_HANDLES_WRONGSEC,
> .op_name = "OP_SECINFO_NO_NAME",
> },
> + [OP_TEST_STATEID] = {
> + .op_func = (nfsd4op_func)nfsd4_test_stateid,
> + .op_flags = ALLOWED_WITHOUT_FH,
> + .op_name = "OP_TEST_STATEID",
> + },
> [OP_FREE_STATEID] = {
> .op_func = (nfsd4op_func)nfsd4_free_stateid,
> .op_flags = ALLOWED_WITHOUT_FH,
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 6beec13..c7bb3ce 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -37,6 +37,7 @@
> #include <linux/slab.h>
> #include <linux/namei.h>
> #include <linux/swap.h>
> +#include <linux/pagemap.h>
> #include <linux/sunrpc/svcauth_gss.h>
> #include <linux/sunrpc/clnt.h>
> #include "xdr4.h"
> @@ -3140,6 +3141,35 @@ static int is_delegation_stateid(stateid_t *stateid)
> return stateid->si_fileid == 0;
> }
>
> +static __be32 nfs4_validate_stateid(stateid_t *stateid, int flags, struct file **filpp, struct svc_fh *current_fh)
> +{
> + struct nfs4_stateid *stp = NULL;
> + __be32 status = nfserr_stale_stateid;
> +
> + if (STALE_STATEID(stateid))
> + goto out;
> +
> + status = nfserr_expired;
> + stp = search_for_stateid(stateid);
> + if (!stp)
> + goto out;
> + status = nfserr_bad_stateid;
> +
> + if (!stp->st_stateowner->so_confirmed)
> + goto out;
> +
> + status = check_stateid_generation(stateid, &stp->st_stateid, flags);
> + if (status)
> + goto out;
> +
> + status = nfs4_check_openmode(stp, flags);
It doesn't make sense to be doing openmode checks here, since
test_stateid doesn't check whether a stateid could be used for read or
write. (And OPENMODE isn't a legal return value for test_stateid.)
> + if (status)
> + goto out;
> + status = nfs_ok;
> +out:
> + return status;
> +}
> +
> /*
> * Checks for stateid operations
> */
> @@ -3220,6 +3250,42 @@ out:
> }
>
> /*
> + * Test if the stateid is valid
> + */
> +__be32
> +nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> + struct nfsd4_test_stateid *test_stateid)
> +{
> + stateid_t *stateid = NULL;
> + int *resp = NULL;
> + struct svc_fh *current_fh;
> + unsigned int cur_id_page = 0;
> + unsigned int cur_resp_page = 0;
> + int i;
> + unsigned int lock_flags = LOCK_STATE | RD_STATE | WR_STATE;
> + unsigned int open_flags = OPEN_STATE | RD_STATE | WR_STATE;
> +
> + current_fh = &cstate->current_fh;
> +
> + for (i = 0; i < test_stateid->ts_num_ids; i++) {
> + if ((i % test_stateid->ts_stateids->items_per_page) == 0) {
> + stateid = (stateid_t *)test_stateid->ts_stateids->items[cur_id_page].first_item;
> + cur_id_page++;
> + }
> + if ((i % test_stateid->ts_valid->items_per_page) == 0) {
> + resp = (int *)test_stateid->ts_valid->items[cur_resp_page].first_item;
> + cur_resp_page++;
> + }
> + *resp = nfs4_validate_stateid(stateid, lock_flags, NULL, current_fh);
> + if (!*resp)
> + *resp = nfs4_validate_stateid(stateid, open_flags, NULL, current_fh);
> + stateid++;
> + resp++;
> + }
> + return nfs_ok;
> +}
> +
> +/*
> * Free a state id
> */
> __be32
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 5da6874..c92c4d2 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -44,6 +44,7 @@
> #include <linux/namei.h>
> #include <linux/statfs.h>
> #include <linux/utsname.h>
> +#include <linux/pagemap.h>
> #include <linux/sunrpc/svcauth_gss.h>
>
> #include "idmap.h"
> @@ -1274,6 +1275,98 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
> DECODE_TAIL;
> }
>
> +static void
> +nfsd4_free_ts_pagearray(struct nfsd4_ts_pagearray *array)
> +{
> + unsigned int i;
> + struct page *page;
> + for (i = 0; i < array->num_pages; i++) {
> + kunmap(array->items[i].page);
> + put_page(array->items[i].page);
> + }
> + page = array->page_ptr;
> + put_page(page);
> +}
> +
> +static struct nfsd4_ts_pagearray *
> +nfsd4_alloc_ts_pagearray(struct nfsd4_test_stateid *test_stateid, unsigned int item_size)
> +{
> + unsigned int num_pages;
> + struct nfsd4_ts_pagearray *array;
> + struct page *page;
> + int i = 0;
> +
> + page = alloc_page(GFP_KERNEL);
> + if (page == NULL)
> + goto out_nomem;
> +
> + array = kmap(page);
> + array->num_pages = 0;
> + array->items_per_page = PAGE_SIZE / item_size;
> + array->page_ptr = page;
> +
> + num_pages = ((test_stateid->ts_num_ids * item_size) / PAGE_SIZE) + 1;
> +
> + for (i = 0; i < num_pages; i++) {
> + page = alloc_page(GFP_KERNEL);
> + if (page == NULL)
> + goto out_freepages;
> + array->items[i].page = page;
> + array->items[i].first_item = kmap(page);
> + array->num_pages += 1;
> + }
> + return array;
> +
> +out_freepages:
> + nfsd4_free_ts_pagearray(array);
> +out_nomem:
> + return ERR_PTR(-ENOMEM);
> +}
> +
> +static __be32
> +nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
> +{
> + stateid_t * si = NULL;
> + unsigned int cur_page = 0;
> + struct page *page;
> + int i;
> +
> + DECODE_HEAD;
> + READ_BUF(4);
> + READ32(test_stateid->ts_num_ids);
Surely we need some sort of sanity-check on the size of that thing?
Otherwise the num_pages calculation above can overflow and weird stuff
happens.
> +
> + test_stateid->ts_stateids = nfsd4_alloc_ts_pagearray(test_stateid, sizeof(stateid_t));
> + if (IS_ERR(test_stateid->ts_stateids))
> + return PTR_ERR(test_stateid->ts_stateids);
> +
> + test_stateid->ts_valid = nfsd4_alloc_ts_pagearray(test_stateid, sizeof(int));
Will this be freed in every case? I guess you're assuming the encode
function will be called whenever the decode succeeds. Off the top of my
head, I don't think that's true if xdr decoding of a later op fails.
--b.
> + if (IS_ERR(test_stateid->ts_valid)) {
> + status = PTR_ERR(test_stateid->ts_valid);
> + goto out_free_pages;
> + }
> +
> + /* Read in a list of stateids provided by the client */
> + for (i = 0; i < test_stateid->ts_num_ids; i++) {
> + if ((i % test_stateid->ts_stateids->items_per_page) == 0) {
> + si = test_stateid->ts_stateids->items[cur_page].first_item;
> + cur_page++;
> + }
> + nfsd4_decode_stateid(argp, si);
> + si++;
> + }
> + kunmap(page);
> + status = 0;
> +out:
> + return status;
> +xdr_error:
> + dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
> + status = nfserr_bad_xdr;
> + nfsd4_free_ts_pagearray(test_stateid->ts_valid);
> +out_free_pages:
> + nfsd4_free_ts_pagearray(test_stateid->ts_stateids);
> + goto out;
> +}
> +
> static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
> {
> DECODE_HEAD;
> @@ -1393,7 +1486,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
> [OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_secinfo_no_name,
> [OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence,
> [OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp,
> - [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
> + [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_test_stateid,
> [OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
> [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp,
> [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete,
> @@ -3166,6 +3259,37 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
> return 0;
> }
>
> +__be32
> +nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
> + struct nfsd4_test_stateid *test_stateid)
> +{
> + __be32 *p;
> + unsigned int i;
> + unsigned int cur_page = 0;
> + int *valid = NULL;
> +
> + if (!nfserr) {
> + RESERVE_SPACE(4);
> + WRITE32(test_stateid->ts_num_ids);
> + ADJUST_ARGS();
> +
> + for (i = 0; i < test_stateid->ts_num_ids; i++) {
> + if ((i % test_stateid->ts_valid->items_per_page) == 0) {
> + valid = (int *)test_stateid->ts_valid->items[cur_page].first_item;
> + cur_page++;
> + }
> + RESERVE_SPACE(4);
> + WRITE32(*valid);
> + ADJUST_ARGS();
> + valid++;
> + }
> + }
> +
> + nfsd4_free_ts_pagearray(test_stateid->ts_valid);
> + nfsd4_free_ts_pagearray(test_stateid->ts_stateids);
> + return nfserr;
> +}
> +
> static __be32
> nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
> {
> @@ -3234,7 +3358,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
> [OP_SECINFO_NO_NAME] = (nfsd4_enc)nfsd4_encode_secinfo_no_name,
> [OP_SEQUENCE] = (nfsd4_enc)nfsd4_encode_sequence,
> [OP_SET_SSV] = (nfsd4_enc)nfsd4_encode_noop,
> - [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_noop,
> + [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_test_stateid,
> [OP_WANT_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
> [OP_DESTROY_CLIENTID] = (nfsd4_enc)nfsd4_encode_noop,
> [OP_RECLAIM_COMPLETE] = (nfsd4_enc)nfsd4_encode_noop,
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index ed1784d..0455f55 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -342,6 +342,24 @@ struct nfsd4_setclientid_confirm {
> nfs4_verifier sc_confirm;
> };
>
> +struct nfsd4_ts_pagearray_item {
> + struct page *page;
> + void *first_item;
> +};
> +
> +struct nfsd4_ts_pagearray {
> + unsigned int num_pages;
> + unsigned int items_per_page;
> + struct page *page_ptr;
> + struct nfsd4_ts_pagearray_item items[0];
> +};
> +
> +struct nfsd4_test_stateid {
> + __be32 ts_num_ids; /* request */
> + struct nfsd4_ts_pagearray *ts_stateids; /* request */
> + struct nfsd4_ts_pagearray *ts_valid; /* response */
> +};
> +
> struct nfsd4_free_stateid {
> stateid_t fr_stateid; /* request */
> __be32 fr_status; /* response */
> @@ -437,6 +455,7 @@ struct nfsd4_op {
> struct nfsd4_destroy_session destroy_session;
> struct nfsd4_sequence sequence;
> struct nfsd4_reclaim_complete reclaim_complete;
> + struct nfsd4_test_stateid test_stateid;
> struct nfsd4_free_stateid free_stateid;
> } u;
> struct nfs4_replay * replay;
> @@ -570,6 +589,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
> struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
> extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
> struct nfsd4_compound_state *, clientid_t *clid);
> +extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
> + struct nfsd4_compound_state *, struct nfsd4_test_stateid *test_stateid);
> extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
> struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
> #endif
> --
> 1.7.5.1
>
From: Bryan Schumaker <[email protected]>
This operation is used by the client to tell the server to free a
stateid.
Signed-off-by: Bryan Schumaker <[email protected]>
---
fs/nfsd/nfs4proc.c | 5 ++++
fs/nfsd/nfs4state.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++
fs/nfsd/nfs4xdr.c | 32 +++++++++++++++++++++++++++-
fs/nfsd/xdr4.h | 8 +++++++
4 files changed, 99 insertions(+), 2 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 3a6dbd7..7e00116 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
.op_flags = OP_HANDLES_WRONGSEC,
.op_name = "OP_SECINFO_NO_NAME",
},
+ [OP_FREE_STATEID] = {
+ .op_func = (nfsd4op_func)nfsd4_free_stateid,
+ .op_flags = ALLOWED_WITHOUT_FH,
+ .op_name = "OP_FREE_STATEID",
+ },
};
static const char *nfsd4_op_name(unsigned opnum)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a2ea14f..6beec13 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -60,6 +60,7 @@ static u64 current_sessionid = 1;
/* forward declarations */
static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
+static struct nfs4_stateid * search_for_stateid(stateid_t *stid);
static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid);
static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
static void nfs4_set_recdir(char *recdir);
@@ -316,6 +317,12 @@ static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE];
static struct list_head client_lru;
static struct list_head close_lru;
+static int
+stateid_has_locks(struct nfs4_stateid *stp)
+{
+ return list_empty(&stp->st_lockowners);
+}
+
/*
* We store the NONE, READ, WRITE, and BOTH bits separately in the
* st_{access,deny}_bmap field of the stateid, in order to track not
@@ -3212,6 +3219,33 @@ out:
return status;
}
+/*
+ * Free a state id
+ */
+__be32
+nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ struct nfsd4_free_stateid *free_stateid)
+{
+ struct nfs4_stateid *stp = NULL;
+ stateid_t *stateid = &free_stateid->fr_stateid;
+
+ stp = search_for_stateid(stateid);
+ if (!stp)
+ return nfserr_bad_stateid;
+ if (stateid->si_generation != 0) {
+ if (stateid->si_generation < stp->st_stateid.si_generation)
+ return nfserr_old_stateid;
+ if (stateid->si_generation > stp->st_stateid.si_generation)
+ return nfserr_bad_stateid;
+ }
+
+ if (stateid_has_locks(stp))
+ return nfserr_locks_held;
+ if (stp)
+ release_open_stateid(stp);
+ return nfs_ok;
+}
+
static inline int
setlkflg (int type)
{
@@ -3619,6 +3653,28 @@ find_stateid(stateid_t *stid, int flags)
return NULL;
}
+static struct nfs4_stateid *
+search_for_stateid(stateid_t *stid)
+{
+ struct nfs4_stateid *local;
+ u32 st_id = stid->si_stateownerid;
+ u32 f_id = stid->si_fileid;
+ unsigned int 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))
+ return local;
+ }
+
+ 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))
+ return local;
+ }
+ return NULL;
+}
+
static struct nfs4_delegation *
find_delegation_stateid(struct inode *ino, stateid_t *stid)
{
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 195a91d..5da6874 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1246,6 +1246,19 @@ nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
}
static __be32
+nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
+ struct nfsd4_free_stateid *free_stateid)
+{
+ DECODE_HEAD;
+
+ READ_BUF(sizeof(stateid_t));
+ READ32(free_stateid->fr_stateid.si_generation);
+ COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
+
+ DECODE_TAIL;
+}
+
+static __be32
nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
struct nfsd4_sequence *seq)
{
@@ -1370,7 +1383,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
[OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id,
[OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session,
[OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session,
- [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
+ [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_free_stateid,
[OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp,
@@ -3115,6 +3128,21 @@ nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
return nfserr;
}
+static __be32
+nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
+ struct nfsd4_free_stateid *free_stateid)
+{
+ __be32 *p;
+
+ if (nfserr)
+ return nfserr;
+
+ RESERVE_SPACE(4);
+ WRITE32(nfserr);
+ ADJUST_ARGS();
+ return nfserr;
+}
+
__be32
nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
struct nfsd4_sequence *seq)
@@ -3196,7 +3224,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
[OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id,
[OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session,
[OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_destroy_session,
- [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_noop,
+ [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_free_stateid,
[OP_GET_DIR_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
[OP_GETDEVICEINFO] = (nfsd4_enc)nfsd4_encode_noop,
[OP_GETDEVICELIST] = (nfsd4_enc)nfsd4_encode_noop,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 366401e..ed1784d 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -342,6 +342,11 @@ struct nfsd4_setclientid_confirm {
nfs4_verifier sc_confirm;
};
+struct nfsd4_free_stateid {
+ stateid_t fr_stateid; /* request */
+ __be32 fr_status; /* response */
+};
+
/* also used for NVERIFY */
struct nfsd4_verify {
u32 ve_bmval[3]; /* request */
@@ -432,6 +437,7 @@ struct nfsd4_op {
struct nfsd4_destroy_session destroy_session;
struct nfsd4_sequence sequence;
struct nfsd4_reclaim_complete reclaim_complete;
+ struct nfsd4_free_stateid free_stateid;
} u;
struct nfs4_replay * replay;
};
@@ -564,6 +570,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, clientid_t *clid);
+extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
+ struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
#endif
/*
--
1.7.5.1