fix for open leaks, for 3.2
Subsystems (2)
Bryan Schumaker noticed that we have a longstanding leak of an open owner each time the first open with a new open owner fails.
The following patches fix that, along with a few other less likely leaks that occur if a memory allocation fails after a file is created.
I intend to commit these to for-3.2. Any review welcomed.
--b.
PATCH 1/8 nfsd4: centralize renew_client() calls
From: "J. Bruce Fields" <[email protected]>
There doesn't seem to be any harm to renewing the client a bit earlier, when it is looked up. That saves us from having to sprinkle renew_client calls over quite so many places.
Also remove a redundant comment and do a little cleanup.
--- fs/nfsd/nfs4state.c | 23 ++++++++++------------- 1 files changed, 10 insertions(+), 13 deletions(-)
fs/nfsd/nfs4state.c +10 -13
@@ -931,9 +931,6 @@ renew_client_locked(struct nfs4_client *clp)return ; } -/* -* Move client to the end to the LRU list. -*/ dprintk ("renewing client (clientid %08x/%08x)\n" , clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
@@ -1220,8 +1217,10 @@ find_confirmed_client(clientid_t *clid)unsigned int idhashval =clientid_hashval (clid->cl_id);list_for_each_entry (clp, &conf_id_hashtbl[idhashval], cl_idhash) { -if (same_clid (&clp->cl_clientid, clid)) +if (same_clid (&clp->cl_clientid, clid)) { +renew_client (clp);return clp; + } }return NULL ; }
@@ -2372,11 +2371,15 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner,static struct nfs4_openowner *find_openstateowner_str (unsigned int hashval,struct nfsd4_open *open) { -struct nfs4_stateowner *so =NULL ; +struct nfs4_stateowner *so; +struct nfs4_openowner *oo;list_for_each_entry (so, &open_ownerstr_hashtbl[hashval], so_strhash) { -if (same_owner_str (so, &open->op_owner, &open->op_clientid)) - return container_of(so, struct nfs4_openowner, oo_owner); +if (same_owner_str (so, &open->op_owner, &open->op_clientid)) { + oo =openowner (so); +renew_client (oo->oo_owner.so_client); +return oo; + } }return NULL ; }
@@ -2536,7 +2539,6 @@ renew: open->op_openowner = oo; }list_del_init (&oo->oo_close_lru); -renew_client (oo->oo_owner.so_client);return nfs_ok; }
@@ -2970,7 +2972,6 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,dprintk ("nfsd4_renew: clientid not found!\n" ); goto out; } -renew_client (clp); status = nfserr_cb_path_down;if (!list_empty (&clp->cl_delegations) && clp->cl_cb_state !=NFSD4_CB_UP )
@@ -3275,7 +3276,6 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, status =nfs4_check_delegmode (dp, flags);if (status) goto out; -renew_client (dp->dl_stid.sc_client);if (filpp) { *filpp = dp->dl_file->fi_deleg_file;BUG_ON (!*filpp);
@@ -3293,7 +3293,6 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, status =nfs4_check_openmode (stp, flags);if (status) goto out; -renew_client (stp->st_stateowner->so_client);if (filpp) {if (flags &RD_STATE ) *filpp =find_readable_file (stp->st_file);
@@ -3412,7 +3411,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,return status; *stpp =openlockstateid (s); cstate->replay_owner = (*stpp)->st_stateowner; -renew_client ((*stpp)->st_stateowner->so_client);return nfs4_seqid_op_checks (cstate, stateid, seqid, *stpp); }
@@ -3643,7 +3641,6 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status =check_stateid_generation (stateid, &dp->dl_stid.sc_stateid,nfsd4_has_session (cstate));if (status) goto out; -renew_client (dp->dl_stid.sc_client);unhash_delegation (dp); out:
Signature · 1 line
1.7.5.4
PATCH 2/8 nfsd4: make is_open_owner boolean
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
--- fs/nfsd/state.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
fs/nfsd/state.h +1 -1
@@ -335,13 +335,13 @@ struct nfs4_replay {struct nfs4_stateowner {struct list_head so_strhash;/* hash by op_name */ struct list_head so_stateids; -int so_is_open_owner;/* 1=openowner,0=lockowner */ struct nfs4_client * so_client;/* after increment in ENCODE_SEQID_OP_TAIL, represents the next * sequence id expected from the client: */ u32 so_seqid;struct xdr_netobj so_owner;/* open owner name */ struct nfs4_replay so_replay; +bool so_is_open_owner; };struct nfs4_openowner {
Signature · 1 line
1.7.5.4
PATCH 3/8 nfsd4: simplify process_open1 logic
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
No change in behavior.
--- fs/nfsd/nfs4state.c | 22 ++++++++-------------- 1 files changed, 8 insertions(+), 14 deletions(-)
fs/nfsd/nfs4state.c +8 -14
@@ -2506,7 +2506,6 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,struct nfs4_client *clp =NULL ;unsigned int strhashval;struct nfs4_openowner *oo =NULL ; -__be32 status;if (STALE_CLIENTID (&open->op_clientid))return nfserr_stale_clientid;
@@ -2515,30 +2514,25 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, oo =find_openstateowner_str (strhashval, open); open->op_openowner = oo;if (!oo) { -/* Make sure the client's lease hasn't expired. */ clp =find_confirmed_client (clientid);if (clp ==NULL )return nfserr_expired; - goto renew; + goto new_owner; }if (!(oo->oo_flags &NFS4_OO_CONFIRMED )) {/* Replace unconfirmed owners without checking for replay. */ clp = oo->oo_owner.so_client;release_openowner (oo); open->op_openowner =NULL ; - goto renew; - } - status =nfsd4_check_seqid (cstate, &oo->oo_owner, open->op_seqid); -if (status) -return status; -renew: -if (open->op_openowner ==NULL ) { - oo =alloc_init_open_stateowner (strhashval, clp, open); -if (oo ==NULL ) -return nfserr_jukebox; - open->op_openowner = oo; + goto new_owner; }list_del_init (&oo->oo_close_lru); +return nfsd4_check_seqid (cstate, &oo->oo_owner, open->op_seqid); +new_owner: + oo =alloc_init_open_stateowner (strhashval, clp, open); +if (oo ==NULL ) +return nfserr_jukebox; + open->op_openowner = oo;return nfs_ok; }
Signature · 1 line
1.7.5.4
PATCH 4/8 nfsd4: clean up open owners on OPEN failure
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
If process_open1() creates a new open owner, but the open later fails, the current code will leave the open owner around. It won't be on the close_lru list, and the client isn't expected to send a CLOSE, so it will hang around as long as the client does.
Similarly, if process_open1() removes an existing open owner from the close lru, anticipating that an open owner that previously had no associated stateid's now will, but the open subsequently fails, then we'll again be left with the same leak.
Fix both problems.
--- fs/nfsd/nfs4proc.c | 1 + fs/nfsd/nfs4state.c | 20 ++++++++++++++++++-- fs/nfsd/state.h | 1 + fs/nfsd/xdr4.h | 1 + 4 files changed, 21 insertions(+), 2 deletions(-)
fs/nfsd/nfs4proc.c +1 -0
@@ -409,6 +409,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,*/ status =nfsd4_process_open2 (rqstp, &cstate->current_fh, open); out: +nfsd4_cleanup_open_state (open, status);if (open->op_openowner) cstate->replay_owner = &open->op_openowner->oo_owner;else
fs/nfsd/nfs4state.c +18 -2
@@ -2320,7 +2320,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, strreturn NULL ; oo->oo_owner.so_is_open_owner =1 ; oo->oo_owner.so_seqid = open->op_seqid; - oo->oo_flags =0 ; + oo->oo_flags =NFS4_OO_NEW ; oo->oo_time =0 ; oo->oo_last_closed_stid =NULL ;INIT_LIST_HEAD (&oo->oo_close_lru);
@@ -2526,7 +2526,6 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, open->op_openowner =NULL ; goto new_owner; } -list_del_init (&oo->oo_close_lru);return nfsd4_check_seqid (cstate, &oo->oo_owner, open->op_seqid); new_owner: oo =alloc_init_open_stateowner (strhashval, clp, open);
@@ -2946,6 +2945,23 @@ out:return status; } +void nfsd4_cleanup_open_state (struct nfsd4_open *open,__be32 status) +{ +if (open->op_openowner) { +struct nfs4_openowner *oo = open->op_openowner; + +if (!list_empty (&oo->oo_owner.so_stateids)) +list_del_init (&oo->oo_close_lru); +if (oo->oo_flags &NFS4_OO_NEW ) { +if (status) { +release_openowner (oo); + open->op_openowner =NULL ; + }else + oo->oo_flags &= ~NFS4_OO_NEW ; + } + } +} +__be32 nfsd4_renew (struct svc_rqst *rqstp,struct nfsd4_compound_state *cstate,clientid_t *clid)
fs/nfsd/state.h +1 -0
@@ -359,6 +359,7 @@ struct nfs4_openowner {time_t oo_time;/* time of placement on so_close_lru */ #define NFS4_OO_CONFIRMED 1#define NFS4_OO_PURGE_CLOSE 2 +#define NFS4_OO_NEW 4unsigned char oo_flags; };
fs/nfsd/xdr4.h +1 -0
@@ -554,6 +554,7 @@ extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *, struct nfsd4_open *open);extern __be32 nfsd4_process_open2 (struct svc_rqst *rqstp,struct svc_fh *current_fh,struct nfsd4_open *open); +extern void nfsd4_cleanup_open_state (struct nfsd4_open *open,__be32 status);extern __be32 nfsd4_open_confirm (struct svc_rqst *rqstp,struct nfsd4_compound_state *,struct nfsd4_open_confirm *oc); extern __be32 nfsd4_close(struct svc_rqst *rqstp,
Signature · 1 line
1.7.5.4
PATCH 5/8 nfsd4: preallocate nfs4_file in process_open1()
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
Creating a new file is an irrevocable step--once it's visible in the filesystem, other processes may have seen it and done something with it, and unlinking it wouldn't simply undo the effects of the create.
Therefore, in the case where OPEN creates a new file, we shouldn't do the create until we know that the rest of the OPEN processing will succeed.
For example, we should preallocate a struct file in case we need it until waiting to allocate it till process_open2(), which is already too late.
--- fs/nfsd/nfs4state.c | 60 ++++++++++++++++++++++++++++++-------------------- fs/nfsd/xdr4.h | 1 + 2 files changed, 37 insertions(+), 24 deletions(-)
fs/nfsd/nfs4state.c +36 -24
@@ -104,6 +104,11 @@ opaque_hashval(const void *ptr, int nbytes)static struct list_head del_recall_lru; +static void nfsd4_free_file (struct nfs4_file *f) +{ +kmem_cache_free (file_slab, f); +} +static inline void put_nfs4_file (struct nfs4_file *fi) {
@@ -111,7 +116,7 @@ put_nfs4_file(struct nfs4_file *fi)list_del (&fi->fi_hash);spin_unlock (&recall_lock);iput (fi->fi_inode); -kmem_cache_free (file_slab, fi); +nfsd4_free_file (fi); } }
@@ -2190,30 +2195,28 @@ out:return status; } +static struct nfs4_file *nfsd4_alloc_file (void ) +{ +return kmem_cache_alloc (file_slab,GFP_KERNEL ); +} +/* OPEN Share state helper functions */ -static inline struct nfs4_file * -alloc_init_file (struct inode *ino) +static void nfsd4_init_file (struct nfs4_file *fp,struct inode *ino) { -struct nfs4_file *fp;unsigned int hashval =file_hashval (ino); - fp =kmem_cache_alloc (file_slab,GFP_KERNEL ); -if (fp) { -atomic_set (&fp->fi_ref,1 ); -INIT_LIST_HEAD (&fp->fi_hash); -INIT_LIST_HEAD (&fp->fi_stateids); -INIT_LIST_HEAD (&fp->fi_delegations); - fp->fi_inode =igrab (ino); - fp->fi_had_conflict = false; - fp->fi_lease =NULL ; -memset (fp->fi_fds,0 ,sizeof (fp->fi_fds)); -memset (fp->fi_access,0 ,sizeof (fp->fi_access)); -spin_lock (&recall_lock); -list_add (&fp->fi_hash, &file_hashtbl[hashval]); -spin_unlock (&recall_lock); -return fp; - } -return NULL ; +atomic_set (&fp->fi_ref,1 ); +INIT_LIST_HEAD (&fp->fi_hash); +INIT_LIST_HEAD (&fp->fi_stateids); +INIT_LIST_HEAD (&fp->fi_delegations); + fp->fi_inode =igrab (ino); + fp->fi_had_conflict = false; + fp->fi_lease =NULL ; +memset (fp->fi_fds,0 ,sizeof (fp->fi_fds)); +memset (fp->fi_access,0 ,sizeof (fp->fi_access)); +spin_lock (&recall_lock); +list_add (&fp->fi_hash, &file_hashtbl[hashval]); +spin_unlock (&recall_lock); } static void
@@ -2509,6 +2512,13 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,if (STALE_CLIENTID (&open->op_clientid))return nfserr_stale_clientid; +/* +* In case we need it later, after we've already created the +* file and don't want to risk a further failure: +*/ + open->op_file =nfsd4_alloc_file (); +if (open->op_file ==NULL ) +return nfserr_jukebox; strhashval =open_ownerstr_hashval (clientid->cl_id, &open->op_owner); oo =find_openstateowner_str (strhashval, open);
@@ -2884,9 +2894,9 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfif (open->op_claim_type ==NFS4_OPEN_CLAIM_DELEGATE_CUR ) goto out; status = nfserr_jukebox; - fp =alloc_init_file (ino); -if (fp ==NULL ) - goto out; + fp = open->op_file; + open->op_file =NULL ; +nfsd4_init_file (fp, ino); }/*
@@ -2960,6 +2970,8 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status) oo->oo_flags &= ~NFS4_OO_NEW ; } } +if (open->op_file) +nfsd4_free_file (open->op_file); } __be32
fs/nfsd/xdr4.h +1 -0
@@ -228,6 +228,7 @@ struct nfsd4_open {u32 op_rflags;/* response */ int op_truncate;/* used during processing */ struct nfs4_openowner *op_openowner;/* used during processing */ +struct nfs4_file *op_file;/* used during processing */ struct nfs4_acl *op_acl; };#define op_iattr iattr
Signature · 1 line
1.7.5.4
PATCH 6/8 nfsd4: do idr preallocation with stateid allocation
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
Move idr preallocation out of stateid initialization, into stateid allocation, so that we no longer have to handle any errors from the former.
This is a little subtle due to the way the idr code manages these preallocated items--document that in comments.
--- fs/nfsd/nfs4state.c | 76 +++++++++++++++++++++++++-------------------------- fs/nfsd/state.h | 4 +- 2 files changed, 39 insertions(+), 41 deletions(-)
fs/nfsd/nfs4state.c +37 -39
@@ -215,13 +215,12 @@ static inline int get_new_stid(struct nfs4_stid *stid)int new_stid;int error; -if (!idr_pre_get (stateids,GFP_KERNEL )) -return -ENOMEM ; - error =idr_get_new_above (stateids, stid, min_stateid, &new_stid);/* -* All this code is currently serialized; the preallocation -* above should still be ours: +* Note: the necessary preallocation was done in +* nfs4_alloc_stateid(). The idr code caps the number of +* preallocations that can exist at a time, but the state lock +* prevents anyone from using ours before we get here: */ BUG_ON (error);/*
@@ -240,7 +239,7 @@ static inline int get_new_stid(struct nfs4_stid *stid)return new_stid; } -static inline __be32 init_stid (struct nfs4_stid *stid,struct nfs4_client *cl,unsigned char type) +static void init_stid (struct nfs4_stid *stid,struct nfs4_client *cl,unsigned char type) {stateid_t *s = &stid->sc_stateid;int new_id;
@@ -249,12 +248,24 @@ static inline __be32 init_stid(struct nfs4_stid *stid, struct nfs4_client *cl, u stid->sc_client = cl; s->si_opaque.so_clid = cl->cl_clientid; new_id =get_new_stid (stid); -if (new_id <0 ) -return nfserr_jukebox; s->si_opaque.so_id = (u32 )new_id;/* Will be incremented before return to client: */ s->si_generation =0 ; -return 0 ; +} + +static struct nfs4_stid *nfs4_alloc_stid (struct nfs4_client *cl,struct kmem_cache *slab) +{ +struct idr *stateids = &cl->cl_stateids; + +if (!idr_pre_get (stateids,GFP_KERNEL )) +return NULL ; +/* +* Note: if we fail here (or any time between now and the time +* we actually get the new idr), we won't need to undo the idr +* preallocation, since the idr code caps the number of +* preallocated entries. +*/ +return kmem_cache_alloc (slab,GFP_KERNEL ); } static struct nfs4_delegation *
@@ -262,7 +273,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv {struct nfs4_delegation *dp;struct nfs4_file *fp = stp->st_file; -__be32 status;dprintk ("NFSD alloc_init_deleg\n" );/*
@@ -276,14 +286,10 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svreturn NULL ;if (num_delegations > max_delegations)return NULL ; - dp =kmem_cache_alloc (deleg_slab,GFP_KERNEL ); + dp =delegstateid (nfs4_alloc_stid (clp, deleg_slab));if (dp ==NULL )return dp; - status =init_stid (&dp->dl_stid, clp,NFS4_DELEG_STID ); -if (status) { -kmem_cache_free (deleg_slab, dp); -return NULL ; - } +init_stid (&dp->dl_stid, clp,NFS4_DELEG_STID );/* * delegation seqid's are never incremented. The 4.1 special * meaning of seqid 0 isn't meaningful, really, but let's avoid
@@ -2331,14 +2337,11 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, strreturn oo; } -static inline __be32 init_open_stateid (struct nfs4_ol_stateid *stp,struct nfs4_file *fp,struct nfsd4_open *open) { +static void init_open_stateid (struct nfs4_ol_stateid *stp,struct nfs4_file *fp,struct nfsd4_open *open) {struct nfs4_openowner *oo = open->op_openowner;struct nfs4_client *clp = oo->oo_owner.so_client; -__be32 status; - status =init_stid (&stp->st_stid, clp,NFS4_OPEN_STID ); -if (status) -return status; +init_stid (&stp->st_stid, clp,NFS4_OPEN_STID );INIT_LIST_HEAD (&stp->st_lockowners);list_add (&stp->st_perstateowner, &oo->oo_owner.so_stateids);list_add (&stp->st_perfile, &fp->fi_stateids);
@@ -2350,7 +2353,6 @@ static inline __be32 init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4___set_bit (open->op_share_access, &stp->st_access_bmap);__set_bit (open->op_share_deny, &stp->st_deny_bmap); stp->st_openstp =NULL ; -return nfs_ok; } static void
@@ -2614,10 +2616,14 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_streturn nfs_ok; } -static inline struct nfs4_ol_stateid * -nfs4_alloc_stateid (void ) +static struct nfs4_ol_stateid *nfs4_alloc_stateid (struct nfs4_client *clp) { -return kmem_cache_alloc (stateid_slab,GFP_KERNEL ); +return openlockstateid (nfs4_alloc_stid (clp, stateid_slab)); +} + +static void nfs4_free_stateid (struct nfs4_ol_stateid *s) +{ +kmem_cache_free (stateid_slab, s); }static inline int nfs4_access_to_access (u32 nfs4_access)
@@ -2661,15 +2667,16 @@ nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_ol_stateid **stpp, struct nfsd4_open *open) {struct nfs4_ol_stateid *stp; +struct nfs4_client *cl = open->op_openowner->oo_owner.so_client;__be32 status; - stp =nfs4_alloc_stateid (); + stp =nfs4_alloc_stateid (cl);if (stp ==NULL )return nfserr_jukebox; status =nfs4_get_vfs_file (rqstp, fp, cur_fh, open);if (status) { -kmem_cache_free (stateid_slab, stp); +nfs4_free_stateid (stp);return status; } *stpp = stp;
@@ -2912,11 +2919,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf status =nfs4_new_open (rqstp, &stp, fp, current_fh, open);if (status) goto out; - status =init_open_stateid (stp, fp, open); -if (status) { -release_open_stateid (stp); - goto out; - } +init_open_stateid (stp, fp, open); status =nfsd4_truncate (rqstp, current_fh, open);if (status) {release_open_stateid (stp);
@@ -3812,16 +3815,11 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct {struct nfs4_ol_stateid *stp;struct nfs4_client *clp = lo->lo_owner.so_client; -__be32 status; - stp =nfs4_alloc_stateid (); + stp =nfs4_alloc_stateid (clp);if (stp ==NULL )return NULL ; - status =init_stid (&stp->st_stid, clp,NFS4_LOCK_STID ); -if (status) { -free_generic_stateid (stp); -return NULL ; - } +init_stid (&stp->st_stid, clp,NFS4_LOCK_STID );list_add (&stp->st_perfile, &fp->fi_stateids);list_add (&stp->st_perstateowner, &lo->lo_owner.so_stateids); stp->st_stateowner = &lo->lo_owner;
fs/nfsd/state.h +2 -2
@@ -85,6 +85,7 @@ struct nfs4_stid { };struct nfs4_delegation { +struct nfs4_stid dl_stid;/* must be first field */ struct list_head dl_perfile;struct list_head dl_perclnt;struct list_head dl_recall_lru;/* delegation recalled */
@@ -93,7 +94,6 @@ struct nfs4_delegation {u32 dl_type;time_t dl_time;/* For recall: */ -struct nfs4_stid dl_stid;struct knfsd_fh dl_fh;int dl_retries;struct nfsd4_callback dl_recall;
@@ -434,7 +434,7 @@ static inline struct file *find_any_file(struct nfs4_file *f)/* "ol" stands for "Open or Lock". Better suggestions welcome. */ struct nfs4_ol_stateid { -struct nfs4_stid st_stid; +struct nfs4_stid st_stid;/* must be first field */ struct list_head st_perfile;struct list_head st_perstateowner;struct list_head st_lockowners;
Signature · 1 line
1.7.5.4
PATCH 7/8 nfsd4: preallocate open stateid in process_open1()
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
As with the nfs4_file, we'd prefer to find out about any failure before creating a new file rather than after.
--- fs/nfsd/nfs4state.c | 49 ++++++++++++++++++++----------------------------- fs/nfsd/xdr4.h | 1 + 2 files changed, 21 insertions(+), 29 deletions(-)
fs/nfsd/nfs4state.c +20 -29
@@ -268,6 +268,11 @@ static struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cacreturn kmem_cache_alloc (slab,GFP_KERNEL ); } +static struct nfs4_ol_stateid *nfs4_alloc_stateid (struct nfs4_client *clp) +{ +return openlockstateid (nfs4_alloc_stid (clp, stateid_slab)); +} +static struct nfs4_delegation *alloc_init_deleg (struct nfs4_client *clp,struct nfs4_ol_stateid *stp,struct svc_fh *current_fh,u32 type) {
@@ -2511,6 +2516,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,struct nfs4_client *clp =NULL ;unsigned int strhashval;struct nfs4_openowner *oo =NULL ; +__be32 status;if (STALE_CLIENTID (&open->op_clientid))return nfserr_stale_clientid;
@@ -2538,12 +2544,20 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, open->op_openowner =NULL ; goto new_owner; } -return nfsd4_check_seqid (cstate, &oo->oo_owner, open->op_seqid); + status =nfsd4_check_seqid (cstate, &oo->oo_owner, open->op_seqid); +if (status) +return status; + clp = oo->oo_owner.so_client; + goto alloc_stateid; new_owner: oo =alloc_init_open_stateowner (strhashval, clp, open);if (oo ==NULL )return nfserr_jukebox; open->op_openowner = oo; +alloc_stateid: + open->op_stp =nfs4_alloc_stateid (clp); +if (!open->op_stp) +return nfserr_jukebox;return nfs_ok; }
@@ -2616,11 +2630,6 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_streturn nfs_ok; } -static struct nfs4_ol_stateid *nfs4_alloc_stateid (struct nfs4_client *clp) -{ -return openlockstateid (nfs4_alloc_stid (clp, stateid_slab)); -} -static void nfs4_free_stateid (struct nfs4_ol_stateid *s) {kmem_cache_free (stateid_slab, s);
@@ -2661,28 +2670,6 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,return nfs_ok; } -static __be32 -nfs4_new_open (struct svc_rqst *rqstp,struct nfs4_ol_stateid **stpp, -struct nfs4_file *fp,struct svc_fh *cur_fh, -struct nfsd4_open *open) -{ -struct nfs4_ol_stateid *stp; -struct nfs4_client *cl = open->op_openowner->oo_owner.so_client; -__be32 status; - - stp =nfs4_alloc_stateid (cl); -if (stp ==NULL ) -return nfserr_jukebox; - - status =nfs4_get_vfs_file (rqstp, fp, cur_fh, open); -if (status) { -nfs4_free_stateid (stp); -return status; - } - *stpp = stp; -return 0 ; -} -static inline __be32 nfsd4_truncate (struct svc_rqst *rqstp,struct svc_fh *fh,struct nfsd4_open *open)
@@ -2916,9 +2903,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfif (status) goto out; }else { - status =nfs4_new_open (rqstp, &stp, fp, current_fh, open); + status =nfs4_get_vfs_file (rqstp, fp, current_fh, open);if (status) goto out; + stp = open->op_stp; + open->op_stp =NULL ;init_open_stateid (stp, fp, open); status =nfsd4_truncate (rqstp, current_fh, open);if (status) {
@@ -2975,6 +2964,8 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status) }if (open->op_file)nfsd4_free_file (open->op_file); +if (open->op_stp) +nfs4_free_stateid (open->op_stp); } __be32
fs/nfsd/xdr4.h +1 -0
@@ -229,6 +229,7 @@ struct nfsd4_open {int op_truncate;/* used during processing */ struct nfs4_openowner *op_openowner;/* used during processing */ struct nfs4_file *op_file;/* used during processing */ +struct nfs4_ol_stateid *op_stp;/* used during processing */ struct nfs4_acl *op_acl; };#define op_iattr iattr
Signature · 1 line
1.7.5.4
PATCH 8/8 nfsd4: warn on open failure after create
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
If we create the object and then return failure to the client, we're left with an unexpected file in the filesystem.
I'm trying to eliminate such cases but not 100% sure I have so an assertion might be helpful for now.
--- fs/nfsd/nfs4proc.c | 7 ++++--- fs/nfsd/vfs.c | 2 +- fs/nfsd/vfs.h | 2 +- fs/nfsd/xdr4.h | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-)
fs/nfsd/nfs4proc.c +4 -3
@@ -194,7 +194,6 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o {struct svc_fh resfh;__be32 status; -int created =0 ;fh_init (&resfh,NFS4_FHSIZE ); open->op_truncate =0 ;
@@ -223,7 +222,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o open->op_fname.len, &open->op_iattr, &resfh, open->op_createmode, (u32 *)open->op_verf.data, - &open->op_truncate, &created); + &open->op_truncate, &open->op_created);/* * Following rfc 3530 14.2.16, use the returned bitmask
@@ -253,7 +252,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o/* set reply cache */ fh_copy_shallow (&open->op_openowner->oo_owner.so_replay.rp_openfh, &resfh.fh_handle); -if (!created) +if (!open->op_created) status =do_open_permission (rqstp, current_fh, open,NFSD_MAY_NOP );
@@ -318,6 +317,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,/* We don't yet support WANT bits: */ open->op_share_access &=NFS4_SHARE_ACCESS_MASK ; + open->op_created =0 ;/* * RFC5661 18.51.3 * Before RECLAIM_COMPLETE done, server should deny new lock
@@ -408,6 +408,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,* set, (2) sets open->op_stateid, (3) sets open->op_delegation. */ status =nfsd4_process_open2 (rqstp, &cstate->current_fh, open); +WARN_ON (status && open->op_created); out:nfsd4_cleanup_open_state (open, status);if (open->op_openowner)
fs/nfsd/vfs.c +1 -1
@@ -1370,7 +1370,7 @@ __be32 do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, char *fname, int flen, struct iattr *iap, struct svc_fh *resfhp, int createmode, u32 *verifier, - int *truncp, int *created) + bool *truncp, bool *created) { struct dentry *dentry, *dchild = NULL;struct inode *dirp;
fs/nfsd/vfs.h +1 -1
@@ -62,7 +62,7 @@ __be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *);__be32 do_nfsd_create (struct svc_rqst *,struct svc_fh *,char *name,int len,struct iattr *attrs,struct svc_fh *res,int createmode, -u32 *verifier,int *truncp,int *created); +u32 *verifier,bool *truncp,bool *created);__be32 nfsd_commit (struct svc_rqst *,struct svc_fh *,loff_t ,unsigned long );#endif /* CONFIG_NFSD_V3 */
fs/nfsd/xdr4.h +2 -1
@@ -226,7 +226,8 @@ struct nfsd4_open {u32 op_recall;/* recall */ struct nfsd4_change_info op_cinfo;/* response */ u32 op_rflags;/* response */ -int op_truncate;/* used during processing */ +bool op_truncate;/* used during processing */ +bool op_created;/* used during processing */ struct nfs4_openowner *op_openowner;/* used during processing */ struct nfs4_file *op_file;/* used during processing */ struct nfs4_ol_stateid *op_stp;/* used during processing */
Signature · 1 line
1.7.5.4
Quoted 5 lines · J. Bruce Fields
Bryan Schumaker noticed that we have a longstanding leak of an open owner each time the first open with a new open owner fails.
The following patches fix that, along with a few other less likely leaks that occur if a memory allocation fails after a file is created.
By the way, Bryan, I didn't try to write a real test case for that leak--would it be easy for you to rerun the test you were running before and verify whether it's gone?
--b.
Quoted 8 lines
I intend to commit these to for-3.2. Any review welcomed.
--b.
--
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
Quoted 10 lines · J. Bruce Fields
Quoted 5 lines · J. Bruce Fields
Bryan Schumaker noticed that we have a longstanding leak of an open owner each time the first open with a new open owner fails.
The following patches fix that, along with a few other less likely leaks that occur if a memory allocation fails after a file is created.
By the way, Bryan, I didn't try to write a real test case for that leak--would it be easy for you to rerun the test you were running before and verify whether it's gone?
Sure, no problem. I'll let you know what I find.
- Bryan
Quoted 15 lines
--b.
Quoted 8 lines
I intend to commit these to for-3.2. Any review welcomed.
--b.
--
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--
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
Quoted 5 lines · Bryan Schumaker
Quoted 4 lines · J. Bruce Fields
Quoted 3 lines · J. Bruce Fields
Bryan Schumaker noticed that we have a longstanding leak of an open owner each time the first open with a new open owner fails.
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
The following patches fix that, along with a few other less likely leaks that occur if a memory allocation fails after a file is created.
By the way, Bryan, I didn't try to write a real test case for that leak--would it be easy for you to rerun the test you were running before and verify whether it's gone?
Sure, no problem. I'll let you know what I find.
Looks like these patches worked. I never deleted more than one client structure during my test.
- Bryan
Quoted 22 lines
- Bryan
Quoted 11 lines
--b.
Quoted 8 lines
I intend to commit these to for-3.2. Any review welcomed.
--b.
--
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.htmlQuoted 4 lines
--
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--
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