diff options
Diffstat (limited to 'fs/nfsd/nfs4state.c')
-rw-r--r-- | fs/nfsd/nfs4state.c | 1028 |
1 files changed, 545 insertions, 483 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 75e8b137580c..89e36526d7f2 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -48,39 +48,32 @@ | |||
48 | #include <linux/nfs4.h> | 48 | #include <linux/nfs4.h> |
49 | #include <linux/nfsd/state.h> | 49 | #include <linux/nfsd/state.h> |
50 | #include <linux/nfsd/xdr4.h> | 50 | #include <linux/nfsd/xdr4.h> |
51 | #include <linux/namei.h> | ||
51 | 52 | ||
52 | #define NFSDDBG_FACILITY NFSDDBG_PROC | 53 | #define NFSDDBG_FACILITY NFSDDBG_PROC |
53 | 54 | ||
54 | /* Globals */ | 55 | /* Globals */ |
55 | static time_t lease_time = 90; /* default lease time */ | 56 | static time_t lease_time = 90; /* default lease time */ |
56 | static time_t old_lease_time = 90; /* past incarnation lease time */ | 57 | static time_t user_lease_time = 90; |
57 | static u32 nfs4_reclaim_init = 0; | 58 | static time_t boot_time; |
58 | time_t boot_time; | 59 | static int in_grace = 1; |
59 | static time_t grace_end = 0; | ||
60 | static u32 current_clientid = 1; | 60 | static u32 current_clientid = 1; |
61 | static u32 current_ownerid = 1; | 61 | static u32 current_ownerid = 1; |
62 | static u32 current_fileid = 1; | 62 | static u32 current_fileid = 1; |
63 | static u32 current_delegid = 1; | 63 | static u32 current_delegid = 1; |
64 | static u32 nfs4_init; | 64 | static u32 nfs4_init; |
65 | stateid_t zerostateid; /* bits all 0 */ | 65 | static stateid_t zerostateid; /* bits all 0 */ |
66 | stateid_t onestateid; /* bits all 1 */ | 66 | static stateid_t onestateid; /* bits all 1 */ |
67 | 67 | ||
68 | /* debug counters */ | 68 | #define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t))) |
69 | u32 list_add_perfile = 0; | 69 | #define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) |
70 | u32 list_del_perfile = 0; | ||
71 | u32 add_perclient = 0; | ||
72 | u32 del_perclient = 0; | ||
73 | u32 alloc_file = 0; | ||
74 | u32 free_file = 0; | ||
75 | u32 vfsopen = 0; | ||
76 | u32 vfsclose = 0; | ||
77 | u32 alloc_delegation= 0; | ||
78 | u32 free_delegation= 0; | ||
79 | 70 | ||
80 | /* forward declarations */ | 71 | /* forward declarations */ |
81 | struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); | 72 | static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); |
82 | static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); | 73 | static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); |
83 | static void release_stateid_lockowners(struct nfs4_stateid *open_stp); | 74 | static void release_stateid_lockowners(struct nfs4_stateid *open_stp); |
75 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; | ||
76 | static void nfs4_set_recdir(char *recdir); | ||
84 | 77 | ||
85 | /* Locking: | 78 | /* Locking: |
86 | * | 79 | * |
@@ -90,6 +83,11 @@ static void release_stateid_lockowners(struct nfs4_stateid *open_stp); | |||
90 | */ | 83 | */ |
91 | static DECLARE_MUTEX(client_sema); | 84 | static DECLARE_MUTEX(client_sema); |
92 | 85 | ||
86 | static kmem_cache_t *stateowner_slab = NULL; | ||
87 | static kmem_cache_t *file_slab = NULL; | ||
88 | static kmem_cache_t *stateid_slab = NULL; | ||
89 | static kmem_cache_t *deleg_slab = NULL; | ||
90 | |||
93 | void | 91 | void |
94 | nfs4_lock_state(void) | 92 | nfs4_lock_state(void) |
95 | { | 93 | { |
@@ -118,16 +116,36 @@ opaque_hashval(const void *ptr, int nbytes) | |||
118 | /* forward declarations */ | 116 | /* forward declarations */ |
119 | static void release_stateowner(struct nfs4_stateowner *sop); | 117 | static void release_stateowner(struct nfs4_stateowner *sop); |
120 | static void release_stateid(struct nfs4_stateid *stp, int flags); | 118 | static void release_stateid(struct nfs4_stateid *stp, int flags); |
121 | static void release_file(struct nfs4_file *fp); | ||
122 | 119 | ||
123 | /* | 120 | /* |
124 | * Delegation state | 121 | * Delegation state |
125 | */ | 122 | */ |
126 | 123 | ||
127 | /* recall_lock protects the del_recall_lru */ | 124 | /* recall_lock protects the del_recall_lru */ |
128 | spinlock_t recall_lock; | 125 | static spinlock_t recall_lock = SPIN_LOCK_UNLOCKED; |
129 | static struct list_head del_recall_lru; | 126 | static struct list_head del_recall_lru; |
130 | 127 | ||
128 | static void | ||
129 | free_nfs4_file(struct kref *kref) | ||
130 | { | ||
131 | struct nfs4_file *fp = container_of(kref, struct nfs4_file, fi_ref); | ||
132 | list_del(&fp->fi_hash); | ||
133 | iput(fp->fi_inode); | ||
134 | kmem_cache_free(file_slab, fp); | ||
135 | } | ||
136 | |||
137 | static inline void | ||
138 | put_nfs4_file(struct nfs4_file *fi) | ||
139 | { | ||
140 | kref_put(&fi->fi_ref, free_nfs4_file); | ||
141 | } | ||
142 | |||
143 | static inline void | ||
144 | get_nfs4_file(struct nfs4_file *fi) | ||
145 | { | ||
146 | kref_get(&fi->fi_ref); | ||
147 | } | ||
148 | |||
131 | static struct nfs4_delegation * | 149 | static struct nfs4_delegation * |
132 | alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_fh *current_fh, u32 type) | 150 | alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_fh *current_fh, u32 type) |
133 | { | 151 | { |
@@ -136,13 +154,14 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f | |||
136 | struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback; | 154 | struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback; |
137 | 155 | ||
138 | dprintk("NFSD alloc_init_deleg\n"); | 156 | dprintk("NFSD alloc_init_deleg\n"); |
139 | if ((dp = kmalloc(sizeof(struct nfs4_delegation), | 157 | dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); |
140 | GFP_KERNEL)) == NULL) | 158 | if (dp == NULL) |
141 | return dp; | 159 | return dp; |
142 | INIT_LIST_HEAD(&dp->dl_del_perfile); | 160 | INIT_LIST_HEAD(&dp->dl_perfile); |
143 | INIT_LIST_HEAD(&dp->dl_del_perclnt); | 161 | INIT_LIST_HEAD(&dp->dl_perclnt); |
144 | INIT_LIST_HEAD(&dp->dl_recall_lru); | 162 | INIT_LIST_HEAD(&dp->dl_recall_lru); |
145 | dp->dl_client = clp; | 163 | dp->dl_client = clp; |
164 | get_nfs4_file(fp); | ||
146 | dp->dl_file = fp; | 165 | dp->dl_file = fp; |
147 | dp->dl_flock = NULL; | 166 | dp->dl_flock = NULL; |
148 | get_file(stp->st_vfs_file); | 167 | get_file(stp->st_vfs_file); |
@@ -160,9 +179,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f | |||
160 | current_fh->fh_handle.fh_size); | 179 | current_fh->fh_handle.fh_size); |
161 | dp->dl_time = 0; | 180 | dp->dl_time = 0; |
162 | atomic_set(&dp->dl_count, 1); | 181 | atomic_set(&dp->dl_count, 1); |
163 | list_add(&dp->dl_del_perfile, &fp->fi_del_perfile); | 182 | list_add(&dp->dl_perfile, &fp->fi_delegations); |
164 | list_add(&dp->dl_del_perclnt, &clp->cl_del_perclnt); | 183 | list_add(&dp->dl_perclnt, &clp->cl_delegations); |
165 | alloc_delegation++; | ||
166 | return dp; | 184 | return dp; |
167 | } | 185 | } |
168 | 186 | ||
@@ -171,8 +189,8 @@ nfs4_put_delegation(struct nfs4_delegation *dp) | |||
171 | { | 189 | { |
172 | if (atomic_dec_and_test(&dp->dl_count)) { | 190 | if (atomic_dec_and_test(&dp->dl_count)) { |
173 | dprintk("NFSD: freeing dp %p\n",dp); | 191 | dprintk("NFSD: freeing dp %p\n",dp); |
174 | kfree(dp); | 192 | put_nfs4_file(dp->dl_file); |
175 | free_delegation++; | 193 | kmem_cache_free(deleg_slab, dp); |
176 | } | 194 | } |
177 | } | 195 | } |
178 | 196 | ||
@@ -193,15 +211,14 @@ nfs4_close_delegation(struct nfs4_delegation *dp) | |||
193 | if (dp->dl_flock) | 211 | if (dp->dl_flock) |
194 | setlease(filp, F_UNLCK, &dp->dl_flock); | 212 | setlease(filp, F_UNLCK, &dp->dl_flock); |
195 | nfsd_close(filp); | 213 | nfsd_close(filp); |
196 | vfsclose++; | ||
197 | } | 214 | } |
198 | 215 | ||
199 | /* Called under the state lock. */ | 216 | /* Called under the state lock. */ |
200 | static void | 217 | static void |
201 | unhash_delegation(struct nfs4_delegation *dp) | 218 | unhash_delegation(struct nfs4_delegation *dp) |
202 | { | 219 | { |
203 | list_del_init(&dp->dl_del_perfile); | 220 | list_del_init(&dp->dl_perfile); |
204 | list_del_init(&dp->dl_del_perclnt); | 221 | list_del_init(&dp->dl_perclnt); |
205 | spin_lock(&recall_lock); | 222 | spin_lock(&recall_lock); |
206 | list_del_init(&dp->dl_recall_lru); | 223 | list_del_init(&dp->dl_recall_lru); |
207 | spin_unlock(&recall_lock); | 224 | spin_unlock(&recall_lock); |
@@ -220,8 +237,8 @@ unhash_delegation(struct nfs4_delegation *dp) | |||
220 | 237 | ||
221 | #define clientid_hashval(id) \ | 238 | #define clientid_hashval(id) \ |
222 | ((id) & CLIENT_HASH_MASK) | 239 | ((id) & CLIENT_HASH_MASK) |
223 | #define clientstr_hashval(name, namelen) \ | 240 | #define clientstr_hashval(name) \ |
224 | (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK) | 241 | (opaque_hashval((name), 8) & CLIENT_HASH_MASK) |
225 | /* | 242 | /* |
226 | * reclaim_str_hashtbl[] holds known client info from previous reset/reboot | 243 | * reclaim_str_hashtbl[] holds known client info from previous reset/reboot |
227 | * used in reboot/reset lease grace period processing | 244 | * used in reboot/reset lease grace period processing |
@@ -331,11 +348,11 @@ expire_client(struct nfs4_client *clp) | |||
331 | 348 | ||
332 | INIT_LIST_HEAD(&reaplist); | 349 | INIT_LIST_HEAD(&reaplist); |
333 | spin_lock(&recall_lock); | 350 | spin_lock(&recall_lock); |
334 | while (!list_empty(&clp->cl_del_perclnt)) { | 351 | while (!list_empty(&clp->cl_delegations)) { |
335 | dp = list_entry(clp->cl_del_perclnt.next, struct nfs4_delegation, dl_del_perclnt); | 352 | dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt); |
336 | dprintk("NFSD: expire client. dp %p, fp %p\n", dp, | 353 | dprintk("NFSD: expire client. dp %p, fp %p\n", dp, |
337 | dp->dl_flock); | 354 | dp->dl_flock); |
338 | list_del_init(&dp->dl_del_perclnt); | 355 | list_del_init(&dp->dl_perclnt); |
339 | list_move(&dp->dl_recall_lru, &reaplist); | 356 | list_move(&dp->dl_recall_lru, &reaplist); |
340 | } | 357 | } |
341 | spin_unlock(&recall_lock); | 358 | spin_unlock(&recall_lock); |
@@ -347,26 +364,26 @@ expire_client(struct nfs4_client *clp) | |||
347 | list_del(&clp->cl_idhash); | 364 | list_del(&clp->cl_idhash); |
348 | list_del(&clp->cl_strhash); | 365 | list_del(&clp->cl_strhash); |
349 | list_del(&clp->cl_lru); | 366 | list_del(&clp->cl_lru); |
350 | while (!list_empty(&clp->cl_perclient)) { | 367 | while (!list_empty(&clp->cl_openowners)) { |
351 | sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient); | 368 | sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient); |
352 | release_stateowner(sop); | 369 | release_stateowner(sop); |
353 | } | 370 | } |
354 | put_nfs4_client(clp); | 371 | put_nfs4_client(clp); |
355 | } | 372 | } |
356 | 373 | ||
357 | static struct nfs4_client * | 374 | static struct nfs4_client * |
358 | create_client(struct xdr_netobj name) { | 375 | create_client(struct xdr_netobj name, char *recdir) { |
359 | struct nfs4_client *clp; | 376 | struct nfs4_client *clp; |
360 | 377 | ||
361 | if (!(clp = alloc_client(name))) | 378 | if (!(clp = alloc_client(name))) |
362 | goto out; | 379 | goto out; |
380 | memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); | ||
363 | atomic_set(&clp->cl_count, 1); | 381 | atomic_set(&clp->cl_count, 1); |
364 | atomic_set(&clp->cl_callback.cb_set, 0); | 382 | atomic_set(&clp->cl_callback.cb_set, 0); |
365 | clp->cl_callback.cb_parsed = 0; | ||
366 | INIT_LIST_HEAD(&clp->cl_idhash); | 383 | INIT_LIST_HEAD(&clp->cl_idhash); |
367 | INIT_LIST_HEAD(&clp->cl_strhash); | 384 | INIT_LIST_HEAD(&clp->cl_strhash); |
368 | INIT_LIST_HEAD(&clp->cl_perclient); | 385 | INIT_LIST_HEAD(&clp->cl_openowners); |
369 | INIT_LIST_HEAD(&clp->cl_del_perclnt); | 386 | INIT_LIST_HEAD(&clp->cl_delegations); |
370 | INIT_LIST_HEAD(&clp->cl_lru); | 387 | INIT_LIST_HEAD(&clp->cl_lru); |
371 | out: | 388 | out: |
372 | return clp; | 389 | return clp; |
@@ -392,11 +409,9 @@ copy_cred(struct svc_cred *target, struct svc_cred *source) { | |||
392 | get_group_info(target->cr_group_info); | 409 | get_group_info(target->cr_group_info); |
393 | } | 410 | } |
394 | 411 | ||
395 | static int | 412 | static inline int |
396 | cmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) { | 413 | same_name(const char *n1, const char *n2) { |
397 | if (!n1 || !n2) | 414 | return 0 == memcmp(n1, n2, HEXDIR_LEN); |
398 | return 0; | ||
399 | return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len)); | ||
400 | } | 415 | } |
401 | 416 | ||
402 | static int | 417 | static int |
@@ -446,7 +461,7 @@ check_name(struct xdr_netobj name) { | |||
446 | return 1; | 461 | return 1; |
447 | } | 462 | } |
448 | 463 | ||
449 | void | 464 | static void |
450 | add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) | 465 | add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) |
451 | { | 466 | { |
452 | unsigned int idhashval; | 467 | unsigned int idhashval; |
@@ -458,7 +473,7 @@ add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) | |||
458 | clp->cl_time = get_seconds(); | 473 | clp->cl_time = get_seconds(); |
459 | } | 474 | } |
460 | 475 | ||
461 | void | 476 | static void |
462 | move_to_confirmed(struct nfs4_client *clp) | 477 | move_to_confirmed(struct nfs4_client *clp) |
463 | { | 478 | { |
464 | unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); | 479 | unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); |
@@ -468,8 +483,7 @@ move_to_confirmed(struct nfs4_client *clp) | |||
468 | list_del_init(&clp->cl_strhash); | 483 | list_del_init(&clp->cl_strhash); |
469 | list_del_init(&clp->cl_idhash); | 484 | list_del_init(&clp->cl_idhash); |
470 | list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); | 485 | list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); |
471 | strhashval = clientstr_hashval(clp->cl_name.data, | 486 | strhashval = clientstr_hashval(clp->cl_recdir); |
472 | clp->cl_name.len); | ||
473 | list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); | 487 | list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); |
474 | renew_client(clp); | 488 | renew_client(clp); |
475 | } | 489 | } |
@@ -500,6 +514,30 @@ find_unconfirmed_client(clientid_t *clid) | |||
500 | return NULL; | 514 | return NULL; |
501 | } | 515 | } |
502 | 516 | ||
517 | static struct nfs4_client * | ||
518 | find_confirmed_client_by_str(const char *dname, unsigned int hashval) | ||
519 | { | ||
520 | struct nfs4_client *clp; | ||
521 | |||
522 | list_for_each_entry(clp, &conf_str_hashtbl[hashval], cl_strhash) { | ||
523 | if (same_name(clp->cl_recdir, dname)) | ||
524 | return clp; | ||
525 | } | ||
526 | return NULL; | ||
527 | } | ||
528 | |||
529 | static struct nfs4_client * | ||
530 | find_unconfirmed_client_by_str(const char *dname, unsigned int hashval) | ||
531 | { | ||
532 | struct nfs4_client *clp; | ||
533 | |||
534 | list_for_each_entry(clp, &unconf_str_hashtbl[hashval], cl_strhash) { | ||
535 | if (same_name(clp->cl_recdir, dname)) | ||
536 | return clp; | ||
537 | } | ||
538 | return NULL; | ||
539 | } | ||
540 | |||
503 | /* a helper function for parse_callback */ | 541 | /* a helper function for parse_callback */ |
504 | static int | 542 | static int |
505 | parse_octet(unsigned int *lenp, char **addrp) | 543 | parse_octet(unsigned int *lenp, char **addrp) |
@@ -534,7 +572,7 @@ parse_octet(unsigned int *lenp, char **addrp) | |||
534 | } | 572 | } |
535 | 573 | ||
536 | /* parse and set the setclientid ipv4 callback address */ | 574 | /* parse and set the setclientid ipv4 callback address */ |
537 | int | 575 | static int |
538 | parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp) | 576 | parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp) |
539 | { | 577 | { |
540 | int temp = 0; | 578 | int temp = 0; |
@@ -570,7 +608,7 @@ parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigne | |||
570 | return 1; | 608 | return 1; |
571 | } | 609 | } |
572 | 610 | ||
573 | void | 611 | static void |
574 | gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se) | 612 | gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se) |
575 | { | 613 | { |
576 | struct nfs4_callback *cb = &clp->cl_callback; | 614 | struct nfs4_callback *cb = &clp->cl_callback; |
@@ -584,14 +622,12 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se) | |||
584 | goto out_err; | 622 | goto out_err; |
585 | cb->cb_prog = se->se_callback_prog; | 623 | cb->cb_prog = se->se_callback_prog; |
586 | cb->cb_ident = se->se_callback_ident; | 624 | cb->cb_ident = se->se_callback_ident; |
587 | cb->cb_parsed = 1; | ||
588 | return; | 625 | return; |
589 | out_err: | 626 | out_err: |
590 | printk(KERN_INFO "NFSD: this client (clientid %08x/%08x) " | 627 | printk(KERN_INFO "NFSD: this client (clientid %08x/%08x) " |
591 | "will not receive delegations\n", | 628 | "will not receive delegations\n", |
592 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | 629 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); |
593 | 630 | ||
594 | cb->cb_parsed = 0; | ||
595 | return; | 631 | return; |
596 | } | 632 | } |
597 | 633 | ||
@@ -638,59 +674,43 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) | |||
638 | }; | 674 | }; |
639 | nfs4_verifier clverifier = setclid->se_verf; | 675 | nfs4_verifier clverifier = setclid->se_verf; |
640 | unsigned int strhashval; | 676 | unsigned int strhashval; |
641 | struct nfs4_client * conf, * unconf, * new, * clp; | 677 | struct nfs4_client *conf, *unconf, *new; |
642 | int status; | 678 | int status; |
679 | char dname[HEXDIR_LEN]; | ||
643 | 680 | ||
644 | status = nfserr_inval; | 681 | status = nfserr_inval; |
645 | if (!check_name(clname)) | 682 | if (!check_name(clname)) |
646 | goto out; | 683 | goto out; |
647 | 684 | ||
685 | status = nfs4_make_rec_clidname(dname, &clname); | ||
686 | if (status) | ||
687 | goto out; | ||
688 | |||
648 | /* | 689 | /* |
649 | * XXX The Duplicate Request Cache (DRC) has been checked (??) | 690 | * XXX The Duplicate Request Cache (DRC) has been checked (??) |
650 | * We get here on a DRC miss. | 691 | * We get here on a DRC miss. |
651 | */ | 692 | */ |
652 | 693 | ||
653 | strhashval = clientstr_hashval(clname.data, clname.len); | 694 | strhashval = clientstr_hashval(dname); |
654 | 695 | ||
655 | conf = NULL; | ||
656 | nfs4_lock_state(); | 696 | nfs4_lock_state(); |
657 | list_for_each_entry(clp, &conf_str_hashtbl[strhashval], cl_strhash) { | 697 | conf = find_confirmed_client_by_str(dname, strhashval); |
658 | if (!cmp_name(&clp->cl_name, &clname)) | 698 | if (conf) { |
659 | continue; | ||
660 | /* | 699 | /* |
661 | * CASE 0: | 700 | * CASE 0: |
662 | * clname match, confirmed, different principal | 701 | * clname match, confirmed, different principal |
663 | * or different ip_address | 702 | * or different ip_address |
664 | */ | 703 | */ |
665 | status = nfserr_clid_inuse; | 704 | status = nfserr_clid_inuse; |
666 | if (!cmp_creds(&clp->cl_cred,&rqstp->rq_cred)) { | 705 | if (!cmp_creds(&conf->cl_cred, &rqstp->rq_cred) |
667 | printk("NFSD: setclientid: string in use by client" | 706 | || conf->cl_addr != ip_addr) { |
668 | "(clientid %08x/%08x)\n", | ||
669 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | ||
670 | goto out; | ||
671 | } | ||
672 | if (clp->cl_addr != ip_addr) { | ||
673 | printk("NFSD: setclientid: string in use by client" | 707 | printk("NFSD: setclientid: string in use by client" |
674 | "(clientid %08x/%08x)\n", | 708 | "(clientid %08x/%08x)\n", |
675 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | 709 | conf->cl_clientid.cl_boot, conf->cl_clientid.cl_id); |
676 | goto out; | 710 | goto out; |
677 | } | 711 | } |
678 | |||
679 | /* | ||
680 | * cl_name match from a previous SETCLIENTID operation | ||
681 | * XXX check for additional matches? | ||
682 | */ | ||
683 | conf = clp; | ||
684 | break; | ||
685 | } | ||
686 | unconf = NULL; | ||
687 | list_for_each_entry(clp, &unconf_str_hashtbl[strhashval], cl_strhash) { | ||
688 | if (!cmp_name(&clp->cl_name, &clname)) | ||
689 | continue; | ||
690 | /* cl_name match from a previous SETCLIENTID operation */ | ||
691 | unconf = clp; | ||
692 | break; | ||
693 | } | 712 | } |
713 | unconf = find_unconfirmed_client_by_str(dname, strhashval); | ||
694 | status = nfserr_resource; | 714 | status = nfserr_resource; |
695 | if (!conf) { | 715 | if (!conf) { |
696 | /* | 716 | /* |
@@ -699,7 +719,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) | |||
699 | */ | 719 | */ |
700 | if (unconf) | 720 | if (unconf) |
701 | expire_client(unconf); | 721 | expire_client(unconf); |
702 | if (!(new = create_client(clname))) | 722 | new = create_client(clname, dname); |
723 | if (new == NULL) | ||
703 | goto out; | 724 | goto out; |
704 | copy_verf(new, &clverifier); | 725 | copy_verf(new, &clverifier); |
705 | new->cl_addr = ip_addr; | 726 | new->cl_addr = ip_addr; |
@@ -722,12 +743,16 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) | |||
722 | * nfs4_client, but with the new callback info and a | 743 | * nfs4_client, but with the new callback info and a |
723 | * new cl_confirm | 744 | * new cl_confirm |
724 | */ | 745 | */ |
725 | if ((unconf) && | 746 | if (unconf) { |
726 | cmp_verf(&unconf->cl_verifier, &conf->cl_verifier) && | 747 | /* Note this is removing unconfirmed {*x***}, |
727 | cmp_clid(&unconf->cl_clientid, &conf->cl_clientid)) { | 748 | * which is stronger than RFC recommended {vxc**}. |
728 | expire_client(unconf); | 749 | * This has the advantage that there is at most |
750 | * one {*x***} in either list at any time. | ||
751 | */ | ||
752 | expire_client(unconf); | ||
729 | } | 753 | } |
730 | if (!(new = create_client(clname))) | 754 | new = create_client(clname, dname); |
755 | if (new == NULL) | ||
731 | goto out; | 756 | goto out; |
732 | copy_verf(new,&conf->cl_verifier); | 757 | copy_verf(new,&conf->cl_verifier); |
733 | new->cl_addr = ip_addr; | 758 | new->cl_addr = ip_addr; |
@@ -745,7 +770,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) | |||
745 | * using input clverifier, clname, and callback info | 770 | * using input clverifier, clname, and callback info |
746 | * and generate a new cl_clientid and cl_confirm. | 771 | * and generate a new cl_clientid and cl_confirm. |
747 | */ | 772 | */ |
748 | if (!(new = create_client(clname))) | 773 | new = create_client(clname, dname); |
774 | if (new == NULL) | ||
749 | goto out; | 775 | goto out; |
750 | copy_verf(new,&clverifier); | 776 | copy_verf(new,&clverifier); |
751 | new->cl_addr = ip_addr; | 777 | new->cl_addr = ip_addr; |
@@ -771,7 +797,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) | |||
771 | * new cl_verifier and a new cl_confirm | 797 | * new cl_verifier and a new cl_confirm |
772 | */ | 798 | */ |
773 | expire_client(unconf); | 799 | expire_client(unconf); |
774 | if (!(new = create_client(clname))) | 800 | new = create_client(clname, dname); |
801 | if (new == NULL) | ||
775 | goto out; | 802 | goto out; |
776 | copy_verf(new,&clverifier); | 803 | copy_verf(new,&clverifier); |
777 | new->cl_addr = ip_addr; | 804 | new->cl_addr = ip_addr; |
@@ -807,7 +834,7 @@ int | |||
807 | nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confirm *setclientid_confirm) | 834 | nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confirm *setclientid_confirm) |
808 | { | 835 | { |
809 | u32 ip_addr = rqstp->rq_addr.sin_addr.s_addr; | 836 | u32 ip_addr = rqstp->rq_addr.sin_addr.s_addr; |
810 | struct nfs4_client *clp, *conf = NULL, *unconf = NULL; | 837 | struct nfs4_client *conf, *unconf; |
811 | nfs4_verifier confirm = setclientid_confirm->sc_confirm; | 838 | nfs4_verifier confirm = setclientid_confirm->sc_confirm; |
812 | clientid_t * clid = &setclientid_confirm->sc_clientid; | 839 | clientid_t * clid = &setclientid_confirm->sc_clientid; |
813 | int status; | 840 | int status; |
@@ -820,102 +847,90 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi | |||
820 | */ | 847 | */ |
821 | 848 | ||
822 | nfs4_lock_state(); | 849 | nfs4_lock_state(); |
823 | clp = find_confirmed_client(clid); | 850 | |
824 | if (clp) { | 851 | conf = find_confirmed_client(clid); |
825 | status = nfserr_inval; | 852 | unconf = find_unconfirmed_client(clid); |
826 | /* | 853 | |
827 | * Found a record for this clientid. If the IP addresses | 854 | status = nfserr_clid_inuse; |
828 | * don't match, return ERR_INVAL just as if the record had | 855 | if (conf && conf->cl_addr != ip_addr) |
829 | * not been found. | 856 | goto out; |
830 | */ | 857 | if (unconf && unconf->cl_addr != ip_addr) |
831 | if (clp->cl_addr != ip_addr) { | 858 | goto out; |
832 | printk("NFSD: setclientid: string in use by client" | 859 | |
833 | "(clientid %08x/%08x)\n", | ||
834 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | ||
835 | goto out; | ||
836 | } | ||
837 | conf = clp; | ||
838 | } | ||
839 | clp = find_unconfirmed_client(clid); | ||
840 | if (clp) { | ||
841 | status = nfserr_inval; | ||
842 | if (clp->cl_addr != ip_addr) { | ||
843 | printk("NFSD: setclientid: string in use by client" | ||
844 | "(clientid %08x/%08x)\n", | ||
845 | clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); | ||
846 | goto out; | ||
847 | } | ||
848 | unconf = clp; | ||
849 | } | ||
850 | /* CASE 1: | ||
851 | * unconf record that matches input clientid and input confirm. | ||
852 | * conf record that matches input clientid. | ||
853 | * conf and unconf records match names, verifiers | ||
854 | */ | ||
855 | if ((conf && unconf) && | 860 | if ((conf && unconf) && |
856 | (cmp_verf(&unconf->cl_confirm, &confirm)) && | 861 | (cmp_verf(&unconf->cl_confirm, &confirm)) && |
857 | (cmp_verf(&conf->cl_verifier, &unconf->cl_verifier)) && | 862 | (cmp_verf(&conf->cl_verifier, &unconf->cl_verifier)) && |
858 | (cmp_name(&conf->cl_name,&unconf->cl_name)) && | 863 | (same_name(conf->cl_recdir,unconf->cl_recdir)) && |
859 | (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm))) { | 864 | (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm))) { |
865 | /* CASE 1: | ||
866 | * unconf record that matches input clientid and input confirm. | ||
867 | * conf record that matches input clientid. | ||
868 | * conf and unconf records match names, verifiers | ||
869 | */ | ||
860 | if (!cmp_creds(&conf->cl_cred, &unconf->cl_cred)) | 870 | if (!cmp_creds(&conf->cl_cred, &unconf->cl_cred)) |
861 | status = nfserr_clid_inuse; | 871 | status = nfserr_clid_inuse; |
862 | else { | 872 | else { |
863 | expire_client(conf); | 873 | /* XXX: We just turn off callbacks until we can handle |
864 | clp = unconf; | 874 | * change request correctly. */ |
865 | move_to_confirmed(unconf); | 875 | atomic_set(&conf->cl_callback.cb_set, 0); |
876 | gen_confirm(conf); | ||
877 | expire_client(unconf); | ||
866 | status = nfs_ok; | 878 | status = nfs_ok; |
879 | |||
867 | } | 880 | } |
868 | goto out; | 881 | } else if ((conf && !unconf) || |
869 | } | ||
870 | /* CASE 2: | ||
871 | * conf record that matches input clientid. | ||
872 | * if unconf record that matches input clientid, then unconf->cl_name | ||
873 | * or unconf->cl_verifier don't match the conf record. | ||
874 | */ | ||
875 | if ((conf && !unconf) || | ||
876 | ((conf && unconf) && | 882 | ((conf && unconf) && |
877 | (!cmp_verf(&conf->cl_verifier, &unconf->cl_verifier) || | 883 | (!cmp_verf(&conf->cl_verifier, &unconf->cl_verifier) || |
878 | !cmp_name(&conf->cl_name, &unconf->cl_name)))) { | 884 | !same_name(conf->cl_recdir, unconf->cl_recdir)))) { |
879 | if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) { | 885 | /* CASE 2: |
886 | * conf record that matches input clientid. | ||
887 | * if unconf record matches input clientid, then | ||
888 | * unconf->cl_name or unconf->cl_verifier don't match the | ||
889 | * conf record. | ||
890 | */ | ||
891 | if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) | ||
880 | status = nfserr_clid_inuse; | 892 | status = nfserr_clid_inuse; |
881 | } else { | 893 | else |
882 | clp = conf; | ||
883 | status = nfs_ok; | 894 | status = nfs_ok; |
884 | } | 895 | } else if (!conf && unconf |
885 | goto out; | 896 | && cmp_verf(&unconf->cl_confirm, &confirm)) { |
886 | } | 897 | /* CASE 3: |
887 | /* CASE 3: | 898 | * conf record not found. |
888 | * conf record not found. | 899 | * unconf record found. |
889 | * unconf record found. | 900 | * unconf->cl_confirm matches input confirm |
890 | * unconf->cl_confirm matches input confirm | 901 | */ |
891 | */ | ||
892 | if (!conf && unconf && cmp_verf(&unconf->cl_confirm, &confirm)) { | ||
893 | if (!cmp_creds(&unconf->cl_cred, &rqstp->rq_cred)) { | 902 | if (!cmp_creds(&unconf->cl_cred, &rqstp->rq_cred)) { |
894 | status = nfserr_clid_inuse; | 903 | status = nfserr_clid_inuse; |
895 | } else { | 904 | } else { |
896 | status = nfs_ok; | 905 | unsigned int hash = |
897 | clp = unconf; | 906 | clientstr_hashval(unconf->cl_recdir); |
907 | conf = find_confirmed_client_by_str(unconf->cl_recdir, | ||
908 | hash); | ||
909 | if (conf) { | ||
910 | nfsd4_remove_clid_dir(conf); | ||
911 | expire_client(conf); | ||
912 | } | ||
898 | move_to_confirmed(unconf); | 913 | move_to_confirmed(unconf); |
914 | conf = unconf; | ||
915 | status = nfs_ok; | ||
899 | } | 916 | } |
900 | goto out; | 917 | } else if ((!conf || (conf && !cmp_verf(&conf->cl_confirm, &confirm))) |
901 | } | 918 | && (!unconf || (unconf && !cmp_verf(&unconf->cl_confirm, |
902 | /* CASE 4: | 919 | &confirm)))) { |
903 | * conf record not found, or if conf, then conf->cl_confirm does not | 920 | /* CASE 4: |
904 | * match input confirm. | 921 | * conf record not found, or if conf, conf->cl_confirm does not |
905 | * unconf record not found, or if unconf, then unconf->cl_confirm | 922 | * match input confirm. |
906 | * does not match input confirm. | 923 | * unconf record not found, or if unconf, unconf->cl_confirm |
907 | */ | 924 | * does not match input confirm. |
908 | if ((!conf || (conf && !cmp_verf(&conf->cl_confirm, &confirm))) && | 925 | */ |
909 | (!unconf || (unconf && !cmp_verf(&unconf->cl_confirm, &confirm)))) { | ||
910 | status = nfserr_stale_clientid; | 926 | status = nfserr_stale_clientid; |
911 | goto out; | 927 | } else { |
928 | /* check that we have hit one of the cases...*/ | ||
929 | status = nfserr_clid_inuse; | ||
912 | } | 930 | } |
913 | /* check that we have hit one of the cases...*/ | ||
914 | status = nfserr_inval; | ||
915 | goto out; | ||
916 | out: | 931 | out: |
917 | if (!status) | 932 | if (!status) |
918 | nfsd4_probe_callback(clp); | 933 | nfsd4_probe_callback(conf); |
919 | nfs4_unlock_state(); | 934 | nfs4_unlock_state(); |
920 | return status; | 935 | return status; |
921 | } | 936 | } |
@@ -961,60 +976,65 @@ alloc_init_file(struct inode *ino) | |||
961 | struct nfs4_file *fp; | 976 | struct nfs4_file *fp; |
962 | unsigned int hashval = file_hashval(ino); | 977 | unsigned int hashval = file_hashval(ino); |
963 | 978 | ||
964 | if ((fp = kmalloc(sizeof(struct nfs4_file),GFP_KERNEL))) { | 979 | fp = kmem_cache_alloc(file_slab, GFP_KERNEL); |
980 | if (fp) { | ||
981 | kref_init(&fp->fi_ref); | ||
965 | INIT_LIST_HEAD(&fp->fi_hash); | 982 | INIT_LIST_HEAD(&fp->fi_hash); |
966 | INIT_LIST_HEAD(&fp->fi_perfile); | 983 | INIT_LIST_HEAD(&fp->fi_stateids); |
967 | INIT_LIST_HEAD(&fp->fi_del_perfile); | 984 | INIT_LIST_HEAD(&fp->fi_delegations); |
968 | list_add(&fp->fi_hash, &file_hashtbl[hashval]); | 985 | list_add(&fp->fi_hash, &file_hashtbl[hashval]); |
969 | fp->fi_inode = igrab(ino); | 986 | fp->fi_inode = igrab(ino); |
970 | fp->fi_id = current_fileid++; | 987 | fp->fi_id = current_fileid++; |
971 | alloc_file++; | ||
972 | return fp; | 988 | return fp; |
973 | } | 989 | } |
974 | return NULL; | 990 | return NULL; |
975 | } | 991 | } |
976 | 992 | ||
977 | static void | 993 | static void |
978 | release_all_files(void) | 994 | nfsd4_free_slab(kmem_cache_t **slab) |
979 | { | 995 | { |
980 | int i; | 996 | int status; |
981 | struct nfs4_file *fp; | ||
982 | 997 | ||
983 | for (i=0;i<FILE_HASH_SIZE;i++) { | 998 | if (*slab == NULL) |
984 | while (!list_empty(&file_hashtbl[i])) { | 999 | return; |
985 | fp = list_entry(file_hashtbl[i].next, struct nfs4_file, fi_hash); | 1000 | status = kmem_cache_destroy(*slab); |
986 | /* this should never be more than once... */ | 1001 | *slab = NULL; |
987 | if (!list_empty(&fp->fi_perfile) || !list_empty(&fp->fi_del_perfile)) { | 1002 | WARN_ON(status); |
988 | printk("ERROR: release_all_files: file %p is open, creating dangling state !!!\n",fp); | ||
989 | } | ||
990 | release_file(fp); | ||
991 | } | ||
992 | } | ||
993 | } | 1003 | } |
994 | 1004 | ||
995 | kmem_cache_t *stateowner_slab = NULL; | 1005 | static void |
1006 | nfsd4_free_slabs(void) | ||
1007 | { | ||
1008 | nfsd4_free_slab(&stateowner_slab); | ||
1009 | nfsd4_free_slab(&file_slab); | ||
1010 | nfsd4_free_slab(&stateid_slab); | ||
1011 | nfsd4_free_slab(&deleg_slab); | ||
1012 | } | ||
996 | 1013 | ||
997 | static int | 1014 | static int |
998 | nfsd4_init_slabs(void) | 1015 | nfsd4_init_slabs(void) |
999 | { | 1016 | { |
1000 | stateowner_slab = kmem_cache_create("nfsd4_stateowners", | 1017 | stateowner_slab = kmem_cache_create("nfsd4_stateowners", |
1001 | sizeof(struct nfs4_stateowner), 0, 0, NULL, NULL); | 1018 | sizeof(struct nfs4_stateowner), 0, 0, NULL, NULL); |
1002 | if (stateowner_slab == NULL) { | 1019 | if (stateowner_slab == NULL) |
1003 | dprintk("nfsd4: out of memory while initializing nfsv4\n"); | 1020 | goto out_nomem; |
1004 | return -ENOMEM; | 1021 | file_slab = kmem_cache_create("nfsd4_files", |
1005 | } | 1022 | sizeof(struct nfs4_file), 0, 0, NULL, NULL); |
1023 | if (file_slab == NULL) | ||
1024 | goto out_nomem; | ||
1025 | stateid_slab = kmem_cache_create("nfsd4_stateids", | ||
1026 | sizeof(struct nfs4_stateid), 0, 0, NULL, NULL); | ||
1027 | if (stateid_slab == NULL) | ||
1028 | goto out_nomem; | ||
1029 | deleg_slab = kmem_cache_create("nfsd4_delegations", | ||
1030 | sizeof(struct nfs4_delegation), 0, 0, NULL, NULL); | ||
1031 | if (deleg_slab == NULL) | ||
1032 | goto out_nomem; | ||
1006 | return 0; | 1033 | return 0; |
1007 | } | 1034 | out_nomem: |
1008 | 1035 | nfsd4_free_slabs(); | |
1009 | static void | 1036 | dprintk("nfsd4: out of memory while initializing nfsv4\n"); |
1010 | nfsd4_free_slabs(void) | 1037 | return -ENOMEM; |
1011 | { | ||
1012 | int status = 0; | ||
1013 | |||
1014 | if (stateowner_slab) | ||
1015 | status = kmem_cache_destroy(stateowner_slab); | ||
1016 | stateowner_slab = NULL; | ||
1017 | BUG_ON(status); | ||
1018 | } | 1038 | } |
1019 | 1039 | ||
1020 | void | 1040 | void |
@@ -1055,14 +1075,13 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str | |||
1055 | INIT_LIST_HEAD(&sop->so_idhash); | 1075 | INIT_LIST_HEAD(&sop->so_idhash); |
1056 | INIT_LIST_HEAD(&sop->so_strhash); | 1076 | INIT_LIST_HEAD(&sop->so_strhash); |
1057 | INIT_LIST_HEAD(&sop->so_perclient); | 1077 | INIT_LIST_HEAD(&sop->so_perclient); |
1058 | INIT_LIST_HEAD(&sop->so_perfilestate); | 1078 | INIT_LIST_HEAD(&sop->so_stateids); |
1059 | INIT_LIST_HEAD(&sop->so_perlockowner); /* not used */ | 1079 | INIT_LIST_HEAD(&sop->so_perstateid); /* not used */ |
1060 | INIT_LIST_HEAD(&sop->so_close_lru); | 1080 | INIT_LIST_HEAD(&sop->so_close_lru); |
1061 | sop->so_time = 0; | 1081 | sop->so_time = 0; |
1062 | list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]); | 1082 | list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]); |
1063 | list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]); | 1083 | list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]); |
1064 | list_add(&sop->so_perclient, &clp->cl_perclient); | 1084 | list_add(&sop->so_perclient, &clp->cl_openowners); |
1065 | add_perclient++; | ||
1066 | sop->so_is_open_owner = 1; | 1085 | sop->so_is_open_owner = 1; |
1067 | sop->so_id = current_ownerid++; | 1086 | sop->so_id = current_ownerid++; |
1068 | sop->so_client = clp; | 1087 | sop->so_client = clp; |
@@ -1080,10 +1099,10 @@ release_stateid_lockowners(struct nfs4_stateid *open_stp) | |||
1080 | { | 1099 | { |
1081 | struct nfs4_stateowner *lock_sop; | 1100 | struct nfs4_stateowner *lock_sop; |
1082 | 1101 | ||
1083 | while (!list_empty(&open_stp->st_perlockowner)) { | 1102 | while (!list_empty(&open_stp->st_lockowners)) { |
1084 | lock_sop = list_entry(open_stp->st_perlockowner.next, | 1103 | lock_sop = list_entry(open_stp->st_lockowners.next, |
1085 | struct nfs4_stateowner, so_perlockowner); | 1104 | struct nfs4_stateowner, so_perstateid); |
1086 | /* list_del(&open_stp->st_perlockowner); */ | 1105 | /* list_del(&open_stp->st_lockowners); */ |
1087 | BUG_ON(lock_sop->so_is_open_owner); | 1106 | BUG_ON(lock_sop->so_is_open_owner); |
1088 | release_stateowner(lock_sop); | 1107 | release_stateowner(lock_sop); |
1089 | } | 1108 | } |
@@ -1096,14 +1115,12 @@ unhash_stateowner(struct nfs4_stateowner *sop) | |||
1096 | 1115 | ||
1097 | list_del(&sop->so_idhash); | 1116 | list_del(&sop->so_idhash); |
1098 | list_del(&sop->so_strhash); | 1117 | list_del(&sop->so_strhash); |
1099 | if (sop->so_is_open_owner) { | 1118 | if (sop->so_is_open_owner) |
1100 | list_del(&sop->so_perclient); | 1119 | list_del(&sop->so_perclient); |
1101 | del_perclient++; | 1120 | list_del(&sop->so_perstateid); |
1102 | } | 1121 | while (!list_empty(&sop->so_stateids)) { |
1103 | list_del(&sop->so_perlockowner); | 1122 | stp = list_entry(sop->so_stateids.next, |
1104 | while (!list_empty(&sop->so_perfilestate)) { | 1123 | struct nfs4_stateid, st_perstateowner); |
1105 | stp = list_entry(sop->so_perfilestate.next, | ||
1106 | struct nfs4_stateid, st_perfilestate); | ||
1107 | if (sop->so_is_open_owner) | 1124 | if (sop->so_is_open_owner) |
1108 | release_stateid(stp, OPEN_STATE); | 1125 | release_stateid(stp, OPEN_STATE); |
1109 | else | 1126 | else |
@@ -1125,14 +1142,14 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open * | |||
1125 | unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); | 1142 | unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); |
1126 | 1143 | ||
1127 | INIT_LIST_HEAD(&stp->st_hash); | 1144 | INIT_LIST_HEAD(&stp->st_hash); |
1128 | INIT_LIST_HEAD(&stp->st_perfilestate); | 1145 | INIT_LIST_HEAD(&stp->st_perstateowner); |
1129 | INIT_LIST_HEAD(&stp->st_perlockowner); | 1146 | INIT_LIST_HEAD(&stp->st_lockowners); |
1130 | INIT_LIST_HEAD(&stp->st_perfile); | 1147 | INIT_LIST_HEAD(&stp->st_perfile); |
1131 | list_add(&stp->st_hash, &stateid_hashtbl[hashval]); | 1148 | list_add(&stp->st_hash, &stateid_hashtbl[hashval]); |
1132 | list_add(&stp->st_perfilestate, &sop->so_perfilestate); | 1149 | list_add(&stp->st_perstateowner, &sop->so_stateids); |
1133 | list_add_perfile++; | 1150 | list_add(&stp->st_perfile, &fp->fi_stateids); |
1134 | list_add(&stp->st_perfile, &fp->fi_perfile); | ||
1135 | stp->st_stateowner = sop; | 1151 | stp->st_stateowner = sop; |
1152 | get_nfs4_file(fp); | ||
1136 | stp->st_file = fp; | 1153 | stp->st_file = fp; |
1137 | stp->st_stateid.si_boot = boot_time; | 1154 | stp->st_stateid.si_boot = boot_time; |
1138 | stp->st_stateid.si_stateownerid = sop->so_id; | 1155 | stp->st_stateid.si_stateownerid = sop->so_id; |
@@ -1150,30 +1167,20 @@ release_stateid(struct nfs4_stateid *stp, int flags) | |||
1150 | struct file *filp = stp->st_vfs_file; | 1167 | struct file *filp = stp->st_vfs_file; |
1151 | 1168 | ||
1152 | list_del(&stp->st_hash); | 1169 | list_del(&stp->st_hash); |
1153 | list_del_perfile++; | ||
1154 | list_del(&stp->st_perfile); | 1170 | list_del(&stp->st_perfile); |
1155 | list_del(&stp->st_perfilestate); | 1171 | list_del(&stp->st_perstateowner); |
1156 | if (flags & OPEN_STATE) { | 1172 | if (flags & OPEN_STATE) { |
1157 | release_stateid_lockowners(stp); | 1173 | release_stateid_lockowners(stp); |
1158 | stp->st_vfs_file = NULL; | 1174 | stp->st_vfs_file = NULL; |
1159 | nfsd_close(filp); | 1175 | nfsd_close(filp); |
1160 | vfsclose++; | ||
1161 | } else if (flags & LOCK_STATE) | 1176 | } else if (flags & LOCK_STATE) |
1162 | locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner); | 1177 | locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner); |
1163 | kfree(stp); | 1178 | put_nfs4_file(stp->st_file); |
1179 | kmem_cache_free(stateid_slab, stp); | ||
1164 | stp = NULL; | 1180 | stp = NULL; |
1165 | } | 1181 | } |
1166 | 1182 | ||
1167 | static void | 1183 | static void |
1168 | release_file(struct nfs4_file *fp) | ||
1169 | { | ||
1170 | free_file++; | ||
1171 | list_del(&fp->fi_hash); | ||
1172 | iput(fp->fi_inode); | ||
1173 | kfree(fp); | ||
1174 | } | ||
1175 | |||
1176 | void | ||
1177 | move_to_close_lru(struct nfs4_stateowner *sop) | 1184 | move_to_close_lru(struct nfs4_stateowner *sop) |
1178 | { | 1185 | { |
1179 | dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); | 1186 | dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); |
@@ -1183,11 +1190,10 @@ move_to_close_lru(struct nfs4_stateowner *sop) | |||
1183 | sop->so_time = get_seconds(); | 1190 | sop->so_time = get_seconds(); |
1184 | } | 1191 | } |
1185 | 1192 | ||
1186 | void | 1193 | static void |
1187 | release_state_owner(struct nfs4_stateid *stp, int flag) | 1194 | release_state_owner(struct nfs4_stateid *stp, int flag) |
1188 | { | 1195 | { |
1189 | struct nfs4_stateowner *sop = stp->st_stateowner; | 1196 | struct nfs4_stateowner *sop = stp->st_stateowner; |
1190 | struct nfs4_file *fp = stp->st_file; | ||
1191 | 1197 | ||
1192 | dprintk("NFSD: release_state_owner\n"); | 1198 | dprintk("NFSD: release_state_owner\n"); |
1193 | release_stateid(stp, flag); | 1199 | release_stateid(stp, flag); |
@@ -1196,12 +1202,8 @@ release_state_owner(struct nfs4_stateid *stp, int flag) | |||
1196 | * released by the laundromat service after the lease period | 1202 | * released by the laundromat service after the lease period |
1197 | * to enable us to handle CLOSE replay | 1203 | * to enable us to handle CLOSE replay |
1198 | */ | 1204 | */ |
1199 | if (sop->so_confirmed && list_empty(&sop->so_perfilestate)) | 1205 | if (sop->so_confirmed && list_empty(&sop->so_stateids)) |
1200 | move_to_close_lru(sop); | 1206 | move_to_close_lru(sop); |
1201 | /* unused nfs4_file's are releseed. XXX slab cache? */ | ||
1202 | if (list_empty(&fp->fi_perfile) && list_empty(&fp->fi_del_perfile)) { | ||
1203 | release_file(fp); | ||
1204 | } | ||
1205 | } | 1207 | } |
1206 | 1208 | ||
1207 | static int | 1209 | static int |
@@ -1231,8 +1233,10 @@ find_file(struct inode *ino) | |||
1231 | struct nfs4_file *fp; | 1233 | struct nfs4_file *fp; |
1232 | 1234 | ||
1233 | list_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) { | 1235 | list_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) { |
1234 | if (fp->fi_inode == ino) | 1236 | if (fp->fi_inode == ino) { |
1237 | get_nfs4_file(fp); | ||
1235 | return fp; | 1238 | return fp; |
1239 | } | ||
1236 | } | 1240 | } |
1237 | return NULL; | 1241 | return NULL; |
1238 | } | 1242 | } |
@@ -1240,7 +1244,7 @@ find_file(struct inode *ino) | |||
1240 | #define TEST_ACCESS(x) ((x > 0 || x < 4)?1:0) | 1244 | #define TEST_ACCESS(x) ((x > 0 || x < 4)?1:0) |
1241 | #define TEST_DENY(x) ((x >= 0 || x < 5)?1:0) | 1245 | #define TEST_DENY(x) ((x >= 0 || x < 5)?1:0) |
1242 | 1246 | ||
1243 | void | 1247 | static void |
1244 | set_access(unsigned int *access, unsigned long bmap) { | 1248 | set_access(unsigned int *access, unsigned long bmap) { |
1245 | int i; | 1249 | int i; |
1246 | 1250 | ||
@@ -1251,7 +1255,7 @@ set_access(unsigned int *access, unsigned long bmap) { | |||
1251 | } | 1255 | } |
1252 | } | 1256 | } |
1253 | 1257 | ||
1254 | void | 1258 | static void |
1255 | set_deny(unsigned int *deny, unsigned long bmap) { | 1259 | set_deny(unsigned int *deny, unsigned long bmap) { |
1256 | int i; | 1260 | int i; |
1257 | 1261 | ||
@@ -1277,25 +1281,30 @@ test_share(struct nfs4_stateid *stp, struct nfsd4_open *open) { | |||
1277 | * Called to check deny when READ with all zero stateid or | 1281 | * Called to check deny when READ with all zero stateid or |
1278 | * WRITE with all zero or all one stateid | 1282 | * WRITE with all zero or all one stateid |
1279 | */ | 1283 | */ |
1280 | int | 1284 | static int |
1281 | nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) | 1285 | nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) |
1282 | { | 1286 | { |
1283 | struct inode *ino = current_fh->fh_dentry->d_inode; | 1287 | struct inode *ino = current_fh->fh_dentry->d_inode; |
1284 | struct nfs4_file *fp; | 1288 | struct nfs4_file *fp; |
1285 | struct nfs4_stateid *stp; | 1289 | struct nfs4_stateid *stp; |
1290 | int ret; | ||
1286 | 1291 | ||
1287 | dprintk("NFSD: nfs4_share_conflict\n"); | 1292 | dprintk("NFSD: nfs4_share_conflict\n"); |
1288 | 1293 | ||
1289 | fp = find_file(ino); | 1294 | fp = find_file(ino); |
1290 | if (fp) { | 1295 | if (!fp) |
1296 | return nfs_ok; | ||
1297 | ret = nfserr_share_denied; | ||
1291 | /* Search for conflicting share reservations */ | 1298 | /* Search for conflicting share reservations */ |
1292 | list_for_each_entry(stp, &fp->fi_perfile, st_perfile) { | 1299 | list_for_each_entry(stp, &fp->fi_stateids, st_perfile) { |
1293 | if (test_bit(deny_type, &stp->st_deny_bmap) || | 1300 | if (test_bit(deny_type, &stp->st_deny_bmap) || |
1294 | test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) | 1301 | test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) |
1295 | return nfserr_share_denied; | 1302 | goto out; |
1296 | } | ||
1297 | } | 1303 | } |
1298 | return nfs_ok; | 1304 | ret = nfs_ok; |
1305 | out: | ||
1306 | put_nfs4_file(fp); | ||
1307 | return ret; | ||
1299 | } | 1308 | } |
1300 | 1309 | ||
1301 | static inline void | 1310 | static inline void |
@@ -1427,7 +1436,7 @@ int nfsd_change_deleg_cb(struct file_lock **onlist, int arg) | |||
1427 | return -EAGAIN; | 1436 | return -EAGAIN; |
1428 | } | 1437 | } |
1429 | 1438 | ||
1430 | struct lock_manager_operations nfsd_lease_mng_ops = { | 1439 | static struct lock_manager_operations nfsd_lease_mng_ops = { |
1431 | .fl_break = nfsd_break_deleg_cb, | 1440 | .fl_break = nfsd_break_deleg_cb, |
1432 | .fl_release_private = nfsd_release_deleg_cb, | 1441 | .fl_release_private = nfsd_release_deleg_cb, |
1433 | .fl_copy_lock = nfsd_copy_lock_deleg_cb, | 1442 | .fl_copy_lock = nfsd_copy_lock_deleg_cb, |
@@ -1526,6 +1535,51 @@ out: | |||
1526 | return status; | 1535 | return status; |
1527 | } | 1536 | } |
1528 | 1537 | ||
1538 | static inline int | ||
1539 | nfs4_check_delegmode(struct nfs4_delegation *dp, int flags) | ||
1540 | { | ||
1541 | if ((flags & WR_STATE) && (dp->dl_type == NFS4_OPEN_DELEGATE_READ)) | ||
1542 | return nfserr_openmode; | ||
1543 | else | ||
1544 | return nfs_ok; | ||
1545 | } | ||
1546 | |||
1547 | static struct nfs4_delegation * | ||
1548 | find_delegation_file(struct nfs4_file *fp, stateid_t *stid) | ||
1549 | { | ||
1550 | struct nfs4_delegation *dp; | ||
1551 | |||
1552 | list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) { | ||
1553 | if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid) | ||
1554 | return dp; | ||
1555 | } | ||
1556 | return NULL; | ||
1557 | } | ||
1558 | |||
1559 | static int | ||
1560 | nfs4_check_deleg(struct nfs4_file *fp, struct nfsd4_open *open, | ||
1561 | struct nfs4_delegation **dp) | ||
1562 | { | ||
1563 | int flags; | ||
1564 | int status = nfserr_bad_stateid; | ||
1565 | |||
1566 | *dp = find_delegation_file(fp, &open->op_delegate_stateid); | ||
1567 | if (*dp == NULL) | ||
1568 | goto out; | ||
1569 | flags = open->op_share_access == NFS4_SHARE_ACCESS_READ ? | ||
1570 | RD_STATE : WR_STATE; | ||
1571 | status = nfs4_check_delegmode(*dp, flags); | ||
1572 | if (status) | ||
1573 | *dp = NULL; | ||
1574 | out: | ||
1575 | if (open->op_claim_type != NFS4_OPEN_CLAIM_DELEGATE_CUR) | ||
1576 | return nfs_ok; | ||
1577 | if (status) | ||
1578 | return status; | ||
1579 | open->op_stateowner->so_confirmed = 1; | ||
1580 | return nfs_ok; | ||
1581 | } | ||
1582 | |||
1529 | static int | 1583 | static int |
1530 | nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_stateid **stpp) | 1584 | nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_stateid **stpp) |
1531 | { | 1585 | { |
@@ -1533,7 +1587,7 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_state | |||
1533 | int status = nfserr_share_denied; | 1587 | int status = nfserr_share_denied; |
1534 | struct nfs4_stateowner *sop = open->op_stateowner; | 1588 | struct nfs4_stateowner *sop = open->op_stateowner; |
1535 | 1589 | ||
1536 | list_for_each_entry(local, &fp->fi_perfile, st_perfile) { | 1590 | list_for_each_entry(local, &fp->fi_stateids, st_perfile) { |
1537 | /* ignore lock owners */ | 1591 | /* ignore lock owners */ |
1538 | if (local->st_stateowner->so_is_open_owner == 0) | 1592 | if (local->st_stateowner->so_is_open_owner == 0) |
1539 | continue; | 1593 | continue; |
@@ -1549,25 +1603,37 @@ out: | |||
1549 | return status; | 1603 | return status; |
1550 | } | 1604 | } |
1551 | 1605 | ||
1606 | static inline struct nfs4_stateid * | ||
1607 | nfs4_alloc_stateid(void) | ||
1608 | { | ||
1609 | return kmem_cache_alloc(stateid_slab, GFP_KERNEL); | ||
1610 | } | ||
1611 | |||
1552 | static int | 1612 | static int |
1553 | nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp, | 1613 | nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp, |
1614 | struct nfs4_delegation *dp, | ||
1554 | struct svc_fh *cur_fh, int flags) | 1615 | struct svc_fh *cur_fh, int flags) |
1555 | { | 1616 | { |
1556 | struct nfs4_stateid *stp; | 1617 | struct nfs4_stateid *stp; |
1557 | int status; | ||
1558 | 1618 | ||
1559 | stp = kmalloc(sizeof(struct nfs4_stateid), GFP_KERNEL); | 1619 | stp = nfs4_alloc_stateid(); |
1560 | if (stp == NULL) | 1620 | if (stp == NULL) |
1561 | return nfserr_resource; | 1621 | return nfserr_resource; |
1562 | 1622 | ||
1563 | status = nfsd_open(rqstp, cur_fh, S_IFREG, flags, &stp->st_vfs_file); | 1623 | if (dp) { |
1564 | if (status) { | 1624 | get_file(dp->dl_vfs_file); |
1565 | if (status == nfserr_dropit) | 1625 | stp->st_vfs_file = dp->dl_vfs_file; |
1566 | status = nfserr_jukebox; | 1626 | } else { |
1567 | kfree(stp); | 1627 | int status; |
1568 | return status; | 1628 | status = nfsd_open(rqstp, cur_fh, S_IFREG, flags, |
1629 | &stp->st_vfs_file); | ||
1630 | if (status) { | ||
1631 | if (status == nfserr_dropit) | ||
1632 | status = nfserr_jukebox; | ||
1633 | kmem_cache_free(stateid_slab, stp); | ||
1634 | return status; | ||
1635 | } | ||
1569 | } | 1636 | } |
1570 | vfsopen++; | ||
1571 | *stpp = stp; | 1637 | *stpp = stp; |
1572 | return 0; | 1638 | return 0; |
1573 | } | 1639 | } |
@@ -1628,6 +1694,7 @@ nfs4_set_claim_prev(struct nfsd4_open *open, int *status) | |||
1628 | *status = nfserr_reclaim_bad; | 1694 | *status = nfserr_reclaim_bad; |
1629 | else { | 1695 | else { |
1630 | open->op_stateowner->so_confirmed = 1; | 1696 | open->op_stateowner->so_confirmed = 1; |
1697 | open->op_stateowner->so_client->cl_firststate = 1; | ||
1631 | open->op_stateowner->so_seqid--; | 1698 | open->op_stateowner->so_seqid--; |
1632 | } | 1699 | } |
1633 | } | 1700 | } |
@@ -1646,14 +1713,30 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta | |||
1646 | int status, flag = 0; | 1713 | int status, flag = 0; |
1647 | 1714 | ||
1648 | flag = NFS4_OPEN_DELEGATE_NONE; | 1715 | flag = NFS4_OPEN_DELEGATE_NONE; |
1649 | if (open->op_claim_type != NFS4_OPEN_CLAIM_NULL | 1716 | open->op_recall = 0; |
1650 | || !atomic_read(&cb->cb_set) || !sop->so_confirmed) | 1717 | switch (open->op_claim_type) { |
1651 | goto out; | 1718 | case NFS4_OPEN_CLAIM_PREVIOUS: |
1652 | 1719 | if (!atomic_read(&cb->cb_set)) | |
1653 | if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) | 1720 | open->op_recall = 1; |
1654 | flag = NFS4_OPEN_DELEGATE_WRITE; | 1721 | flag = open->op_delegate_type; |
1655 | else | 1722 | if (flag == NFS4_OPEN_DELEGATE_NONE) |
1656 | flag = NFS4_OPEN_DELEGATE_READ; | 1723 | goto out; |
1724 | break; | ||
1725 | case NFS4_OPEN_CLAIM_NULL: | ||
1726 | /* Let's not give out any delegations till everyone's | ||
1727 | * had the chance to reclaim theirs.... */ | ||
1728 | if (nfs4_in_grace()) | ||
1729 | goto out; | ||
1730 | if (!atomic_read(&cb->cb_set) || !sop->so_confirmed) | ||
1731 | goto out; | ||
1732 | if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) | ||
1733 | flag = NFS4_OPEN_DELEGATE_WRITE; | ||
1734 | else | ||
1735 | flag = NFS4_OPEN_DELEGATE_READ; | ||
1736 | break; | ||
1737 | default: | ||
1738 | goto out; | ||
1739 | } | ||
1657 | 1740 | ||
1658 | dp = alloc_init_deleg(sop->so_client, stp, fh, flag); | 1741 | dp = alloc_init_deleg(sop->so_client, stp, fh, flag); |
1659 | if (dp == NULL) { | 1742 | if (dp == NULL) { |
@@ -1687,6 +1770,10 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta | |||
1687 | dp->dl_stateid.si_fileid, | 1770 | dp->dl_stateid.si_fileid, |
1688 | dp->dl_stateid.si_generation); | 1771 | dp->dl_stateid.si_generation); |
1689 | out: | 1772 | out: |
1773 | if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS | ||
1774 | && flag == NFS4_OPEN_DELEGATE_NONE | ||
1775 | && open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) | ||
1776 | printk("NFSD: WARNING: refusing delegation reclaim\n"); | ||
1690 | open->op_delegate_type = flag; | 1777 | open->op_delegate_type = flag; |
1691 | } | 1778 | } |
1692 | 1779 | ||
@@ -1699,6 +1786,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf | |||
1699 | struct nfs4_file *fp = NULL; | 1786 | struct nfs4_file *fp = NULL; |
1700 | struct inode *ino = current_fh->fh_dentry->d_inode; | 1787 | struct inode *ino = current_fh->fh_dentry->d_inode; |
1701 | struct nfs4_stateid *stp = NULL; | 1788 | struct nfs4_stateid *stp = NULL; |
1789 | struct nfs4_delegation *dp = NULL; | ||
1702 | int status; | 1790 | int status; |
1703 | 1791 | ||
1704 | status = nfserr_inval; | 1792 | status = nfserr_inval; |
@@ -1713,7 +1801,13 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf | |||
1713 | if (fp) { | 1801 | if (fp) { |
1714 | if ((status = nfs4_check_open(fp, open, &stp))) | 1802 | if ((status = nfs4_check_open(fp, open, &stp))) |
1715 | goto out; | 1803 | goto out; |
1804 | status = nfs4_check_deleg(fp, open, &dp); | ||
1805 | if (status) | ||
1806 | goto out; | ||
1716 | } else { | 1807 | } else { |
1808 | status = nfserr_bad_stateid; | ||
1809 | if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) | ||
1810 | goto out; | ||
1717 | status = nfserr_resource; | 1811 | status = nfserr_resource; |
1718 | fp = alloc_init_file(ino); | 1812 | fp = alloc_init_file(ino); |
1719 | if (fp == NULL) | 1813 | if (fp == NULL) |
@@ -1736,7 +1830,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf | |||
1736 | flags = MAY_WRITE; | 1830 | flags = MAY_WRITE; |
1737 | else | 1831 | else |
1738 | flags = MAY_READ; | 1832 | flags = MAY_READ; |
1739 | if ((status = nfs4_new_open(rqstp, &stp, current_fh, flags))) | 1833 | status = nfs4_new_open(rqstp, &stp, dp, current_fh, flags); |
1834 | if (status) | ||
1740 | goto out; | 1835 | goto out; |
1741 | init_stateid(stp, fp, open); | 1836 | init_stateid(stp, fp, open); |
1742 | status = nfsd4_truncate(rqstp, current_fh, open); | 1837 | status = nfsd4_truncate(rqstp, current_fh, open); |
@@ -1759,10 +1854,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf | |||
1759 | stp->st_stateid.si_boot, stp->st_stateid.si_stateownerid, | 1854 | stp->st_stateid.si_boot, stp->st_stateid.si_stateownerid, |
1760 | stp->st_stateid.si_fileid, stp->st_stateid.si_generation); | 1855 | stp->st_stateid.si_fileid, stp->st_stateid.si_generation); |
1761 | out: | 1856 | out: |
1762 | /* take the opportunity to clean up unused state */ | 1857 | if (fp) |
1763 | if (fp && list_empty(&fp->fi_perfile) && list_empty(&fp->fi_del_perfile)) | 1858 | put_nfs4_file(fp); |
1764 | release_file(fp); | ||
1765 | |||
1766 | /* CLAIM_PREVIOUS has different error returns */ | 1859 | /* CLAIM_PREVIOUS has different error returns */ |
1767 | nfs4_set_claim_prev(open, &status); | 1860 | nfs4_set_claim_prev(open, &status); |
1768 | /* | 1861 | /* |
@@ -1775,6 +1868,7 @@ out: | |||
1775 | return status; | 1868 | return status; |
1776 | } | 1869 | } |
1777 | 1870 | ||
1871 | static struct workqueue_struct *laundry_wq; | ||
1778 | static struct work_struct laundromat_work; | 1872 | static struct work_struct laundromat_work; |
1779 | static void laundromat_main(void *); | 1873 | static void laundromat_main(void *); |
1780 | static DECLARE_WORK(laundromat_work, laundromat_main, NULL); | 1874 | static DECLARE_WORK(laundromat_work, laundromat_main, NULL); |
@@ -1800,7 +1894,7 @@ nfsd4_renew(clientid_t *clid) | |||
1800 | } | 1894 | } |
1801 | renew_client(clp); | 1895 | renew_client(clp); |
1802 | status = nfserr_cb_path_down; | 1896 | status = nfserr_cb_path_down; |
1803 | if (!list_empty(&clp->cl_del_perclnt) | 1897 | if (!list_empty(&clp->cl_delegations) |
1804 | && !atomic_read(&clp->cl_callback.cb_set)) | 1898 | && !atomic_read(&clp->cl_callback.cb_set)) |
1805 | goto out; | 1899 | goto out; |
1806 | status = nfs_ok; | 1900 | status = nfs_ok; |
@@ -1809,7 +1903,15 @@ out: | |||
1809 | return status; | 1903 | return status; |
1810 | } | 1904 | } |
1811 | 1905 | ||
1812 | time_t | 1906 | static void |
1907 | end_grace(void) | ||
1908 | { | ||
1909 | dprintk("NFSD: end of grace period\n"); | ||
1910 | nfsd4_recdir_purge_old(); | ||
1911 | in_grace = 0; | ||
1912 | } | ||
1913 | |||
1914 | static time_t | ||
1813 | nfs4_laundromat(void) | 1915 | nfs4_laundromat(void) |
1814 | { | 1916 | { |
1815 | struct nfs4_client *clp; | 1917 | struct nfs4_client *clp; |
@@ -1823,6 +1925,8 @@ nfs4_laundromat(void) | |||
1823 | nfs4_lock_state(); | 1925 | nfs4_lock_state(); |
1824 | 1926 | ||
1825 | dprintk("NFSD: laundromat service - starting\n"); | 1927 | dprintk("NFSD: laundromat service - starting\n"); |
1928 | if (in_grace) | ||
1929 | end_grace(); | ||
1826 | list_for_each_safe(pos, next, &client_lru) { | 1930 | list_for_each_safe(pos, next, &client_lru) { |
1827 | clp = list_entry(pos, struct nfs4_client, cl_lru); | 1931 | clp = list_entry(pos, struct nfs4_client, cl_lru); |
1828 | if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { | 1932 | if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { |
@@ -1833,6 +1937,7 @@ nfs4_laundromat(void) | |||
1833 | } | 1937 | } |
1834 | dprintk("NFSD: purging unused client (clientid %08x)\n", | 1938 | dprintk("NFSD: purging unused client (clientid %08x)\n", |
1835 | clp->cl_clientid.cl_id); | 1939 | clp->cl_clientid.cl_id); |
1940 | nfsd4_remove_clid_dir(clp); | ||
1836 | expire_client(clp); | 1941 | expire_client(clp); |
1837 | } | 1942 | } |
1838 | INIT_LIST_HEAD(&reaplist); | 1943 | INIT_LIST_HEAD(&reaplist); |
@@ -1882,13 +1987,13 @@ laundromat_main(void *not_used) | |||
1882 | 1987 | ||
1883 | t = nfs4_laundromat(); | 1988 | t = nfs4_laundromat(); |
1884 | dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); | 1989 | dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); |
1885 | schedule_delayed_work(&laundromat_work, t*HZ); | 1990 | queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); |
1886 | } | 1991 | } |
1887 | 1992 | ||
1888 | /* search ownerid_hashtbl[] and close_lru for stateid owner | 1993 | /* search ownerid_hashtbl[] and close_lru for stateid owner |
1889 | * (stateid->si_stateownerid) | 1994 | * (stateid->si_stateownerid) |
1890 | */ | 1995 | */ |
1891 | struct nfs4_stateowner * | 1996 | static struct nfs4_stateowner * |
1892 | find_openstateowner_id(u32 st_id, int flags) { | 1997 | find_openstateowner_id(u32 st_id, int flags) { |
1893 | struct nfs4_stateowner *local = NULL; | 1998 | struct nfs4_stateowner *local = NULL; |
1894 | 1999 | ||
@@ -1949,15 +2054,6 @@ out: | |||
1949 | } | 2054 | } |
1950 | 2055 | ||
1951 | static inline int | 2056 | static inline int |
1952 | nfs4_check_delegmode(struct nfs4_delegation *dp, int flags) | ||
1953 | { | ||
1954 | if ((flags & WR_STATE) && (dp->dl_type == NFS4_OPEN_DELEGATE_READ)) | ||
1955 | return nfserr_openmode; | ||
1956 | else | ||
1957 | return nfs_ok; | ||
1958 | } | ||
1959 | |||
1960 | static inline int | ||
1961 | check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) | 2057 | check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) |
1962 | { | 2058 | { |
1963 | /* Trying to call delegreturn with a special stateid? Yuch: */ | 2059 | /* Trying to call delegreturn with a special stateid? Yuch: */ |
@@ -2071,7 +2167,7 @@ out: | |||
2071 | /* | 2167 | /* |
2072 | * Checks for sequence id mutating operations. | 2168 | * Checks for sequence id mutating operations. |
2073 | */ | 2169 | */ |
2074 | int | 2170 | static int |
2075 | nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp, clientid_t *lockclid) | 2171 | nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp, clientid_t *lockclid) |
2076 | { | 2172 | { |
2077 | int status; | 2173 | int status; |
@@ -2230,6 +2326,8 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs | |||
2230 | stp->st_stateid.si_stateownerid, | 2326 | stp->st_stateid.si_stateownerid, |
2231 | stp->st_stateid.si_fileid, | 2327 | stp->st_stateid.si_fileid, |
2232 | stp->st_stateid.si_generation); | 2328 | stp->st_stateid.si_generation); |
2329 | |||
2330 | nfsd4_create_clid_dir(sop->so_client); | ||
2233 | out: | 2331 | out: |
2234 | if (oc->oc_stateowner) | 2332 | if (oc->oc_stateowner) |
2235 | nfs4_get_stateowner(oc->oc_stateowner); | 2333 | nfs4_get_stateowner(oc->oc_stateowner); |
@@ -2387,7 +2485,7 @@ static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE]; | |||
2387 | static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; | 2485 | static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; |
2388 | static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE]; | 2486 | static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE]; |
2389 | 2487 | ||
2390 | struct nfs4_stateid * | 2488 | static struct nfs4_stateid * |
2391 | find_stateid(stateid_t *stid, int flags) | 2489 | find_stateid(stateid_t *stid, int flags) |
2392 | { | 2490 | { |
2393 | struct nfs4_stateid *local = NULL; | 2491 | struct nfs4_stateid *local = NULL; |
@@ -2419,25 +2517,19 @@ find_stateid(stateid_t *stid, int flags) | |||
2419 | static struct nfs4_delegation * | 2517 | static struct nfs4_delegation * |
2420 | find_delegation_stateid(struct inode *ino, stateid_t *stid) | 2518 | find_delegation_stateid(struct inode *ino, stateid_t *stid) |
2421 | { | 2519 | { |
2422 | struct nfs4_delegation *dp = NULL; | 2520 | struct nfs4_file *fp; |
2423 | struct nfs4_file *fp = NULL; | 2521 | struct nfs4_delegation *dl; |
2424 | u32 st_id; | ||
2425 | 2522 | ||
2426 | dprintk("NFSD:find_delegation_stateid stateid=(%08x/%08x/%08x/%08x)\n", | 2523 | dprintk("NFSD:find_delegation_stateid stateid=(%08x/%08x/%08x/%08x)\n", |
2427 | stid->si_boot, stid->si_stateownerid, | 2524 | stid->si_boot, stid->si_stateownerid, |
2428 | stid->si_fileid, stid->si_generation); | 2525 | stid->si_fileid, stid->si_generation); |
2429 | 2526 | ||
2430 | st_id = stid->si_stateownerid; | ||
2431 | fp = find_file(ino); | 2527 | fp = find_file(ino); |
2432 | if (fp) { | 2528 | if (!fp) |
2433 | list_for_each_entry(dp, &fp->fi_del_perfile, dl_del_perfile) { | 2529 | return NULL; |
2434 | if(dp->dl_stateid.si_stateownerid == st_id) { | 2530 | dl = find_delegation_file(fp, stid); |
2435 | dprintk("NFSD: find_delegation dp %p\n",dp); | 2531 | put_nfs4_file(fp); |
2436 | return dp; | 2532 | return dl; |
2437 | } | ||
2438 | } | ||
2439 | } | ||
2440 | return NULL; | ||
2441 | } | 2533 | } |
2442 | 2534 | ||
2443 | /* | 2535 | /* |
@@ -2457,7 +2549,7 @@ nfs4_transform_lock_offset(struct file_lock *lock) | |||
2457 | lock->fl_end = OFFSET_MAX; | 2549 | lock->fl_end = OFFSET_MAX; |
2458 | } | 2550 | } |
2459 | 2551 | ||
2460 | int | 2552 | static int |
2461 | nfs4_verify_lock_stateowner(struct nfs4_stateowner *sop, unsigned int hashval) | 2553 | nfs4_verify_lock_stateowner(struct nfs4_stateowner *sop, unsigned int hashval) |
2462 | { | 2554 | { |
2463 | struct nfs4_stateowner *local = NULL; | 2555 | struct nfs4_stateowner *local = NULL; |
@@ -2498,22 +2590,6 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) | |||
2498 | } | 2590 | } |
2499 | 2591 | ||
2500 | static struct nfs4_stateowner * | 2592 | static struct nfs4_stateowner * |
2501 | find_lockstateowner(struct xdr_netobj *owner, clientid_t *clid) | ||
2502 | { | ||
2503 | struct nfs4_stateowner *local = NULL; | ||
2504 | int i; | ||
2505 | |||
2506 | for (i = 0; i < LOCK_HASH_SIZE; i++) { | ||
2507 | list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) { | ||
2508 | if (!cmp_owner_str(local, owner, clid)) | ||
2509 | continue; | ||
2510 | return local; | ||
2511 | } | ||
2512 | } | ||
2513 | return NULL; | ||
2514 | } | ||
2515 | |||
2516 | static struct nfs4_stateowner * | ||
2517 | find_lockstateowner_str(struct inode *inode, clientid_t *clid, | 2593 | find_lockstateowner_str(struct inode *inode, clientid_t *clid, |
2518 | struct xdr_netobj *owner) | 2594 | struct xdr_netobj *owner) |
2519 | { | 2595 | { |
@@ -2548,13 +2624,13 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str | |||
2548 | INIT_LIST_HEAD(&sop->so_idhash); | 2624 | INIT_LIST_HEAD(&sop->so_idhash); |
2549 | INIT_LIST_HEAD(&sop->so_strhash); | 2625 | INIT_LIST_HEAD(&sop->so_strhash); |
2550 | INIT_LIST_HEAD(&sop->so_perclient); | 2626 | INIT_LIST_HEAD(&sop->so_perclient); |
2551 | INIT_LIST_HEAD(&sop->so_perfilestate); | 2627 | INIT_LIST_HEAD(&sop->so_stateids); |
2552 | INIT_LIST_HEAD(&sop->so_perlockowner); | 2628 | INIT_LIST_HEAD(&sop->so_perstateid); |
2553 | INIT_LIST_HEAD(&sop->so_close_lru); /* not used */ | 2629 | INIT_LIST_HEAD(&sop->so_close_lru); /* not used */ |
2554 | sop->so_time = 0; | 2630 | sop->so_time = 0; |
2555 | list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); | 2631 | list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); |
2556 | list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); | 2632 | list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); |
2557 | list_add(&sop->so_perlockowner, &open_stp->st_perlockowner); | 2633 | list_add(&sop->so_perstateid, &open_stp->st_lockowners); |
2558 | sop->so_is_open_owner = 0; | 2634 | sop->so_is_open_owner = 0; |
2559 | sop->so_id = current_ownerid++; | 2635 | sop->so_id = current_ownerid++; |
2560 | sop->so_client = clp; | 2636 | sop->so_client = clp; |
@@ -2567,24 +2643,24 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str | |||
2567 | return sop; | 2643 | return sop; |
2568 | } | 2644 | } |
2569 | 2645 | ||
2570 | struct nfs4_stateid * | 2646 | static struct nfs4_stateid * |
2571 | alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struct nfs4_stateid *open_stp) | 2647 | alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struct nfs4_stateid *open_stp) |
2572 | { | 2648 | { |
2573 | struct nfs4_stateid *stp; | 2649 | struct nfs4_stateid *stp; |
2574 | unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); | 2650 | unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); |
2575 | 2651 | ||
2576 | if ((stp = kmalloc(sizeof(struct nfs4_stateid), | 2652 | stp = nfs4_alloc_stateid(); |
2577 | GFP_KERNEL)) == NULL) | 2653 | if (stp == NULL) |
2578 | goto out; | 2654 | goto out; |
2579 | INIT_LIST_HEAD(&stp->st_hash); | 2655 | INIT_LIST_HEAD(&stp->st_hash); |
2580 | INIT_LIST_HEAD(&stp->st_perfile); | 2656 | INIT_LIST_HEAD(&stp->st_perfile); |
2581 | INIT_LIST_HEAD(&stp->st_perfilestate); | 2657 | INIT_LIST_HEAD(&stp->st_perstateowner); |
2582 | INIT_LIST_HEAD(&stp->st_perlockowner); /* not used */ | 2658 | INIT_LIST_HEAD(&stp->st_lockowners); /* not used */ |
2583 | list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]); | 2659 | list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]); |
2584 | list_add(&stp->st_perfile, &fp->fi_perfile); | 2660 | list_add(&stp->st_perfile, &fp->fi_stateids); |
2585 | list_add_perfile++; | 2661 | list_add(&stp->st_perstateowner, &sop->so_stateids); |
2586 | list_add(&stp->st_perfilestate, &sop->so_perfilestate); | ||
2587 | stp->st_stateowner = sop; | 2662 | stp->st_stateowner = sop; |
2663 | get_nfs4_file(fp); | ||
2588 | stp->st_file = fp; | 2664 | stp->st_file = fp; |
2589 | stp->st_stateid.si_boot = boot_time; | 2665 | stp->st_stateid.si_boot = boot_time; |
2590 | stp->st_stateid.si_stateownerid = sop->so_id; | 2666 | stp->st_stateid.si_stateownerid = sop->so_id; |
@@ -2598,7 +2674,7 @@ out: | |||
2598 | return stp; | 2674 | return stp; |
2599 | } | 2675 | } |
2600 | 2676 | ||
2601 | int | 2677 | static int |
2602 | check_lock_length(u64 offset, u64 length) | 2678 | check_lock_length(u64 offset, u64 length) |
2603 | { | 2679 | { |
2604 | return ((length == 0) || ((length != ~(u64)0) && | 2680 | return ((length == 0) || ((length != ~(u64)0) && |
@@ -2611,7 +2687,7 @@ check_lock_length(u64 offset, u64 length) | |||
2611 | int | 2687 | int |
2612 | nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock *lock) | 2688 | nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock *lock) |
2613 | { | 2689 | { |
2614 | struct nfs4_stateowner *lock_sop = NULL, *open_sop = NULL; | 2690 | struct nfs4_stateowner *open_sop = NULL; |
2615 | struct nfs4_stateid *lock_stp; | 2691 | struct nfs4_stateid *lock_stp; |
2616 | struct file *filp; | 2692 | struct file *filp; |
2617 | struct file_lock file_lock; | 2693 | struct file_lock file_lock; |
@@ -2670,16 +2746,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock | |||
2670 | strhashval = lock_ownerstr_hashval(fp->fi_inode, | 2746 | strhashval = lock_ownerstr_hashval(fp->fi_inode, |
2671 | open_sop->so_client->cl_clientid.cl_id, | 2747 | open_sop->so_client->cl_clientid.cl_id, |
2672 | &lock->v.new.owner); | 2748 | &lock->v.new.owner); |
2673 | /* | 2749 | /* XXX: Do we need to check for duplicate stateowners on |
2674 | * If we already have this lock owner, the client is in | 2750 | * the same file, or should they just be allowed (and |
2675 | * error (or our bookeeping is wrong!) | 2751 | * create new stateids)? */ |
2676 | * for asking for a 'new lock'. | ||
2677 | */ | ||
2678 | status = nfserr_bad_stateid; | ||
2679 | lock_sop = find_lockstateowner(&lock->v.new.owner, | ||
2680 | &lock->v.new.clientid); | ||
2681 | if (lock_sop) | ||
2682 | goto out; | ||
2683 | status = nfserr_resource; | 2752 | status = nfserr_resource; |
2684 | if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock))) | 2753 | if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock))) |
2685 | goto out; | 2754 | goto out; |
@@ -2970,8 +3039,11 @@ int | |||
2970 | nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner) | 3039 | nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner) |
2971 | { | 3040 | { |
2972 | clientid_t *clid = &rlockowner->rl_clientid; | 3041 | clientid_t *clid = &rlockowner->rl_clientid; |
2973 | struct nfs4_stateowner *local = NULL; | 3042 | struct nfs4_stateowner *sop; |
3043 | struct nfs4_stateid *stp; | ||
2974 | struct xdr_netobj *owner = &rlockowner->rl_owner; | 3044 | struct xdr_netobj *owner = &rlockowner->rl_owner; |
3045 | struct list_head matches; | ||
3046 | int i; | ||
2975 | int status; | 3047 | int status; |
2976 | 3048 | ||
2977 | dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", | 3049 | dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", |
@@ -2987,22 +3059,32 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner * | |||
2987 | 3059 | ||
2988 | nfs4_lock_state(); | 3060 | nfs4_lock_state(); |
2989 | 3061 | ||
2990 | status = nfs_ok; | 3062 | status = nfserr_locks_held; |
2991 | local = find_lockstateowner(owner, clid); | 3063 | /* XXX: we're doing a linear search through all the lockowners. |
2992 | if (local) { | 3064 | * Yipes! For now we'll just hope clients aren't really using |
2993 | struct nfs4_stateid *stp; | 3065 | * release_lockowner much, but eventually we have to fix these |
2994 | 3066 | * data structures. */ | |
2995 | /* check for any locks held by any stateid | 3067 | INIT_LIST_HEAD(&matches); |
2996 | * associated with the (lock) stateowner */ | 3068 | for (i = 0; i < LOCK_HASH_SIZE; i++) { |
2997 | status = nfserr_locks_held; | 3069 | list_for_each_entry(sop, &lock_ownerid_hashtbl[i], so_idhash) { |
2998 | list_for_each_entry(stp, &local->so_perfilestate, | 3070 | if (!cmp_owner_str(sop, owner, clid)) |
2999 | st_perfilestate) { | 3071 | continue; |
3000 | if (check_for_locks(stp->st_vfs_file, local)) | 3072 | list_for_each_entry(stp, &sop->so_stateids, |
3001 | goto out; | 3073 | st_perstateowner) { |
3074 | if (check_for_locks(stp->st_vfs_file, sop)) | ||
3075 | goto out; | ||
3076 | /* Note: so_perclient unused for lockowners, | ||
3077 | * so it's OK to fool with here. */ | ||
3078 | list_add(&sop->so_perclient, &matches); | ||
3079 | } | ||
3002 | } | 3080 | } |
3003 | /* no locks held by (lock) stateowner */ | 3081 | } |
3004 | status = nfs_ok; | 3082 | /* Clients probably won't expect us to return with some (but not all) |
3005 | release_stateowner(local); | 3083 | * of the lockowner state released; so don't release any until all |
3084 | * have been checked. */ | ||
3085 | status = nfs_ok; | ||
3086 | list_for_each_entry(sop, &matches, so_perclient) { | ||
3087 | release_stateowner(sop); | ||
3006 | } | 3088 | } |
3007 | out: | 3089 | out: |
3008 | nfs4_unlock_state(); | 3090 | nfs4_unlock_state(); |
@@ -3010,39 +3092,38 @@ out: | |||
3010 | } | 3092 | } |
3011 | 3093 | ||
3012 | static inline struct nfs4_client_reclaim * | 3094 | static inline struct nfs4_client_reclaim * |
3013 | alloc_reclaim(int namelen) | 3095 | alloc_reclaim(void) |
3014 | { | 3096 | { |
3015 | struct nfs4_client_reclaim *crp = NULL; | 3097 | return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); |
3098 | } | ||
3016 | 3099 | ||
3017 | crp = kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); | 3100 | int |
3018 | if (!crp) | 3101 | nfs4_has_reclaimed_state(const char *name) |
3019 | return NULL; | 3102 | { |
3020 | crp->cr_name.data = kmalloc(namelen, GFP_KERNEL); | 3103 | unsigned int strhashval = clientstr_hashval(name); |
3021 | if (!crp->cr_name.data) { | 3104 | struct nfs4_client *clp; |
3022 | kfree(crp); | 3105 | |
3023 | return NULL; | 3106 | clp = find_confirmed_client_by_str(name, strhashval); |
3024 | } | 3107 | return clp ? 1 : 0; |
3025 | return crp; | ||
3026 | } | 3108 | } |
3027 | 3109 | ||
3028 | /* | 3110 | /* |
3029 | * failure => all reset bets are off, nfserr_no_grace... | 3111 | * failure => all reset bets are off, nfserr_no_grace... |
3030 | */ | 3112 | */ |
3031 | static int | 3113 | int |
3032 | nfs4_client_to_reclaim(char *name, int namlen) | 3114 | nfs4_client_to_reclaim(const char *name) |
3033 | { | 3115 | { |
3034 | unsigned int strhashval; | 3116 | unsigned int strhashval; |
3035 | struct nfs4_client_reclaim *crp = NULL; | 3117 | struct nfs4_client_reclaim *crp = NULL; |
3036 | 3118 | ||
3037 | dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", namlen, name); | 3119 | dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name); |
3038 | crp = alloc_reclaim(namlen); | 3120 | crp = alloc_reclaim(); |
3039 | if (!crp) | 3121 | if (!crp) |
3040 | return 0; | 3122 | return 0; |
3041 | strhashval = clientstr_hashval(name, namlen); | 3123 | strhashval = clientstr_hashval(name); |
3042 | INIT_LIST_HEAD(&crp->cr_strhash); | 3124 | INIT_LIST_HEAD(&crp->cr_strhash); |
3043 | list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); | 3125 | list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); |
3044 | memcpy(crp->cr_name.data, name, namlen); | 3126 | memcpy(crp->cr_recdir, name, HEXDIR_LEN); |
3045 | crp->cr_name.len = namlen; | ||
3046 | reclaim_str_hashtbl_size++; | 3127 | reclaim_str_hashtbl_size++; |
3047 | return 1; | 3128 | return 1; |
3048 | } | 3129 | } |
@@ -3053,13 +3134,11 @@ nfs4_release_reclaim(void) | |||
3053 | struct nfs4_client_reclaim *crp = NULL; | 3134 | struct nfs4_client_reclaim *crp = NULL; |
3054 | int i; | 3135 | int i; |
3055 | 3136 | ||
3056 | BUG_ON(!nfs4_reclaim_init); | ||
3057 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | 3137 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { |
3058 | while (!list_empty(&reclaim_str_hashtbl[i])) { | 3138 | while (!list_empty(&reclaim_str_hashtbl[i])) { |
3059 | crp = list_entry(reclaim_str_hashtbl[i].next, | 3139 | crp = list_entry(reclaim_str_hashtbl[i].next, |
3060 | struct nfs4_client_reclaim, cr_strhash); | 3140 | struct nfs4_client_reclaim, cr_strhash); |
3061 | list_del(&crp->cr_strhash); | 3141 | list_del(&crp->cr_strhash); |
3062 | kfree(crp->cr_name.data); | ||
3063 | kfree(crp); | 3142 | kfree(crp); |
3064 | reclaim_str_hashtbl_size--; | 3143 | reclaim_str_hashtbl_size--; |
3065 | } | 3144 | } |
@@ -3069,7 +3148,7 @@ nfs4_release_reclaim(void) | |||
3069 | 3148 | ||
3070 | /* | 3149 | /* |
3071 | * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ | 3150 | * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ |
3072 | struct nfs4_client_reclaim * | 3151 | static struct nfs4_client_reclaim * |
3073 | nfs4_find_reclaim_client(clientid_t *clid) | 3152 | nfs4_find_reclaim_client(clientid_t *clid) |
3074 | { | 3153 | { |
3075 | unsigned int strhashval; | 3154 | unsigned int strhashval; |
@@ -3082,13 +3161,14 @@ nfs4_find_reclaim_client(clientid_t *clid) | |||
3082 | if (clp == NULL) | 3161 | if (clp == NULL) |
3083 | return NULL; | 3162 | return NULL; |
3084 | 3163 | ||
3085 | dprintk("NFSD: nfs4_find_reclaim_client for %.*s\n", | 3164 | dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n", |
3086 | clp->cl_name.len, clp->cl_name.data); | 3165 | clp->cl_name.len, clp->cl_name.data, |
3166 | clp->cl_recdir); | ||
3087 | 3167 | ||
3088 | /* find clp->cl_name in reclaim_str_hashtbl */ | 3168 | /* find clp->cl_name in reclaim_str_hashtbl */ |
3089 | strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len); | 3169 | strhashval = clientstr_hashval(clp->cl_recdir); |
3090 | list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { | 3170 | list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { |
3091 | if (cmp_name(&crp->cr_name, &clp->cl_name)) { | 3171 | if (same_name(crp->cr_recdir, clp->cl_recdir)) { |
3092 | return crp; | 3172 | return crp; |
3093 | } | 3173 | } |
3094 | } | 3174 | } |
@@ -3101,30 +3181,16 @@ nfs4_find_reclaim_client(clientid_t *clid) | |||
3101 | int | 3181 | int |
3102 | nfs4_check_open_reclaim(clientid_t *clid) | 3182 | nfs4_check_open_reclaim(clientid_t *clid) |
3103 | { | 3183 | { |
3104 | struct nfs4_client_reclaim *crp; | 3184 | return nfs4_find_reclaim_client(clid) ? nfs_ok : nfserr_reclaim_bad; |
3105 | |||
3106 | if ((crp = nfs4_find_reclaim_client(clid)) == NULL) | ||
3107 | return nfserr_reclaim_bad; | ||
3108 | return nfs_ok; | ||
3109 | } | 3185 | } |
3110 | 3186 | ||
3187 | /* initialization to perform at module load time: */ | ||
3111 | 3188 | ||
3112 | /* | 3189 | void |
3113 | * Start and stop routines | 3190 | nfs4_state_init(void) |
3114 | */ | ||
3115 | |||
3116 | static void | ||
3117 | __nfs4_state_init(void) | ||
3118 | { | 3191 | { |
3119 | int i; | 3192 | int i; |
3120 | time_t grace_time; | ||
3121 | 3193 | ||
3122 | if (!nfs4_reclaim_init) { | ||
3123 | for (i = 0; i < CLIENT_HASH_SIZE; i++) | ||
3124 | INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); | ||
3125 | reclaim_str_hashtbl_size = 0; | ||
3126 | nfs4_reclaim_init = 1; | ||
3127 | } | ||
3128 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | 3194 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { |
3129 | INIT_LIST_HEAD(&conf_id_hashtbl[i]); | 3195 | INIT_LIST_HEAD(&conf_id_hashtbl[i]); |
3130 | INIT_LIST_HEAD(&conf_str_hashtbl[i]); | 3196 | INIT_LIST_HEAD(&conf_str_hashtbl[i]); |
@@ -3146,26 +3212,46 @@ __nfs4_state_init(void) | |||
3146 | INIT_LIST_HEAD(&lock_ownerid_hashtbl[i]); | 3212 | INIT_LIST_HEAD(&lock_ownerid_hashtbl[i]); |
3147 | INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); | 3213 | INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); |
3148 | } | 3214 | } |
3149 | memset(&zerostateid, 0, sizeof(stateid_t)); | ||
3150 | memset(&onestateid, ~0, sizeof(stateid_t)); | 3215 | memset(&onestateid, ~0, sizeof(stateid_t)); |
3151 | |||
3152 | INIT_LIST_HEAD(&close_lru); | 3216 | INIT_LIST_HEAD(&close_lru); |
3153 | INIT_LIST_HEAD(&client_lru); | 3217 | INIT_LIST_HEAD(&client_lru); |
3154 | INIT_LIST_HEAD(&del_recall_lru); | 3218 | INIT_LIST_HEAD(&del_recall_lru); |
3155 | spin_lock_init(&recall_lock); | 3219 | for (i = 0; i < CLIENT_HASH_SIZE; i++) |
3220 | INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); | ||
3221 | reclaim_str_hashtbl_size = 0; | ||
3222 | } | ||
3223 | |||
3224 | static void | ||
3225 | nfsd4_load_reboot_recovery_data(void) | ||
3226 | { | ||
3227 | int status; | ||
3228 | |||
3229 | nfs4_lock_state(); | ||
3230 | nfsd4_init_recdir(user_recovery_dirname); | ||
3231 | status = nfsd4_recdir_load(); | ||
3232 | nfs4_unlock_state(); | ||
3233 | if (status) | ||
3234 | printk("NFSD: Failure reading reboot recovery data\n"); | ||
3235 | } | ||
3236 | |||
3237 | /* initialization to perform when the nfsd service is started: */ | ||
3238 | |||
3239 | static void | ||
3240 | __nfs4_state_start(void) | ||
3241 | { | ||
3242 | time_t grace_time; | ||
3243 | |||
3156 | boot_time = get_seconds(); | 3244 | boot_time = get_seconds(); |
3157 | grace_time = max(old_lease_time, lease_time); | 3245 | grace_time = max(user_lease_time, lease_time); |
3158 | if (reclaim_str_hashtbl_size == 0) | 3246 | lease_time = user_lease_time; |
3159 | grace_time = 0; | 3247 | in_grace = 1; |
3160 | if (grace_time) | 3248 | printk("NFSD: starting %ld-second grace period\n", grace_time); |
3161 | printk("NFSD: starting %ld-second grace period\n", grace_time); | 3249 | laundry_wq = create_singlethread_workqueue("nfsd4"); |
3162 | grace_end = boot_time + grace_time; | 3250 | queue_delayed_work(laundry_wq, &laundromat_work, grace_time*HZ); |
3163 | INIT_WORK(&laundromat_work,laundromat_main, NULL); | ||
3164 | schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ); | ||
3165 | } | 3251 | } |
3166 | 3252 | ||
3167 | int | 3253 | int |
3168 | nfs4_state_init(void) | 3254 | nfs4_state_start(void) |
3169 | { | 3255 | { |
3170 | int status; | 3256 | int status; |
3171 | 3257 | ||
@@ -3174,7 +3260,8 @@ nfs4_state_init(void) | |||
3174 | status = nfsd4_init_slabs(); | 3260 | status = nfsd4_init_slabs(); |
3175 | if (status) | 3261 | if (status) |
3176 | return status; | 3262 | return status; |
3177 | __nfs4_state_init(); | 3263 | nfsd4_load_reboot_recovery_data(); |
3264 | __nfs4_state_start(); | ||
3178 | nfs4_init = 1; | 3265 | nfs4_init = 1; |
3179 | return 0; | 3266 | return 0; |
3180 | } | 3267 | } |
@@ -3182,14 +3269,7 @@ nfs4_state_init(void) | |||
3182 | int | 3269 | int |
3183 | nfs4_in_grace(void) | 3270 | nfs4_in_grace(void) |
3184 | { | 3271 | { |
3185 | return get_seconds() < grace_end; | 3272 | return in_grace; |
3186 | } | ||
3187 | |||
3188 | void | ||
3189 | set_no_grace(void) | ||
3190 | { | ||
3191 | printk("NFSD: ERROR in reboot recovery. State reclaims will fail.\n"); | ||
3192 | grace_end = get_seconds(); | ||
3193 | } | 3273 | } |
3194 | 3274 | ||
3195 | time_t | 3275 | time_t |
@@ -3236,21 +3316,11 @@ __nfs4_state_shutdown(void) | |||
3236 | unhash_delegation(dp); | 3316 | unhash_delegation(dp); |
3237 | } | 3317 | } |
3238 | 3318 | ||
3239 | release_all_files(); | ||
3240 | cancel_delayed_work(&laundromat_work); | 3319 | cancel_delayed_work(&laundromat_work); |
3241 | flush_scheduled_work(); | 3320 | flush_workqueue(laundry_wq); |
3321 | destroy_workqueue(laundry_wq); | ||
3322 | nfsd4_shutdown_recdir(); | ||
3242 | nfs4_init = 0; | 3323 | nfs4_init = 0; |
3243 | dprintk("NFSD: list_add_perfile %d list_del_perfile %d\n", | ||
3244 | list_add_perfile, list_del_perfile); | ||
3245 | dprintk("NFSD: add_perclient %d del_perclient %d\n", | ||
3246 | add_perclient, del_perclient); | ||
3247 | dprintk("NFSD: alloc_file %d free_file %d\n", | ||
3248 | alloc_file, free_file); | ||
3249 | dprintk("NFSD: vfsopen %d vfsclose %d\n", | ||
3250 | vfsopen, vfsclose); | ||
3251 | dprintk("NFSD: alloc_delegation %d free_delegation %d\n", | ||
3252 | alloc_delegation, free_delegation); | ||
3253 | |||
3254 | } | 3324 | } |
3255 | 3325 | ||
3256 | void | 3326 | void |
@@ -3263,56 +3333,48 @@ nfs4_state_shutdown(void) | |||
3263 | nfs4_unlock_state(); | 3333 | nfs4_unlock_state(); |
3264 | } | 3334 | } |
3265 | 3335 | ||
3336 | static void | ||
3337 | nfs4_set_recdir(char *recdir) | ||
3338 | { | ||
3339 | nfs4_lock_state(); | ||
3340 | strcpy(user_recovery_dirname, recdir); | ||
3341 | nfs4_unlock_state(); | ||
3342 | } | ||
3343 | |||
3344 | /* | ||
3345 | * Change the NFSv4 recovery directory to recdir. | ||
3346 | */ | ||
3347 | int | ||
3348 | nfs4_reset_recoverydir(char *recdir) | ||
3349 | { | ||
3350 | int status; | ||
3351 | struct nameidata nd; | ||
3352 | |||
3353 | status = path_lookup(recdir, LOOKUP_FOLLOW, &nd); | ||
3354 | if (status) | ||
3355 | return status; | ||
3356 | status = -ENOTDIR; | ||
3357 | if (S_ISDIR(nd.dentry->d_inode->i_mode)) { | ||
3358 | nfs4_set_recdir(recdir); | ||
3359 | status = 0; | ||
3360 | } | ||
3361 | path_release(&nd); | ||
3362 | return status; | ||
3363 | } | ||
3364 | |||
3266 | /* | 3365 | /* |
3267 | * Called when leasetime is changed. | 3366 | * Called when leasetime is changed. |
3268 | * | 3367 | * |
3269 | * if nfsd is not started, simply set the global lease. | 3368 | * The only way the protocol gives us to handle on-the-fly lease changes is to |
3270 | * | 3369 | * simulate a reboot. Instead of doing that, we just wait till the next time |
3271 | * if nfsd(s) are running, lease change requires nfsv4 state to be reset. | 3370 | * we start to register any changes in lease time. If the administrator |
3272 | * e.g: boot_time is reset, existing nfs4_client structs are | 3371 | * really wants to change the lease time *now*, they can go ahead and bring |
3273 | * used to fill reclaim_str_hashtbl, then all state (except for the | 3372 | * nfsd down and then back up again after changing the lease time. |
3274 | * reclaim_str_hashtbl) is re-initialized. | ||
3275 | * | ||
3276 | * if the old lease time is greater than the new lease time, the grace | ||
3277 | * period needs to be set to the old lease time to allow clients to reclaim | ||
3278 | * their state. XXX - we may want to set the grace period == lease time | ||
3279 | * after an initial grace period == old lease time | ||
3280 | * | ||
3281 | * if an error occurs in this process, the new lease is set, but the server | ||
3282 | * will not honor OPEN or LOCK reclaims, and will return nfserr_no_grace | ||
3283 | * which means OPEN/LOCK/READ/WRITE will fail during grace period. | ||
3284 | * | ||
3285 | * clients will attempt to reset all state with SETCLIENTID/CONFIRM, and | ||
3286 | * OPEN and LOCK reclaims. | ||
3287 | */ | 3373 | */ |
3288 | void | 3374 | void |
3289 | nfs4_reset_lease(time_t leasetime) | 3375 | nfs4_reset_lease(time_t leasetime) |
3290 | { | 3376 | { |
3291 | struct nfs4_client *clp; | 3377 | lock_kernel(); |
3292 | int i; | 3378 | user_lease_time = leasetime; |
3293 | 3379 | unlock_kernel(); | |
3294 | printk("NFSD: New leasetime %ld\n",leasetime); | ||
3295 | if (!nfs4_init) | ||
3296 | return; | ||
3297 | nfs4_lock_state(); | ||
3298 | old_lease_time = lease_time; | ||
3299 | lease_time = leasetime; | ||
3300 | |||
3301 | nfs4_release_reclaim(); | ||
3302 | |||
3303 | /* populate reclaim_str_hashtbl with current confirmed nfs4_clientid */ | ||
3304 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | ||
3305 | list_for_each_entry(clp, &conf_id_hashtbl[i], cl_idhash) { | ||
3306 | if (!nfs4_client_to_reclaim(clp->cl_name.data, | ||
3307 | clp->cl_name.len)) { | ||
3308 | nfs4_release_reclaim(); | ||
3309 | goto init_state; | ||
3310 | } | ||
3311 | } | ||
3312 | } | ||
3313 | init_state: | ||
3314 | __nfs4_state_shutdown(); | ||
3315 | __nfs4_state_init(); | ||
3316 | nfs4_unlock_state(); | ||
3317 | } | 3380 | } |
3318 | |||