diff options
author | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-07-29 21:34:34 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2014-07-31 14:20:24 -0400 |
commit | 7ffb588086e941aa0a46a33e2bf2bf3c0963ed98 (patch) | |
tree | ea15185306219711d7e5d7b7546ac1d03bb61a1f /fs/nfsd/nfs4state.c | |
parent | b401be22b5cf059290ee98106bc780e087407d45 (diff) |
nfsd: Protect adding/removing open state owners using client_lock
Once we remove client mutex protection, we'll need to ensure that
stateowner lookup and creation are atomic between concurrent compounds.
Ensure that alloc_init_open_stateowner checks the hashtable under the
client_lock before adding a new element.
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/nfs4state.c')
-rw-r--r-- | fs/nfsd/nfs4state.c | 118 |
1 files changed, 80 insertions, 38 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6d26d26751f5..c4bb7f2b29d9 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -239,6 +239,53 @@ static void nfsd4_put_session(struct nfsd4_session *ses) | |||
239 | spin_unlock(&nn->client_lock); | 239 | spin_unlock(&nn->client_lock); |
240 | } | 240 | } |
241 | 241 | ||
242 | static int | ||
243 | same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, | ||
244 | clientid_t *clid) | ||
245 | { | ||
246 | return (sop->so_owner.len == owner->len) && | ||
247 | 0 == memcmp(sop->so_owner.data, owner->data, owner->len) && | ||
248 | (sop->so_client->cl_clientid.cl_id == clid->cl_id); | ||
249 | } | ||
250 | |||
251 | static struct nfs4_openowner * | ||
252 | find_openstateowner_str_locked(unsigned int hashval, struct nfsd4_open *open, | ||
253 | bool sessions, struct nfsd_net *nn) | ||
254 | { | ||
255 | struct nfs4_stateowner *so; | ||
256 | struct nfs4_openowner *oo; | ||
257 | struct nfs4_client *clp; | ||
258 | |||
259 | lockdep_assert_held(&nn->client_lock); | ||
260 | |||
261 | list_for_each_entry(so, &nn->ownerstr_hashtbl[hashval], so_strhash) { | ||
262 | if (!so->so_is_open_owner) | ||
263 | continue; | ||
264 | if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { | ||
265 | oo = openowner(so); | ||
266 | clp = oo->oo_owner.so_client; | ||
267 | if ((bool)clp->cl_minorversion != sessions) | ||
268 | break; | ||
269 | renew_client_locked(clp); | ||
270 | atomic_inc(&so->so_count); | ||
271 | return oo; | ||
272 | } | ||
273 | } | ||
274 | return NULL; | ||
275 | } | ||
276 | |||
277 | static struct nfs4_openowner * | ||
278 | find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, | ||
279 | bool sessions, struct nfsd_net *nn) | ||
280 | { | ||
281 | struct nfs4_openowner *oo; | ||
282 | |||
283 | spin_lock(&nn->client_lock); | ||
284 | oo = find_openstateowner_str_locked(hashval, open, sessions, nn); | ||
285 | spin_unlock(&nn->client_lock); | ||
286 | return oo; | ||
287 | } | ||
288 | |||
242 | 289 | ||
243 | static inline u32 | 290 | static inline u32 |
244 | opaque_hashval(const void *ptr, int nbytes) | 291 | opaque_hashval(const void *ptr, int nbytes) |
@@ -1005,8 +1052,13 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp) | |||
1005 | nfs4_put_stid(&stp->st_stid); | 1052 | nfs4_put_stid(&stp->st_stid); |
1006 | } | 1053 | } |
1007 | 1054 | ||
1008 | static void unhash_openowner(struct nfs4_openowner *oo) | 1055 | static void unhash_openowner_locked(struct nfs4_openowner *oo) |
1009 | { | 1056 | { |
1057 | struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net, | ||
1058 | nfsd_net_id); | ||
1059 | |||
1060 | lockdep_assert_held(&nn->client_lock); | ||
1061 | |||
1010 | list_del_init(&oo->oo_owner.so_strhash); | 1062 | list_del_init(&oo->oo_owner.so_strhash); |
1011 | list_del_init(&oo->oo_perclient); | 1063 | list_del_init(&oo->oo_perclient); |
1012 | } | 1064 | } |
@@ -1025,18 +1077,29 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo) | |||
1025 | static void release_openowner_stateids(struct nfs4_openowner *oo) | 1077 | static void release_openowner_stateids(struct nfs4_openowner *oo) |
1026 | { | 1078 | { |
1027 | struct nfs4_ol_stateid *stp; | 1079 | struct nfs4_ol_stateid *stp; |
1080 | struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net, | ||
1081 | nfsd_net_id); | ||
1082 | |||
1083 | lockdep_assert_held(&nn->client_lock); | ||
1028 | 1084 | ||
1029 | while (!list_empty(&oo->oo_owner.so_stateids)) { | 1085 | while (!list_empty(&oo->oo_owner.so_stateids)) { |
1030 | stp = list_first_entry(&oo->oo_owner.so_stateids, | 1086 | stp = list_first_entry(&oo->oo_owner.so_stateids, |
1031 | struct nfs4_ol_stateid, st_perstateowner); | 1087 | struct nfs4_ol_stateid, st_perstateowner); |
1088 | spin_unlock(&nn->client_lock); | ||
1032 | release_open_stateid(stp); | 1089 | release_open_stateid(stp); |
1090 | spin_lock(&nn->client_lock); | ||
1033 | } | 1091 | } |
1034 | } | 1092 | } |
1035 | 1093 | ||
1036 | static void release_openowner(struct nfs4_openowner *oo) | 1094 | static void release_openowner(struct nfs4_openowner *oo) |
1037 | { | 1095 | { |
1038 | unhash_openowner(oo); | 1096 | struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net, |
1097 | nfsd_net_id); | ||
1098 | |||
1099 | spin_lock(&nn->client_lock); | ||
1100 | unhash_openowner_locked(oo); | ||
1039 | release_openowner_stateids(oo); | 1101 | release_openowner_stateids(oo); |
1102 | spin_unlock(&nn->client_lock); | ||
1040 | release_last_closed_stateid(oo); | 1103 | release_last_closed_stateid(oo); |
1041 | nfs4_put_stateowner(&oo->oo_owner); | 1104 | nfs4_put_stateowner(&oo->oo_owner); |
1042 | } | 1105 | } |
@@ -3004,8 +3067,11 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, u | |||
3004 | static void nfs4_unhash_openowner(struct nfs4_stateowner *so) | 3067 | static void nfs4_unhash_openowner(struct nfs4_stateowner *so) |
3005 | { | 3068 | { |
3006 | struct nfs4_openowner *oo = openowner(so); | 3069 | struct nfs4_openowner *oo = openowner(so); |
3070 | struct nfsd_net *nn = net_generic(so->so_client->net, nfsd_net_id); | ||
3007 | 3071 | ||
3008 | unhash_openowner(oo); | 3072 | spin_lock(&nn->client_lock); |
3073 | unhash_openowner_locked(oo); | ||
3074 | spin_unlock(&nn->client_lock); | ||
3009 | } | 3075 | } |
3010 | 3076 | ||
3011 | static void nfs4_free_openowner(struct nfs4_stateowner *so) | 3077 | static void nfs4_free_openowner(struct nfs4_stateowner *so) |
@@ -3025,7 +3091,8 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, | |||
3025 | struct nfsd4_compound_state *cstate) | 3091 | struct nfsd4_compound_state *cstate) |
3026 | { | 3092 | { |
3027 | struct nfs4_client *clp = cstate->clp; | 3093 | struct nfs4_client *clp = cstate->clp; |
3028 | struct nfs4_openowner *oo; | 3094 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
3095 | struct nfs4_openowner *oo, *ret; | ||
3029 | 3096 | ||
3030 | oo = alloc_stateowner(openowner_slab, &open->op_owner, clp); | 3097 | oo = alloc_stateowner(openowner_slab, &open->op_owner, clp); |
3031 | if (!oo) | 3098 | if (!oo) |
@@ -3039,7 +3106,15 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, | |||
3039 | oo->oo_time = 0; | 3106 | oo->oo_time = 0; |
3040 | oo->oo_last_closed_stid = NULL; | 3107 | oo->oo_last_closed_stid = NULL; |
3041 | INIT_LIST_HEAD(&oo->oo_close_lru); | 3108 | INIT_LIST_HEAD(&oo->oo_close_lru); |
3042 | hash_openowner(oo, clp, strhashval); | 3109 | spin_lock(&nn->client_lock); |
3110 | ret = find_openstateowner_str_locked(strhashval, | ||
3111 | open, clp->cl_minorversion, nn); | ||
3112 | if (ret == NULL) { | ||
3113 | hash_openowner(oo, clp, strhashval); | ||
3114 | ret = oo; | ||
3115 | } else | ||
3116 | nfs4_free_openowner(&oo->oo_owner); | ||
3117 | spin_unlock(&nn->client_lock); | ||
3043 | return oo; | 3118 | return oo; |
3044 | } | 3119 | } |
3045 | 3120 | ||
@@ -3100,39 +3175,6 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net) | |||
3100 | oo->oo_time = get_seconds(); | 3175 | oo->oo_time = get_seconds(); |
3101 | } | 3176 | } |
3102 | 3177 | ||
3103 | static int | ||
3104 | same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, | ||
3105 | clientid_t *clid) | ||
3106 | { | ||
3107 | return (sop->so_owner.len == owner->len) && | ||
3108 | 0 == memcmp(sop->so_owner.data, owner->data, owner->len) && | ||
3109 | (sop->so_client->cl_clientid.cl_id == clid->cl_id); | ||
3110 | } | ||
3111 | |||
3112 | static struct nfs4_openowner * | ||
3113 | find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, | ||
3114 | bool sessions, struct nfsd_net *nn) | ||
3115 | { | ||
3116 | struct nfs4_stateowner *so; | ||
3117 | struct nfs4_openowner *oo; | ||
3118 | struct nfs4_client *clp; | ||
3119 | |||
3120 | list_for_each_entry(so, &nn->ownerstr_hashtbl[hashval], so_strhash) { | ||
3121 | if (!so->so_is_open_owner) | ||
3122 | continue; | ||
3123 | if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { | ||
3124 | oo = openowner(so); | ||
3125 | clp = oo->oo_owner.so_client; | ||
3126 | if ((bool)clp->cl_minorversion != sessions) | ||
3127 | return NULL; | ||
3128 | renew_client(oo->oo_owner.so_client); | ||
3129 | atomic_inc(&oo->oo_owner.so_count); | ||
3130 | return oo; | ||
3131 | } | ||
3132 | } | ||
3133 | return NULL; | ||
3134 | } | ||
3135 | |||
3136 | /* search file_hashtbl[] for file */ | 3178 | /* search file_hashtbl[] for file */ |
3137 | static struct nfs4_file * | 3179 | static struct nfs4_file * |
3138 | find_file_locked(struct knfsd_fh *fh) | 3180 | find_file_locked(struct knfsd_fh *fh) |