diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-19 03:17:12 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-19 03:17:12 +0300 |
commit | 0bb73e42f027db64054fff4c3b3203c1626b9dc1 (patch) | |
tree | 2318d7db9d1cc315d51d8d263f66579aaf8c13d2 | |
parent | f60c55a94e1d127186566f06294f2dadd966e9b4 (diff) | |
parent | a0753c29004f4983e303abce019f29e183b1ee48 (diff) | |
download | linux-0bb73e42f027db64054fff4c3b3203c1626b9dc1.tar.xz |
Merge tag 'afs-next-20190915' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
Pull AFS updates from David Howells:
"Here's a set of patches for AFS. The first three are trivial, deleting
unused symbols and rolling out a wrapper function.
The fourth and fifth patches make use of the previously added RCU-safe
request_key facility to allow afs_permission() and afs_d_revalidate()
to attempt to operate without dropping out of RCU-mode pathwalk. Under
certain conditions, such as conflict with another client, we still
have to drop out anyway, take a lock and consult the server"
* tag 'afs-next-20190915' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
afs: Support RCU pathwalk
afs: Provide an RCU-capable key lookup
afs: Use afs_extract_discard() rather than iov_iter_discard()
afs: remove unused variable 'afs_zero_fid'
afs: remove unused variable 'afs_voltypes'
-rw-r--r-- | fs/afs/dir.c | 54 | ||||
-rw-r--r-- | fs/afs/fsclient.c | 6 | ||||
-rw-r--r-- | fs/afs/internal.h | 1 | ||||
-rw-r--r-- | fs/afs/security.c | 108 | ||||
-rw-r--r-- | fs/afs/volume.c | 2 | ||||
-rw-r--r-- | fs/afs/yfsclient.c | 6 | ||||
-rw-r--r-- | include/linux/key.h | 14 |
7 files changed, 162 insertions, 29 deletions
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 139b4e3cc946..cc12772d0a4d 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -966,6 +966,58 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, } /* + * Check the validity of a dentry under RCU conditions. + */ +static int afs_d_revalidate_rcu(struct dentry *dentry) +{ + struct afs_vnode *dvnode, *vnode; + struct dentry *parent; + struct inode *dir, *inode; + long dir_version, de_version; + + _enter("%p", dentry); + + /* Check the parent directory is still valid first. */ + parent = READ_ONCE(dentry->d_parent); + dir = d_inode_rcu(parent); + if (!dir) + return -ECHILD; + dvnode = AFS_FS_I(dir); + if (test_bit(AFS_VNODE_DELETED, &dvnode->flags)) + return -ECHILD; + + if (!afs_check_validity(dvnode)) + return -ECHILD; + + /* We only need to invalidate a dentry if the server's copy changed + * behind our back. If we made the change, it's no problem. Note that + * on a 32-bit system, we only have 32 bits in the dentry to store the + * version. + */ + dir_version = (long)READ_ONCE(dvnode->status.data_version); + de_version = (long)READ_ONCE(dentry->d_fsdata); + if (de_version != dir_version) { + dir_version = (long)READ_ONCE(dvnode->invalid_before); + if (de_version - dir_version < 0) + return -ECHILD; + } + + /* Check to see if the vnode referred to by the dentry still + * has a callback. + */ + if (d_really_is_positive(dentry)) { + inode = d_inode_rcu(dentry); + if (inode) { + vnode = AFS_FS_I(inode); + if (!afs_check_validity(vnode)) + return -ECHILD; + } + } + + return 1; /* Still valid */ +} + +/* * check that a dentry lookup hit has found a valid entry * - NOTE! the hit can be a negative hit too, so we can't assume we have an * inode @@ -982,7 +1034,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) int ret; if (flags & LOOKUP_RCU) - return -ECHILD; + return afs_d_revalidate_rcu(dentry); if (d_really_is_positive(dentry)) { vnode = AFS_FS_I(d_inode(dentry)); diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 114f281f3687..6f84231f11a5 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -15,8 +15,6 @@ #include "xdr_fs.h" #include "protocol_yfs.h" -static const struct afs_fid afs_zero_fid; - static inline void afs_use_fs_server(struct afs_call *call, struct afs_cb_interest *cbi) { call->cbi = afs_get_cb_interest(cbi); @@ -394,7 +392,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) goto no_more_data; /* Discard any excess data the server gave us */ - iov_iter_discard(&call->iter, READ, req->actual_len - req->len); + afs_extract_discard(call, req->actual_len - req->len); call->unmarshall = 3; /* Fall through */ @@ -1872,7 +1870,7 @@ static int afs_deliver_fs_get_capabilities(struct afs_call *call) call->count = count; call->count2 = count; - iov_iter_discard(&call->iter, READ, count * sizeof(__be32)); + afs_extract_discard(call, count * sizeof(__be32)); call->unmarshall++; /* Fall through */ diff --git a/fs/afs/internal.h b/fs/afs/internal.h index f66a3be12fd6..9cdfabaeaa0b 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1217,6 +1217,7 @@ extern void afs_cache_permit(struct afs_vnode *, struct key *, unsigned int, struct afs_status_cb *); extern void afs_zap_permits(struct rcu_head *); extern struct key *afs_request_key(struct afs_cell *); +extern struct key *afs_request_key_rcu(struct afs_cell *); extern int afs_check_permit(struct afs_vnode *, struct key *, afs_access_t *); extern int afs_permission(struct inode *, int); extern void __exit afs_clean_up_permit_cache(void); diff --git a/fs/afs/security.c b/fs/afs/security.c index 71e71c07568f..ce9de1e6742b 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c @@ -27,8 +27,37 @@ struct key *afs_request_key(struct afs_cell *cell) _enter("{%x}", key_serial(cell->anonymous_key)); _debug("key %s", cell->anonymous_key->description); - key = request_key(&key_type_rxrpc, cell->anonymous_key->description, - NULL); + key = request_key_net(&key_type_rxrpc, cell->anonymous_key->description, + cell->net->net, NULL); + if (IS_ERR(key)) { + if (PTR_ERR(key) != -ENOKEY) { + _leave(" = %ld", PTR_ERR(key)); + return key; + } + + /* act as anonymous user */ + _leave(" = {%x} [anon]", key_serial(cell->anonymous_key)); + return key_get(cell->anonymous_key); + } else { + /* act as authorised user */ + _leave(" = {%x} [auth]", key_serial(key)); + return key; + } +} + +/* + * Get a key when pathwalk is in rcuwalk mode. + */ +struct key *afs_request_key_rcu(struct afs_cell *cell) +{ + struct key *key; + + _enter("{%x}", key_serial(cell->anonymous_key)); + + _debug("key %s", cell->anonymous_key->description); + key = request_key_net_rcu(&key_type_rxrpc, + cell->anonymous_key->description, + cell->net->net); if (IS_ERR(key)) { if (PTR_ERR(key) != -ENOKEY) { _leave(" = %ld", PTR_ERR(key)); @@ -274,6 +303,40 @@ someone_else_changed_it: return; } +static bool afs_check_permit_rcu(struct afs_vnode *vnode, struct key *key, + afs_access_t *_access) +{ + const struct afs_permits *permits; + int i; + + _enter("{%llx:%llu},%x", + vnode->fid.vid, vnode->fid.vnode, key_serial(key)); + + /* check the permits to see if we've got one yet */ + if (key == vnode->volume->cell->anonymous_key) { + *_access = vnode->status.anon_access; + _leave(" = t [anon %x]", *_access); + return true; + } + + permits = rcu_dereference(vnode->permit_cache); + if (permits) { + for (i = 0; i < permits->nr_permits; i++) { + if (permits->permits[i].key < key) + continue; + if (permits->permits[i].key > key) + break; + + *_access = permits->permits[i].access; + _leave(" = %u [perm %x]", !permits->invalidated, *_access); + return !permits->invalidated; + } + } + + _leave(" = f"); + return false; +} + /* * check with the fileserver to see if the directory or parent directory is * permitted to be accessed with this authorisation, and if so, what access it @@ -340,33 +403,42 @@ int afs_permission(struct inode *inode, int mask) struct afs_vnode *vnode = AFS_FS_I(inode); afs_access_t uninitialized_var(access); struct key *key; - int ret; - - if (mask & MAY_NOT_BLOCK) - return -ECHILD; + int ret = 0; _enter("{{%llx:%llu},%lx},%x,", vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask); - key = afs_request_key(vnode->volume->cell); - if (IS_ERR(key)) { - _leave(" = %ld [key]", PTR_ERR(key)); - return PTR_ERR(key); - } + if (mask & MAY_NOT_BLOCK) { + key = afs_request_key_rcu(vnode->volume->cell); + if (IS_ERR(key)) + return -ECHILD; + + ret = -ECHILD; + if (!afs_check_validity(vnode) || + !afs_check_permit_rcu(vnode, key, &access)) + goto error; + } else { + key = afs_request_key(vnode->volume->cell); + if (IS_ERR(key)) { + _leave(" = %ld [key]", PTR_ERR(key)); + return PTR_ERR(key); + } - ret = afs_validate(vnode, key); - if (ret < 0) - goto error; + ret = afs_validate(vnode, key); + if (ret < 0) + goto error; - /* check the permits to see if we've got one yet */ - ret = afs_check_permit(vnode, key, &access); - if (ret < 0) - goto error; + /* check the permits to see if we've got one yet */ + ret = afs_check_permit(vnode, key, &access); + if (ret < 0) + goto error; + } /* interpret the access mask */ _debug("REQ %x ACC %x on %s", mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file"); + ret = 0; if (S_ISDIR(inode->i_mode)) { if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) { if (!(access & AFS_ACE_LOOKUP)) diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 1a414300b654..92ca5e27573b 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -12,8 +12,6 @@ unsigned __read_mostly afs_volume_gc_delay = 10; unsigned __read_mostly afs_volume_record_life = 60 * 60; -static const char *const afs_voltypes[] = { "R/W", "R/O", "BAK" }; - /* * Allocate a volume record and load it up from a vldb record. */ diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index ca2452806ebf..3ee7abf4b2d0 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c @@ -505,7 +505,7 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call) goto no_more_data; /* Discard any excess data the server gave us */ - iov_iter_discard(&call->iter, READ, req->actual_len - req->len); + afs_extract_discard(call, req->actual_len - req->len); call->unmarshall = 3; /* Fall through */ @@ -2007,7 +2007,7 @@ static int yfs_deliver_fs_fetch_opaque_acl(struct afs_call *call) acl->size = call->count2; afs_extract_begin(call, acl->data, size); } else { - iov_iter_discard(&call->iter, READ, size); + afs_extract_discard(call, size); } call->unmarshall++; /* Fall through */ @@ -2039,7 +2039,7 @@ static int yfs_deliver_fs_fetch_opaque_acl(struct afs_call *call) acl->size = call->count2; afs_extract_begin(call, acl->data, size); } else { - iov_iter_discard(&call->iter, READ, size); + afs_extract_discard(call, size); } call->unmarshall++; /* Fall through */ diff --git a/include/linux/key.h b/include/linux/key.h index 50028338a4cc..6cf8e71cf8b7 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -324,7 +324,7 @@ static inline struct key *request_key(struct key_type *type, } #ifdef CONFIG_NET -/* +/** * request_key_net - Request a key for a net namespace and wait for construction * @type: Type of key. * @description: The searchable description of the key. @@ -341,6 +341,18 @@ static inline struct key *request_key(struct key_type *type, */ #define request_key_net(type, description, net, callout_info) \ request_key_tag(type, description, net->key_domain, callout_info); + +/** + * request_key_net_rcu - Request a key for a net namespace under RCU conditions + * @type: Type of key. + * @description: The searchable description of the key. + * @net: The network namespace that is the key's domain of operation. + * + * As for request_key_rcu() except that only keys that operate the specified + * network namespace are used. + */ +#define request_key_net_rcu(type, description, net) \ + request_key_rcu(type, description, net->key_domain); #endif /* CONFIG_NET */ extern int wait_for_key_construction(struct key *key, bool intr); |