From patchwork Tue Sep 5 21:35:47 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: NeilBrown X-Patchwork-Id: 9939637 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id A4132604D4 for ; Tue, 5 Sep 2017 21:36:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 930E428A1A for ; Tue, 5 Sep 2017 21:36:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 84B6728A18; Tue, 5 Sep 2017 21:36:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 365D828A14 for ; Tue, 5 Sep 2017 21:36:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752682AbdIEVgB (ORCPT ); Tue, 5 Sep 2017 17:36:01 -0400 Received: from mx2.suse.de ([195.135.220.15]:35794 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751413AbdIEVgA (ORCPT ); Tue, 5 Sep 2017 17:36:00 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id CF6F8ADAA; Tue, 5 Sep 2017 21:35:58 +0000 (UTC) From: NeilBrown To: "J. Bruce Fields" Date: Wed, 06 Sep 2017 07:35:47 +1000 Cc: linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Trond Myklebust Subject: Re: [PATCH 2/3] fs: hide another detail of delegation logic In-Reply-To: <20170905195619.GB17828@parsley.fieldses.org> References: <1503697958-6122-3-git-send-email-bfields@redhat.com> <878ti4i9pi.fsf@notabene.neil.brown.name> <20170829214026.GI8822@parsley.fieldses.org> <8760d5hol3.fsf@notabene.neil.brown.name> <20170830170938.GC24373@parsley.fieldses.org> <87wp5kfxi3.fsf@notabene.neil.brown.name> <20170831190531.GA8223@parsley.fieldses.org> <873787fhc5.fsf@notabene.neil.brown.name> <20170901161809.GA22140@parsley.fieldses.org> <871snndq04.fsf@notabene.neil.brown.name> <20170905195619.GB17828@parsley.fieldses.org> Message-ID: <877excde18.fsf@notabene.neil.brown.name> MIME-Version: 1.0 Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP On Tue, Sep 05 2017, J. Bruce Fields wrote: > On Mon, Sep 04, 2017 at 02:52:43PM +1000, NeilBrown wrote: >> On Fri, Sep 01 2017, J. Bruce Fields wrote: >> >> >> >> >> nfsd would need to find that delegation, prevent further delegations >> >> being handed out, and check that there aren't already conflicting >> >> delegations. If there are conflicts, recall them. Once there are no >> >> conflicting delegations, make the vfs_ request. >> > >> > The way that we currently serialize setting, unsetting, and breaking >> > delegations is by locks on the delegated inodes which aren't taken till >> > deeper in the vfs code. >> >> Do we? >> I can see nfs4_set_delegation adding a new delegation for a new client >> without entering the vfs at all if there is already a lease held. > > By "delegations", I meant locks of type FL_DELEG. But even then I was > wrong, apologies. > > There is an inode_trylock in generic_add_lease that will prevent any new > delegations from being given while the inode's locked. > >> If there isn't a lease already, vfs_setlease() is called, which doesn't >> its own internal locking of course. Much the same applies to unsetting >> delegations. >> Breaking delegations involves nfsd_break_deleg_cb() which has a comment >> that it is called with i_lock held.... that seems to be used to >> be sure that it is safe to a reference to the delegation state id. >> Is that the only dependency on the vfs locking, or did I miss something? >> >> > >> > I guess you're suggesting adding a second mechanism to prevent >> > delegations being given out on the inode. We could add an atomic >> > counter taken by each nfsd breaker while it's in progress. Hrm. >> >> Something like that. >> We would also need to be able to look up an nfs4_file by inode (why >> *are* they hashed by file handle??) > > Grepping the logs.... That was ca9432178378 "nfsd: Use the filehandle > to look up the struct nfs4_file instead of inode" which doesn't give a > full justification. Later commits suggest it might be about keeping > nfsv4 state in many-to-one filehandle->inode cases (spec requirement, I > believe) and preventing the nfs4_file from pinning the inode (not seeing > immediately why that was an issue). > > Anyway, I can't think of a reason why hashing the filehandle's a > problem. Thanks for the background. I didn't see it as a problem exactly, though I did wonder about different filehandles mapping to the same nfs4_file (unlikely but possible). You say that is required and I can see how that might be. My perspective was more that I do want to perform a lookup by inode. When an UNLINK arrives we lookup the dentry and so know the inode. Then we want to see if the client holds a delegation. So we want to find the nfs4_file given the dentry/inode. We could, of course, use export_encode_fh, but that seems a bit round-about. We could add a second index, but would need to allow that there could be multiple nfs4_files for a given inode. > >> and add some wait queue somewhere >> so the breaker could wait for a delegation to be returned. > > In the nfsd case we're just returning to the client immediately, so > that's not really necessary, though maybe it could be useful. Ah yes, so we do. I inverted the logic in my mind. That makes it easier. > >> My big-picture point is that any complexity created by NFSD's choice to >> merge delegations to multiple clients into a single vfs-level delegation >> should be handled by NFSD, and not imposed on the VFS. >> It certainly makes sense for the VFS to understand that certain >> operations are being performed by an fl_owner_t, and to allow >> delegations to that owner to remain. It doesn't make as much sense for >> the VFS to understand that there is a finer granularity of ownership >> than the one that it already supports. > > Fair enough, I'll think about that. Thanks. Below is a patch that does compile but is probably wrong is various ways and definitely needs cleanliness work at least. I provide it just to be more concrete about my thinking. NeilBrown diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index d2fb9c8ed205..c823a244f8b2 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -216,7 +216,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid, newattrs.ia_gid = gid; newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; inode_lock(d_inode(dentry)); - notify_change(dentry, &newattrs, NULL); + notify_change(dentry, &newattrs, NULL, NULL); inode_unlock(d_inode(dentry)); /* mark as kernel-created inode */ @@ -323,9 +323,9 @@ static int handle_remove(const char *nodename, struct device *dev) newattrs.ia_valid = ATTR_UID|ATTR_GID|ATTR_MODE; inode_lock(d_inode(dentry)); - notify_change(dentry, &newattrs, NULL); + notify_change(dentry, &newattrs, NULL, NULL); inode_unlock(d_inode(dentry)); - err = vfs_unlink(d_inode(parent.dentry), dentry, NULL); + err = vfs_unlink(d_inode(parent.dentry), dentry, NULL, NULL); if (!err || err == -ENOENT) deleted = 1; } diff --git a/fs/attr.c b/fs/attr.c index 135304146120..d94e516070af 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -185,6 +185,7 @@ EXPORT_SYMBOL(setattr_copy); * notify_change - modify attributes of a filesytem object * @dentry: object affected * @iattr: new attributes + * @owner: allow delegations to this owner to remain * @delegated_inode: returns inode, if the inode is delegated * * The caller must hold the i_mutex on the affected object. @@ -201,7 +202,7 @@ EXPORT_SYMBOL(setattr_copy); * the file open for write, as there can be no conflicting delegation in * that case. */ -int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) +int notify_change(struct dentry * dentry, struct iattr * attr, fl_owner_t owner, struct inode **delegated_inode) { struct inode *inode = dentry->d_inode; umode_t mode = inode->i_mode; @@ -304,7 +305,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de error = security_inode_setattr(dentry, attr); if (error) return error; - error = try_break_deleg(inode, delegated_inode); + error = try_break_deleg(inode, owner, delegated_inode); if (error) return error; diff --git a/fs/inode.c b/fs/inode.c index 50370599e371..c28fbb91b863 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1788,7 +1788,7 @@ static int __remove_privs(struct dentry *dentry, int kill) * Note we call this on write, so notify_change will not * encounter any conflicting delegations: */ - return notify_change(dentry, &newattrs, NULL); + return notify_change(dentry, &newattrs, NULL, NULL); } /* diff --git a/fs/locks.c b/fs/locks.c index afefeb4ad6de..231d93bfbdc1 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1408,6 +1408,8 @@ static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker) return false; if ((breaker->fl_flags & FL_DELEG) && (lease->fl_flags & FL_LEASE)) return false; + if (breaker->fl_owner && breaker->fl_owner == lease->fl_owner) + return false; return locks_conflict(breaker, lease); } @@ -1429,6 +1431,7 @@ any_leases_conflict(struct inode *inode, struct file_lock *breaker) /** * __break_lease - revoke all outstanding leases on file * @inode: the inode of the file to return + * @owner: if non-NULL, ignore leases held by this owner. * @mode: O_RDONLY: break only write leases; O_WRONLY or O_RDWR: * break all leases * @type: FL_LEASE: break leases and delegations; FL_DELEG: break @@ -1439,7 +1442,7 @@ any_leases_conflict(struct inode *inode, struct file_lock *breaker) * a call to open() or truncate(). This function can sleep unless you * specified %O_NONBLOCK to your open(). */ -int __break_lease(struct inode *inode, unsigned int mode, unsigned int type) +int __break_lease(struct inode *inode, fl_owner_t owner, unsigned int mode, unsigned int type) { int error = 0; struct file_lock_context *ctx; @@ -1452,6 +1455,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type) if (IS_ERR(new_fl)) return PTR_ERR(new_fl); new_fl->fl_flags = type; + new_fl->fl_owner = owner; /* typically we will check that ctx is non-NULL before calling */ ctx = smp_load_acquire(&inode->i_flctx); diff --git a/fs/namei.c b/fs/namei.c index ddb6a7c2b3d4..a1bf2ccdabb5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3941,6 +3941,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname) * vfs_unlink - unlink a filesystem object * @dir: parent directory * @dentry: victim + * @owner: allow delegation to this owner to remain. * @delegated_inode: returns victim inode, if the inode is delegated. * * The caller must hold dir->i_mutex. @@ -3955,7 +3956,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname) * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) +int vfs_unlink(struct inode *dir, struct dentry *dentry, fl_owner_t owner, struct inode **delegated_inode) { struct inode *target = dentry->d_inode; int error = may_delete(dir, dentry, 0); @@ -3972,7 +3973,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate else { error = security_inode_unlink(dir, dentry); if (!error) { - error = try_break_deleg(target, delegated_inode); + error = try_break_deleg(target, owner, delegated_inode); if (error) goto out; error = dir->i_op->unlink(dir, dentry); @@ -4040,7 +4041,7 @@ static long do_unlinkat(int dfd, const char __user *pathname) error = security_path_unlink(&path, dentry); if (error) goto exit2; - error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode); + error = vfs_unlink(path.dentry->d_inode, dentry, NULL, &delegated_inode); exit2: dput(dentry); } @@ -4049,7 +4050,7 @@ static long do_unlinkat(int dfd, const char __user *pathname) iput(inode); /* truncate the inode here */ inode = NULL; if (delegated_inode) { - error = break_deleg_wait(&delegated_inode); + error = break_deleg_wait(NULL, &delegated_inode); if (!error) goto retry_deleg; } @@ -4152,6 +4153,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn * @old_dentry: object to be linked * @dir: new parent * @new_dentry: where to create the new link + * @owner: allow delegation to this owner to remain * @delegated_inode: returns inode needing a delegation break * * The caller must hold dir->i_mutex @@ -4166,7 +4168,8 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) +int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, + fl_owner_t owner, struct inode **delegated_inode) { struct inode *inode = old_dentry->d_inode; unsigned max_links = dir->i_sb->s_max_links; @@ -4210,7 +4213,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de else if (max_links && inode->i_nlink >= max_links) error = -EMLINK; else { - error = try_break_deleg(inode, delegated_inode); + error = try_break_deleg(inode, owner, delegated_inode); if (!error) error = dir->i_op->link(old_dentry, dir, new_dentry); } @@ -4280,11 +4283,11 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, error = security_path_link(old_path.dentry, &new_path, new_dentry); if (error) goto out_dput; - error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); + error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, NULL, &delegated_inode); out_dput: done_path_create(&new_path, new_dentry); if (delegated_inode) { - error = break_deleg_wait(&delegated_inode); + error = break_deleg_wait(NULL, &delegated_inode); if (!error) { path_put(&old_path); goto retry; @@ -4312,6 +4315,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname * @old_dentry: source * @new_dir: parent of destination * @new_dentry: destination + * @owner: allow delegation to this owner to remain * @delegated_inode: returns an inode needing a delegation break * @flags: rename flags * @@ -4358,6 +4362,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname */ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, + fl_owner_t owner, struct inode **delegated_inode, unsigned int flags) { int error; @@ -4435,12 +4440,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (is_dir && !(flags & RENAME_EXCHANGE) && target) shrink_dcache_parent(new_dentry); if (!is_dir) { - error = try_break_deleg(source, delegated_inode); + error = try_break_deleg(source, owner, delegated_inode); if (error) goto out; } if (target && !new_is_dir) { - error = try_break_deleg(target, delegated_inode); + error = try_break_deleg(target, owner, delegated_inode); if (error) goto out; } @@ -4594,7 +4599,7 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, goto exit5; error = vfs_rename(old_path.dentry->d_inode, old_dentry, new_path.dentry->d_inode, new_dentry, - &delegated_inode, flags); + NULL, &delegated_inode, flags); exit5: dput(new_dentry); exit4: @@ -4602,7 +4607,7 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, exit3: unlock_rename(new_path.dentry, old_path.dentry); if (delegated_inode) { - error = break_deleg_wait(&delegated_inode); + error = break_deleg_wait(NULL, &delegated_inode); if (!error) goto retry_deleg; } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0c04f81aa63b..e713484b93b3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3654,6 +3654,27 @@ find_file_locked(struct knfsd_fh *fh, unsigned int hashval) return NULL; } +static struct nfs4_file * +find_deleg_file_by_inode(struct inode *ino) +{ + int i; + struct nfs4_file *fp; + + if (!ino) + return NULL; + + rcu_read_lock(); + for (i = 0; i < FILE_HASH_SIZE; i++) + hlist_for_each_entry_rcu(fp, &file_hashtbl[i], fi_hash) + if (fp->fi_deleg_file && file_inode(fp->fi_deleg_file) == ino) + if (atomic_inc_not_zero(&fp->fi_ref)) { + rcu_read_unlock(); + return fp; + } + + return NULL; +} + struct nfs4_file * find_file(struct knfsd_fh *fh) { @@ -3825,6 +3846,49 @@ nfsd_break_deleg_cb(struct file_lock *fl) return ret; } +static struct nfs4_client *nfsd4_client_from_rqst(struct svc_rqst *rqst) +{ + struct nfsd4_compoundres *resp; + + /* + * In case it's possible we could be called from NLM or ACL + * code?: + */ + if (rqst->rq_prog != NFS_PROGRAM) + return NULL; + if (rqst->rq_vers != 4) + return NULL; + resp = rqst->rq_resp; + return resp->cstate.clp; +} + +int nfsd_conflicting_leases(struct dentry *dentry, struct svc_rqst *rqstp) +{ + struct nfs4_client *cl; + struct nfs4_delegation *dl; + struct nfs4_file *fi; + bool conflict; + + cl = nfsd4_client_from_rqst(rqstp); + if (!cl) + return 0; + fi = find_deleg_file_by_inode(d_inode(dentry)); + if (!fi) + return 0; + + spin_lock(&fi->fi_lock); + conflict = false; + list_for_each_entry(dl, &fi->fi_delegations, dl_perfile) { + if (dl->dl_stid.sc_client != cl) { + fi->fi_had_conflict = true; + nfsd_break_one_deleg(dl); + conflict = true; + } + } + spin_unlock(&fi->fi_lock); + return conflict ? -EWOULDBLOCK : 0; +} + static int nfsd_change_deleg_cb(struct file_lock *onlist, int arg, struct list_head *dispose) @@ -4137,6 +4201,8 @@ static bool nfsd4_cb_channel_good(struct nfs4_client *clp) return clp->cl_minorversion && clp->cl_cb_state == NFSD4_CB_UNKNOWN; } +char nfsd_deleg_owner[1]; + static struct file_lock *nfs4_alloc_init_lease(struct nfs4_file *fp, int flag) { struct file_lock *fl; @@ -4148,7 +4214,7 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_file *fp, int flag) fl->fl_flags = FL_DELEG; fl->fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK; fl->fl_end = OFFSET_MAX; - fl->fl_owner = (fl_owner_t)fp; + fl->fl_owner = (fl_owner_t)nfsd_deleg_owner; fl->fl_pid = current->tgid; return fl; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index bc69d40c4e8b..c091633fe441 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -75,6 +75,9 @@ struct raparm_hbucket { #define RAPARM_HASH_MASK (RAPARM_HASH_SIZE-1) static struct raparm_hbucket raparm_hash[RAPARM_HASH_SIZE]; +bool nfsd_conflicting_leases(struct dentry *dentry, struct svc_rqst *rqstp); +extern char nfsd_deleg_owner[1]; + /* * Called from nfsd_lookup and encode_dirent. Check if we have crossed * a mount point. @@ -455,7 +458,8 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, .ia_size = iap->ia_size, }; - host_err = notify_change(dentry, &size_attr, NULL); + host_err = nfsd_conflicting_leases(dentry, rqstp); + host_err = host_err ?: notify_change(dentry, &size_attr, nfsd_deleg_owner, NULL); if (host_err) goto out_unlock; iap->ia_valid &= ~ATTR_SIZE; @@ -470,7 +474,8 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, } iap->ia_valid |= ATTR_CTIME; - host_err = notify_change(dentry, iap, NULL); + host_err = nfsd_conflicting_leases(dentry, rqstp); + host_err = host_err ?: notify_change(dentry, iap, nfsd_deleg_owner, NULL); out_unlock: fh_unlock(fhp); @@ -1590,7 +1595,8 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, err = nfserr_noent; if (d_really_is_negative(dold)) goto out_dput; - host_err = vfs_link(dold, dirp, dnew, NULL); + host_err = nfsd_conflicting_leases(dold, rqstp); + host_err = host_err ?: vfs_link(dold, dirp, dnew, nfsd_deleg_owner, NULL); if (!host_err) { err = nfserrno(commit_metadata(ffhp)); if (!err) @@ -1683,7 +1689,9 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, if (ffhp->fh_export->ex_path.dentry != tfhp->fh_export->ex_path.dentry) goto out_dput_new; - host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL, 0); + host_err = nfsd_conflicting_leases(odentry, rqstp); + host_err |= nfsd_conflicting_leases(ndentry, rqstp); + host_err = host_err ?: vfs_rename(fdir, odentry, tdir, ndentry, nfsd_deleg_owner, NULL, 0); if (!host_err) { host_err = commit_metadata(tfhp); if (!host_err) @@ -1752,9 +1760,10 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (!type) type = d_inode(rdentry)->i_mode & S_IFMT; - if (type != S_IFDIR) - host_err = vfs_unlink(dirp, rdentry, NULL); - else + if (type != S_IFDIR) { + host_err = nfsd_conflicting_leases(dentry, rqstp); + host_err = host_err ?: vfs_unlink(dirp, rdentry, nfsd_deleg_owner, NULL); + } else host_err = vfs_rmdir(dirp, rdentry); if (!host_err) host_err = commit_metadata(fhp); diff --git a/fs/open.c b/fs/open.c index 35bb784763a4..fad27de55ec0 100644 --- a/fs/open.c +++ b/fs/open.c @@ -60,7 +60,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, inode_lock(dentry->d_inode); /* Note any delegations or leases have already been broken: */ - ret = notify_change(dentry, &newattrs, NULL); + ret = notify_change(dentry, &newattrs, NULL, NULL); inode_unlock(dentry->d_inode); return ret; } @@ -529,11 +529,11 @@ static int chmod_common(const struct path *path, umode_t mode) goto out_unlock; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change(path->dentry, &newattrs, NULL, &delegated_inode); out_unlock: inode_unlock(inode); if (delegated_inode) { - error = break_deleg_wait(&delegated_inode); + error = break_deleg_wait(NULL, &delegated_inode); if (!error) goto retry_deleg; } @@ -609,10 +609,10 @@ static int chown_common(const struct path *path, uid_t user, gid_t group) inode_lock(inode); error = security_path_chown(path, uid, gid); if (!error) - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change(path->dentry, &newattrs, NULL, &delegated_inode); inode_unlock(inode); if (delegated_inode) { - error = break_deleg_wait(&delegated_inode); + error = break_deleg_wait(NULL, &delegated_inode); if (!error) goto retry_deleg; } diff --git a/fs/utimes.c b/fs/utimes.c index 6571d8c848a0..c7b53d3602ce 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -87,10 +87,10 @@ static int utimes_common(const struct path *path, struct timespec *times) } retry_deleg: inode_lock(inode); - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change(path->dentry, &newattrs, NULL, &delegated_inode); inode_unlock(inode); if (delegated_inode) { - error = break_deleg_wait(&delegated_inode); + error = break_deleg_wait(NULL, &delegated_inode); if (!error) goto retry_deleg; } diff --git a/include/linux/fs.h b/include/linux/fs.h index cc2e0f5a8fd1..6e434c677e4c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1084,7 +1084,7 @@ extern int vfs_test_lock(struct file *, struct file_lock *); extern int vfs_lock_file(struct file *, unsigned int, struct file_lock *, struct file_lock *); extern int vfs_cancel_lock(struct file *filp, struct file_lock *fl); extern int locks_lock_inode_wait(struct inode *inode, struct file_lock *fl); -extern int __break_lease(struct inode *inode, unsigned int flags, unsigned int type); +extern int __break_lease(struct inode *inode, fl_owner_t owner, unsigned int flags, unsigned int type); extern void lease_get_mtime(struct inode *, struct timespec *time); extern int generic_setlease(struct file *, long, struct file_lock **, void **priv); extern int vfs_setlease(struct file *, long, struct file_lock **, void **); @@ -1195,7 +1195,7 @@ static inline int locks_lock_inode_wait(struct inode *inode, struct file_lock *f return -ENOLCK; } -static inline int __break_lease(struct inode *inode, unsigned int mode, unsigned int type) +static inline int __break_lease(struct inode *inode, fl_owner_t owner, unsigned int mode, unsigned int type) { return 0; } @@ -1573,10 +1573,10 @@ extern int vfs_create(struct inode *, struct dentry *, umode_t, bool); extern int vfs_mkdir(struct inode *, struct dentry *, umode_t); extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t); extern int vfs_symlink(struct inode *, struct dentry *, const char *); -extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **); +extern int vfs_link(struct dentry *, struct inode *, struct dentry *, fl_owner_t, struct inode **); extern int vfs_rmdir(struct inode *, struct dentry *); -extern int vfs_unlink(struct inode *, struct dentry *, struct inode **); -extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int); +extern int vfs_unlink(struct inode *, struct dentry *, fl_owner_t, struct inode **); +extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, fl_owner_t, struct inode **, unsigned int); extern int vfs_whiteout(struct inode *, struct dentry *); extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, @@ -2260,11 +2260,11 @@ static inline int break_lease(struct inode *inode, unsigned int mode) */ smp_mb(); if (inode->i_flctx && !list_empty_careful(&inode->i_flctx->flc_lease)) - return __break_lease(inode, mode, FL_LEASE); + return __break_lease(inode, NULL, mode, FL_LEASE); return 0; } -static inline int break_deleg(struct inode *inode, unsigned int mode) +static inline int break_deleg(struct inode *inode, fl_owner_t owner, unsigned int mode) { /* * Since this check is lockless, we must ensure that any refcounts @@ -2274,15 +2274,15 @@ static inline int break_deleg(struct inode *inode, unsigned int mode) */ smp_mb(); if (inode->i_flctx && !list_empty_careful(&inode->i_flctx->flc_lease)) - return __break_lease(inode, mode, FL_DELEG); + return __break_lease(inode, owner, mode, FL_DELEG); return 0; } -static inline int try_break_deleg(struct inode *inode, struct inode **delegated_inode) +static inline int try_break_deleg(struct inode *inode, fl_owner_t owner, struct inode **delegated_inode) { int ret; - ret = break_deleg(inode, O_WRONLY|O_NONBLOCK); + ret = break_deleg(inode, owner, O_WRONLY|O_NONBLOCK); if (ret == -EWOULDBLOCK && delegated_inode) { *delegated_inode = inode; ihold(inode); @@ -2290,11 +2290,11 @@ static inline int try_break_deleg(struct inode *inode, struct inode **delegated_ return ret; } -static inline int break_deleg_wait(struct inode **delegated_inode) +static inline int break_deleg_wait(fl_owner_t owner, struct inode **delegated_inode) { int ret; - ret = break_deleg(*delegated_inode, O_WRONLY); + ret = break_deleg(*delegated_inode, owner, O_WRONLY); iput(*delegated_inode); *delegated_inode = NULL; return ret; @@ -2304,7 +2304,7 @@ static inline int break_layout(struct inode *inode, bool wait) { smp_mb(); if (inode->i_flctx && !list_empty_careful(&inode->i_flctx->flc_lease)) - return __break_lease(inode, + return __break_lease(inode, NULL, wait ? O_WRONLY : O_WRONLY | O_NONBLOCK, FL_LAYOUT); return 0; @@ -2316,17 +2316,17 @@ static inline int break_lease(struct inode *inode, unsigned int mode) return 0; } -static inline int break_deleg(struct inode *inode, unsigned int mode) +static inline int break_deleg(struct inode *inode, fl_owner_t owner, unsigned int mode) { return 0; } -static inline int try_break_deleg(struct inode *inode, struct inode **delegated_inode) +static inline int try_break_deleg(struct inode *inode, fl_owner_t owner, struct inode **delegated_inode) { return 0; } -static inline int break_deleg_wait(struct inode **delegated_inode) +static inline int break_deleg_wait(fl_owner_t owner, struct inode **delegated_inode) { BUG(); return 0; @@ -2643,7 +2643,7 @@ extern void emergency_remount(void); #ifdef CONFIG_BLOCK extern sector_t bmap(struct inode *, sector_t); #endif -extern int notify_change(struct dentry *, struct iattr *, struct inode **); +extern int notify_change(struct dentry *, struct iattr *, fl_owner_t, struct inode **); extern int inode_permission(struct inode *, int); extern int __inode_permission(struct inode *, int); extern int generic_permission(struct inode *, int);