diff options
author | J. Bruce Fields <bfields@redhat.com> | 2011-09-23 16:21:15 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2011-09-26 17:35:26 -0400 |
commit | 6136d2b409652b064b2da6d43d5c47cbd1d2cc14 (patch) | |
tree | 08d43b181dfe90cc577550fd90b90af0add1bb5f | |
parent | 2a74aba799bfbc02977b69400b7bf4d2850aea79 (diff) |
nfsd4: use idr for stateid's
The idr system is designed exactly for generating id and looking up
integer id's. Thanks to Trond for pointing it out.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r-- | fs/nfsd/nfs4state.c | 124 | ||||
-rw-r--r-- | fs/nfsd/state.h | 1 |
2 files changed, 73 insertions, 52 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cb36c9a2e8ca..a9e71cdf4a84 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -32,6 +32,7 @@ | |||
32 | * | 32 | * |
33 | */ | 33 | */ |
34 | 34 | ||
35 | #include <linux/idr.h> | ||
35 | #include <linux/file.h> | 36 | #include <linux/file.h> |
36 | #include <linux/fs.h> | 37 | #include <linux/fs.h> |
37 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
@@ -49,7 +50,6 @@ | |||
49 | time_t nfsd4_lease = 90; /* default lease time */ | 50 | time_t nfsd4_lease = 90; /* default lease time */ |
50 | time_t nfsd4_grace = 90; | 51 | time_t nfsd4_grace = 90; |
51 | static time_t boot_time; | 52 | static time_t boot_time; |
52 | static u32 current_stateid = 1; | ||
53 | static stateid_t zerostateid; /* bits all 0 */ | 53 | static stateid_t zerostateid; /* bits all 0 */ |
54 | static stateid_t onestateid; /* bits all 1 */ | 54 | static stateid_t onestateid; /* bits all 1 */ |
55 | static u64 current_sessionid = 1; | 55 | static u64 current_sessionid = 1; |
@@ -149,10 +149,7 @@ static struct list_head open_ownerstr_hashtbl[OPEN_OWNER_HASH_SIZE]; | |||
149 | #define FILE_HASH_BITS 8 | 149 | #define FILE_HASH_BITS 8 |
150 | #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) | 150 | #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) |
151 | 151 | ||
152 | /* hash table for (open)nfs4_ol_stateid */ | 152 | struct idr stateids; |
153 | #define STATEID_HASH_BITS 10 | ||
154 | #define STATEID_HASH_SIZE (1 << STATEID_HASH_BITS) | ||
155 | #define STATEID_HASH_MASK (STATEID_HASH_SIZE - 1) | ||
156 | 153 | ||
157 | static unsigned int file_hashval(struct inode *ino) | 154 | static unsigned int file_hashval(struct inode *ino) |
158 | { | 155 | { |
@@ -160,13 +157,7 @@ static unsigned int file_hashval(struct inode *ino) | |||
160 | return hash_ptr(ino, FILE_HASH_BITS); | 157 | return hash_ptr(ino, FILE_HASH_BITS); |
161 | } | 158 | } |
162 | 159 | ||
163 | static unsigned int stateid_hashval(stateid_t *s) | ||
164 | { | ||
165 | return opaque_hashval(&s->si_opaque, sizeof(stateid_opaque_t)) & STATEID_HASH_MASK; | ||
166 | } | ||
167 | |||
168 | static struct list_head file_hashtbl[FILE_HASH_SIZE]; | 160 | static struct list_head file_hashtbl[FILE_HASH_SIZE]; |
169 | static struct list_head stateid_hashtbl[STATEID_HASH_SIZE]; | ||
170 | 161 | ||
171 | static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag) | 162 | static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag) |
172 | { | 163 | { |
@@ -215,26 +206,52 @@ static void nfs4_file_put_access(struct nfs4_file *fp, int oflag) | |||
215 | __nfs4_file_put_access(fp, oflag); | 206 | __nfs4_file_put_access(fp, oflag); |
216 | } | 207 | } |
217 | 208 | ||
218 | static inline void hash_stid(struct nfs4_stid *stid) | 209 | static inline int get_new_stid(struct nfs4_stid *stid) |
219 | { | 210 | { |
220 | stateid_t *s = &stid->sc_stateid; | 211 | static int min_stateid = 0; |
221 | unsigned int hashval; | 212 | int new_stid; |
213 | int error; | ||
214 | |||
215 | if (!idr_pre_get(&stateids, GFP_KERNEL)) | ||
216 | return -ENOMEM; | ||
217 | |||
218 | error = idr_get_new_above(&stateids, stid, min_stateid, &new_stid); | ||
219 | /* | ||
220 | * All this code is currently serialized; the preallocation | ||
221 | * above should still be ours: | ||
222 | */ | ||
223 | BUG_ON(error); | ||
224 | /* | ||
225 | * It shouldn't be a problem to reuse an opaque stateid value. | ||
226 | * I don't think it is for 4.1. But with 4.0 I worry that, for | ||
227 | * example, a stray write retransmission could be accepted by | ||
228 | * the server when it should have been rejected. Therefore, | ||
229 | * adopt a trick from the sctp code to attempt to maximize the | ||
230 | * amount of time until an id is reused, by ensuring they always | ||
231 | * "increase" (mod INT_MAX): | ||
232 | */ | ||
222 | 233 | ||
223 | hashval = stateid_hashval(s); | 234 | min_stateid = new_stid+1; |
224 | list_add(&stid->sc_hash, &stateid_hashtbl[hashval]); | 235 | if (min_stateid == INT_MAX) |
236 | min_stateid = 0; | ||
237 | return new_stid; | ||
225 | } | 238 | } |
226 | 239 | ||
227 | static void init_stid(struct nfs4_stid *stid, struct nfs4_client *cl, unsigned char type) | 240 | static inline __be32 init_stid(struct nfs4_stid *stid, struct nfs4_client *cl, unsigned char type) |
228 | { | 241 | { |
229 | stateid_t *s = &stid->sc_stateid; | 242 | stateid_t *s = &stid->sc_stateid; |
243 | int new_id; | ||
230 | 244 | ||
231 | stid->sc_type = type; | 245 | stid->sc_type = type; |
232 | stid->sc_client = cl; | 246 | stid->sc_client = cl; |
233 | s->si_opaque.so_clid = cl->cl_clientid; | 247 | s->si_opaque.so_clid = cl->cl_clientid; |
234 | s->si_opaque.so_id = current_stateid++; | 248 | new_id = get_new_stid(stid); |
249 | if (new_id < 0) | ||
250 | return nfserr_jukebox; | ||
251 | s->si_opaque.so_id = (u32)new_id; | ||
235 | /* Will be incremented before return to client: */ | 252 | /* Will be incremented before return to client: */ |
236 | s->si_generation = 0; | 253 | s->si_generation = 0; |
237 | hash_stid(stid); | 254 | return 0; |
238 | } | 255 | } |
239 | 256 | ||
240 | static struct nfs4_delegation * | 257 | static struct nfs4_delegation * |
@@ -242,6 +259,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv | |||
242 | { | 259 | { |
243 | struct nfs4_delegation *dp; | 260 | struct nfs4_delegation *dp; |
244 | struct nfs4_file *fp = stp->st_file; | 261 | struct nfs4_file *fp = stp->st_file; |
262 | __be32 status; | ||
245 | 263 | ||
246 | dprintk("NFSD alloc_init_deleg\n"); | 264 | dprintk("NFSD alloc_init_deleg\n"); |
247 | /* | 265 | /* |
@@ -258,11 +276,15 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv | |||
258 | dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); | 276 | dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); |
259 | if (dp == NULL) | 277 | if (dp == NULL) |
260 | return dp; | 278 | return dp; |
261 | init_stid(&dp->dl_stid, clp, NFS4_DELEG_STID); | 279 | status = init_stid(&dp->dl_stid, clp, NFS4_DELEG_STID); |
280 | if (status) { | ||
281 | kmem_cache_free(deleg_slab, dp); | ||
282 | return NULL; | ||
283 | } | ||
262 | /* | 284 | /* |
263 | * delegation seqid's are never incremented. The 4.1 special | 285 | * delegation seqid's are never incremented. The 4.1 special |
264 | * meaning of seqid 0 isn't really meaningful, really, but let's | 286 | * meaning of seqid 0 isn't meaningful, really, but let's avoid |
265 | * avoid 0 anyway just for consistency and use 1: | 287 | * 0 anyway just for consistency and use 1: |
266 | */ | 288 | */ |
267 | dp->dl_stid.sc_stateid.si_generation = 1; | 289 | dp->dl_stid.sc_stateid.si_generation = 1; |
268 | num_delegations++; | 290 | num_delegations++; |
@@ -300,11 +322,16 @@ static void nfs4_put_deleg_lease(struct nfs4_file *fp) | |||
300 | } | 322 | } |
301 | } | 323 | } |
302 | 324 | ||
325 | static void unhash_stid(struct nfs4_stid *s) | ||
326 | { | ||
327 | idr_remove(&stateids, s->sc_stateid.si_opaque.so_id); | ||
328 | } | ||
329 | |||
303 | /* Called under the state lock. */ | 330 | /* Called under the state lock. */ |
304 | static void | 331 | static void |
305 | unhash_delegation(struct nfs4_delegation *dp) | 332 | unhash_delegation(struct nfs4_delegation *dp) |
306 | { | 333 | { |
307 | list_del_init(&dp->dl_stid.sc_hash); | 334 | unhash_stid(&dp->dl_stid); |
308 | list_del_init(&dp->dl_perclnt); | 335 | list_del_init(&dp->dl_perclnt); |
309 | spin_lock(&recall_lock); | 336 | spin_lock(&recall_lock); |
310 | list_del_init(&dp->dl_perfile); | 337 | list_del_init(&dp->dl_perfile); |
@@ -457,7 +484,7 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp) | |||
457 | struct file *file; | 484 | struct file *file; |
458 | 485 | ||
459 | unhash_generic_stateid(stp); | 486 | unhash_generic_stateid(stp); |
460 | list_del(&stp->st_stid.sc_hash); | 487 | unhash_stid(&stp->st_stid); |
461 | file = find_any_file(stp->st_file); | 488 | file = find_any_file(stp->st_file); |
462 | if (file) | 489 | if (file) |
463 | locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner)); | 490 | locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner)); |
@@ -506,7 +533,7 @@ static void unhash_open_stateid(struct nfs4_ol_stateid *stp) | |||
506 | static void release_open_stateid(struct nfs4_ol_stateid *stp) | 533 | static void release_open_stateid(struct nfs4_ol_stateid *stp) |
507 | { | 534 | { |
508 | unhash_open_stateid(stp); | 535 | unhash_open_stateid(stp); |
509 | list_del(&stp->st_stid.sc_hash); | 536 | unhash_stid(&stp->st_stid); |
510 | free_generic_stateid(stp); | 537 | free_generic_stateid(stp); |
511 | } | 538 | } |
512 | 539 | ||
@@ -528,7 +555,7 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo) | |||
528 | struct nfs4_ol_stateid *s = oo->oo_last_closed_stid; | 555 | struct nfs4_ol_stateid *s = oo->oo_last_closed_stid; |
529 | 556 | ||
530 | if (s) { | 557 | if (s) { |
531 | list_del_init(&s->st_stid.sc_hash); | 558 | unhash_stid(&s->st_stid); |
532 | free_generic_stateid(s); | 559 | free_generic_stateid(s); |
533 | oo->oo_last_closed_stid = NULL; | 560 | oo->oo_last_closed_stid = NULL; |
534 | } | 561 | } |
@@ -1099,23 +1126,9 @@ static void gen_confirm(struct nfs4_client *clp) | |||
1099 | *p++ = i++; | 1126 | *p++ = i++; |
1100 | } | 1127 | } |
1101 | 1128 | ||
1102 | static int | ||
1103 | same_stateid(stateid_t *id_one, stateid_t *id_two) | ||
1104 | { | ||
1105 | return 0 == memcmp(&id_one->si_opaque, &id_two->si_opaque, | ||
1106 | sizeof(stateid_opaque_t)); | ||
1107 | } | ||
1108 | |||
1109 | static struct nfs4_stid *find_stateid(stateid_t *t) | 1129 | static struct nfs4_stid *find_stateid(stateid_t *t) |
1110 | { | 1130 | { |
1111 | struct nfs4_stid *s; | 1131 | return idr_find(&stateids, t->si_opaque.so_id); |
1112 | unsigned int hashval; | ||
1113 | |||
1114 | hashval = stateid_hashval(t); | ||
1115 | list_for_each_entry(s, &stateid_hashtbl[hashval], sc_hash) | ||
1116 | if (same_stateid(&s->sc_stateid, t)) | ||
1117 | return s; | ||
1118 | return NULL; | ||
1119 | } | 1132 | } |
1120 | 1133 | ||
1121 | static struct nfs4_stid *find_stateid_by_type(stateid_t *t, char typemask) | 1134 | static struct nfs4_stid *find_stateid_by_type(stateid_t *t, char typemask) |
@@ -2342,12 +2355,14 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str | |||
2342 | return oo; | 2355 | return oo; |
2343 | } | 2356 | } |
2344 | 2357 | ||
2345 | static inline void | 2358 | static inline __be32 init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { |
2346 | init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { | ||
2347 | struct nfs4_openowner *oo = open->op_openowner; | 2359 | struct nfs4_openowner *oo = open->op_openowner; |
2348 | struct nfs4_client *clp = oo->oo_owner.so_client; | 2360 | struct nfs4_client *clp = oo->oo_owner.so_client; |
2361 | __be32 status; | ||
2349 | 2362 | ||
2350 | init_stid(&stp->st_stid, clp, NFS4_OPEN_STID); | 2363 | status = init_stid(&stp->st_stid, clp, NFS4_OPEN_STID); |
2364 | if (status) | ||
2365 | return status; | ||
2351 | INIT_LIST_HEAD(&stp->st_lockowners); | 2366 | INIT_LIST_HEAD(&stp->st_lockowners); |
2352 | list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); | 2367 | list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); |
2353 | list_add(&stp->st_perfile, &fp->fi_stateids); | 2368 | list_add(&stp->st_perfile, &fp->fi_stateids); |
@@ -2360,6 +2375,7 @@ init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd | |||
2360 | &stp->st_access_bmap); | 2375 | &stp->st_access_bmap); |
2361 | __set_bit(open->op_share_deny, &stp->st_deny_bmap); | 2376 | __set_bit(open->op_share_deny, &stp->st_deny_bmap); |
2362 | stp->st_openstp = NULL; | 2377 | stp->st_openstp = NULL; |
2378 | return nfs_ok; | ||
2363 | } | 2379 | } |
2364 | 2380 | ||
2365 | static void | 2381 | static void |
@@ -2949,7 +2965,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf | |||
2949 | status = nfs4_new_open(rqstp, &stp, fp, current_fh, open); | 2965 | status = nfs4_new_open(rqstp, &stp, fp, current_fh, open); |
2950 | if (status) | 2966 | if (status) |
2951 | goto out; | 2967 | goto out; |
2952 | init_open_stateid(stp, fp, open); | 2968 | status = init_open_stateid(stp, fp, open); |
2969 | if (status) { | ||
2970 | release_open_stateid(stp); | ||
2971 | goto out; | ||
2972 | } | ||
2953 | status = nfsd4_truncate(rqstp, current_fh, open); | 2973 | status = nfsd4_truncate(rqstp, current_fh, open); |
2954 | if (status) { | 2974 | if (status) { |
2955 | release_open_stateid(stp); | 2975 | release_open_stateid(stp); |
@@ -3824,11 +3844,16 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct | |||
3824 | { | 3844 | { |
3825 | struct nfs4_ol_stateid *stp; | 3845 | struct nfs4_ol_stateid *stp; |
3826 | struct nfs4_client *clp = lo->lo_owner.so_client; | 3846 | struct nfs4_client *clp = lo->lo_owner.so_client; |
3847 | __be32 status; | ||
3827 | 3848 | ||
3828 | stp = nfs4_alloc_stateid(); | 3849 | stp = nfs4_alloc_stateid(); |
3829 | if (stp == NULL) | 3850 | if (stp == NULL) |
3830 | goto out; | 3851 | return NULL; |
3831 | init_stid(&stp->st_stid, clp, NFS4_LOCK_STID); | 3852 | status = init_stid(&stp->st_stid, clp, NFS4_LOCK_STID); |
3853 | if (status) { | ||
3854 | free_generic_stateid(stp); | ||
3855 | return NULL; | ||
3856 | } | ||
3832 | list_add(&stp->st_perfile, &fp->fi_stateids); | 3857 | list_add(&stp->st_perfile, &fp->fi_stateids); |
3833 | list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); | 3858 | list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); |
3834 | stp->st_stateowner = &lo->lo_owner; | 3859 | stp->st_stateowner = &lo->lo_owner; |
@@ -3837,8 +3862,6 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct | |||
3837 | stp->st_access_bmap = 0; | 3862 | stp->st_access_bmap = 0; |
3838 | stp->st_deny_bmap = open_stp->st_deny_bmap; | 3863 | stp->st_deny_bmap = open_stp->st_deny_bmap; |
3839 | stp->st_openstp = open_stp; | 3864 | stp->st_openstp = open_stp; |
3840 | |||
3841 | out: | ||
3842 | return stp; | 3865 | return stp; |
3843 | } | 3866 | } |
3844 | 3867 | ||
@@ -4386,8 +4409,7 @@ nfs4_state_init(void) | |||
4386 | for (i = 0; i < OPEN_OWNER_HASH_SIZE; i++) { | 4409 | for (i = 0; i < OPEN_OWNER_HASH_SIZE; i++) { |
4387 | INIT_LIST_HEAD(&open_ownerstr_hashtbl[i]); | 4410 | INIT_LIST_HEAD(&open_ownerstr_hashtbl[i]); |
4388 | } | 4411 | } |
4389 | for (i = 0; i < STATEID_HASH_SIZE; i++) | 4412 | idr_init(&stateids); |
4390 | INIT_LIST_HEAD(&stateid_hashtbl[i]); | ||
4391 | for (i = 0; i < LOCK_HASH_SIZE; i++) { | 4413 | for (i = 0; i < LOCK_HASH_SIZE; i++) { |
4392 | INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); | 4414 | INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); |
4393 | } | 4415 | } |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 70062b75e24a..3ed5f99141ec 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -79,7 +79,6 @@ struct nfs4_stid { | |||
79 | /* For an open stateid kept around *only* to process close replays: */ | 79 | /* For an open stateid kept around *only* to process close replays: */ |
80 | #define NFS4_CLOSED_STID 8 | 80 | #define NFS4_CLOSED_STID 8 |
81 | unsigned char sc_type; | 81 | unsigned char sc_type; |
82 | struct list_head sc_hash; | ||
83 | stateid_t sc_stateid; | 82 | stateid_t sc_stateid; |
84 | struct nfs4_client *sc_client; | 83 | struct nfs4_client *sc_client; |
85 | }; | 84 | }; |