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 | }; |
