diff options
author | NeilBrown <neilb@cse.unsw.edu.au> | 2005-06-24 01:04:30 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-24 03:06:36 -0400 |
commit | c7b9a45927e74c81d6562153f7fde9d32da00159 (patch) | |
tree | d21fb5d43052bce7469c168d1ad485c821a42079 | |
parent | 190e4fbf96037e5e526ba3210f2bcc2a3b6fe964 (diff) |
[PATCH] knfsd: nfsd4: reboot recovery
This patch adds the code to create and remove client subdirectories from the
recovery directory, as described in the previous patch comment.
Signed-off-by: Andy Adamson <andros@citi.umich.edu>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | fs/nfsd/nfs4recover.c | 169 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 16 | ||||
-rw-r--r-- | include/linux/nfsd/state.h | 5 |
3 files changed, 190 insertions, 0 deletions
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 2dc9851a1d37..2805c5245eac 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c | |||
@@ -120,6 +120,70 @@ out: | |||
120 | return status; | 120 | return status; |
121 | } | 121 | } |
122 | 122 | ||
123 | static int | ||
124 | nfsd4_rec_fsync(struct dentry *dentry) | ||
125 | { | ||
126 | struct file *filp; | ||
127 | int status = nfs_ok; | ||
128 | |||
129 | dprintk("NFSD: nfs4_fsync_rec_dir\n"); | ||
130 | filp = dentry_open(dget(dentry), mntget(rec_dir.mnt), O_RDWR); | ||
131 | if (IS_ERR(filp)) { | ||
132 | status = PTR_ERR(filp); | ||
133 | goto out; | ||
134 | } | ||
135 | if (filp->f_op && filp->f_op->fsync) | ||
136 | status = filp->f_op->fsync(filp, filp->f_dentry, 0); | ||
137 | fput(filp); | ||
138 | out: | ||
139 | if (status) | ||
140 | printk("nfsd4: unable to sync recovery directory\n"); | ||
141 | return status; | ||
142 | } | ||
143 | |||
144 | int | ||
145 | nfsd4_create_clid_dir(struct nfs4_client *clp) | ||
146 | { | ||
147 | char *dname = clp->cl_recdir; | ||
148 | struct dentry *dentry; | ||
149 | uid_t uid; | ||
150 | gid_t gid; | ||
151 | int status; | ||
152 | |||
153 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); | ||
154 | |||
155 | if (!rec_dir_init || clp->cl_firststate) | ||
156 | return 0; | ||
157 | |||
158 | nfs4_save_user(&uid, &gid); | ||
159 | |||
160 | /* lock the parent */ | ||
161 | down(&rec_dir.dentry->d_inode->i_sem); | ||
162 | |||
163 | dentry = lookup_one_len(dname, rec_dir.dentry, HEXDIR_LEN-1); | ||
164 | if (IS_ERR(dentry)) { | ||
165 | status = PTR_ERR(dentry); | ||
166 | goto out_unlock; | ||
167 | } | ||
168 | status = -EEXIST; | ||
169 | if (dentry->d_inode) { | ||
170 | dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n"); | ||
171 | goto out_put; | ||
172 | } | ||
173 | status = vfs_mkdir(rec_dir.dentry->d_inode, dentry, S_IRWXU); | ||
174 | out_put: | ||
175 | dput(dentry); | ||
176 | out_unlock: | ||
177 | up(&rec_dir.dentry->d_inode->i_sem); | ||
178 | if (status == 0) { | ||
179 | clp->cl_firststate = 1; | ||
180 | status = nfsd4_rec_fsync(rec_dir.dentry); | ||
181 | } | ||
182 | nfs4_reset_user(uid, gid); | ||
183 | dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status); | ||
184 | return status; | ||
185 | } | ||
186 | |||
123 | typedef int (recdir_func)(struct dentry *, struct dentry *); | 187 | typedef int (recdir_func)(struct dentry *, struct dentry *); |
124 | 188 | ||
125 | struct dentry_list { | 189 | struct dentry_list { |
@@ -202,6 +266,111 @@ out: | |||
202 | } | 266 | } |
203 | 267 | ||
204 | static int | 268 | static int |
269 | nfsd4_remove_clid_file(struct dentry *dir, struct dentry *dentry) | ||
270 | { | ||
271 | int status; | ||
272 | |||
273 | if (!S_ISREG(dir->d_inode->i_mode)) { | ||
274 | printk("nfsd4: non-file found in client recovery directory\n"); | ||
275 | return -EINVAL; | ||
276 | } | ||
277 | down(&dir->d_inode->i_sem); | ||
278 | status = vfs_unlink(dir->d_inode, dentry); | ||
279 | up(&dir->d_inode->i_sem); | ||
280 | return status; | ||
281 | } | ||
282 | |||
283 | static int | ||
284 | nfsd4_clear_clid_dir(struct dentry *dir, struct dentry *dentry) | ||
285 | { | ||
286 | int status; | ||
287 | |||
288 | /* For now this directory should already be empty, but we empty it of | ||
289 | * any regular files anyway, just in case the directory was created by | ||
290 | * a kernel from the future.... */ | ||
291 | nfsd4_list_rec_dir(dentry, nfsd4_remove_clid_file); | ||
292 | down(&dir->d_inode->i_sem); | ||
293 | status = vfs_rmdir(dir->d_inode, dentry); | ||
294 | up(&dir->d_inode->i_sem); | ||
295 | return status; | ||
296 | } | ||
297 | |||
298 | static int | ||
299 | nfsd4_unlink_clid_dir(char *name, int namlen) | ||
300 | { | ||
301 | struct dentry *dentry; | ||
302 | int status; | ||
303 | |||
304 | dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name); | ||
305 | |||
306 | dentry = lookup_one_len(name, rec_dir.dentry, namlen); | ||
307 | if (IS_ERR(dentry)) { | ||
308 | status = PTR_ERR(dentry); | ||
309 | return status; | ||
310 | } | ||
311 | status = -ENOENT; | ||
312 | if (!dentry->d_inode) | ||
313 | goto out; | ||
314 | |||
315 | status = nfsd4_clear_clid_dir(rec_dir.dentry, dentry); | ||
316 | out: | ||
317 | dput(dentry); | ||
318 | return status; | ||
319 | } | ||
320 | |||
321 | void | ||
322 | nfsd4_remove_clid_dir(struct nfs4_client *clp) | ||
323 | { | ||
324 | uid_t uid; | ||
325 | gid_t gid; | ||
326 | int status; | ||
327 | |||
328 | if (!rec_dir_init || !clp->cl_firststate) | ||
329 | return; | ||
330 | |||
331 | nfs4_save_user(&uid, &gid); | ||
332 | status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1); | ||
333 | nfs4_reset_user(uid, gid); | ||
334 | if (status == 0) | ||
335 | status = nfsd4_rec_fsync(rec_dir.dentry); | ||
336 | if (status) | ||
337 | printk("NFSD: Failed to remove expired client state directory" | ||
338 | " %.*s\n", HEXDIR_LEN, clp->cl_recdir); | ||
339 | return; | ||
340 | } | ||
341 | |||
342 | static int | ||
343 | purge_old(struct dentry *parent, struct dentry *child) | ||
344 | { | ||
345 | int status; | ||
346 | |||
347 | if (nfs4_has_reclaimed_state(child->d_name.name)) | ||
348 | return nfs_ok; | ||
349 | |||
350 | status = nfsd4_clear_clid_dir(parent, child); | ||
351 | if (status) | ||
352 | printk("failed to remove client recovery directory %s\n", | ||
353 | child->d_name.name); | ||
354 | /* Keep trying, success or failure: */ | ||
355 | return nfs_ok; | ||
356 | } | ||
357 | |||
358 | void | ||
359 | nfsd4_recdir_purge_old(void) { | ||
360 | int status; | ||
361 | |||
362 | if (!rec_dir_init) | ||
363 | return; | ||
364 | status = nfsd4_list_rec_dir(rec_dir.dentry, purge_old); | ||
365 | if (status == 0) | ||
366 | status = nfsd4_rec_fsync(rec_dir.dentry); | ||
367 | if (status) | ||
368 | printk("nfsd4: failed to purge old clients from recovery" | ||
369 | " directory %s\n", rec_dir.dentry->d_name.name); | ||
370 | return; | ||
371 | } | ||
372 | |||
373 | static int | ||
205 | load_recdir(struct dentry *parent, struct dentry *child) | 374 | load_recdir(struct dentry *parent, struct dentry *child) |
206 | { | 375 | { |
207 | if (child->d_name.len != HEXDIR_LEN - 1) { | 376 | if (child->d_name.len != HEXDIR_LEN - 1) { |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6b9d23c39afe..6cca358cd650 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -905,6 +905,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi | |||
905 | conf = find_confirmed_client_by_str(unconf->cl_recdir, | 905 | conf = find_confirmed_client_by_str(unconf->cl_recdir, |
906 | hash); | 906 | hash); |
907 | if (conf) { | 907 | if (conf) { |
908 | nfsd4_remove_clid_dir(conf); | ||
908 | expire_client(conf); | 909 | expire_client(conf); |
909 | } | 910 | } |
910 | move_to_confirmed(unconf); | 911 | move_to_confirmed(unconf); |
@@ -1691,6 +1692,7 @@ nfs4_set_claim_prev(struct nfsd4_open *open, int *status) | |||
1691 | *status = nfserr_reclaim_bad; | 1692 | *status = nfserr_reclaim_bad; |
1692 | else { | 1693 | else { |
1693 | open->op_stateowner->so_confirmed = 1; | 1694 | open->op_stateowner->so_confirmed = 1; |
1695 | open->op_stateowner->so_client->cl_firststate = 1; | ||
1694 | open->op_stateowner->so_seqid--; | 1696 | open->op_stateowner->so_seqid--; |
1695 | } | 1697 | } |
1696 | } | 1698 | } |
@@ -1903,6 +1905,7 @@ static void | |||
1903 | end_grace(void) | 1905 | end_grace(void) |
1904 | { | 1906 | { |
1905 | dprintk("NFSD: end of grace period\n"); | 1907 | dprintk("NFSD: end of grace period\n"); |
1908 | nfsd4_recdir_purge_old(); | ||
1906 | in_grace = 0; | 1909 | in_grace = 0; |
1907 | } | 1910 | } |
1908 | 1911 | ||
@@ -1932,6 +1935,7 @@ nfs4_laundromat(void) | |||
1932 | } | 1935 | } |
1933 | dprintk("NFSD: purging unused client (clientid %08x)\n", | 1936 | dprintk("NFSD: purging unused client (clientid %08x)\n", |
1934 | clp->cl_clientid.cl_id); | 1937 | clp->cl_clientid.cl_id); |
1938 | nfsd4_remove_clid_dir(clp); | ||
1935 | expire_client(clp); | 1939 | expire_client(clp); |
1936 | } | 1940 | } |
1937 | INIT_LIST_HEAD(&reaplist); | 1941 | INIT_LIST_HEAD(&reaplist); |
@@ -2320,6 +2324,8 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs | |||
2320 | stp->st_stateid.si_stateownerid, | 2324 | stp->st_stateid.si_stateownerid, |
2321 | stp->st_stateid.si_fileid, | 2325 | stp->st_stateid.si_fileid, |
2322 | stp->st_stateid.si_generation); | 2326 | stp->st_stateid.si_generation); |
2327 | |||
2328 | nfsd4_create_clid_dir(sop->so_client); | ||
2323 | out: | 2329 | out: |
2324 | if (oc->oc_stateowner) | 2330 | if (oc->oc_stateowner) |
2325 | nfs4_get_stateowner(oc->oc_stateowner); | 2331 | nfs4_get_stateowner(oc->oc_stateowner); |
@@ -3089,6 +3095,16 @@ alloc_reclaim(void) | |||
3089 | return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); | 3095 | return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); |
3090 | } | 3096 | } |
3091 | 3097 | ||
3098 | int | ||
3099 | nfs4_has_reclaimed_state(const char *name) | ||
3100 | { | ||
3101 | unsigned int strhashval = clientstr_hashval(name); | ||
3102 | struct nfs4_client *clp; | ||
3103 | |||
3104 | clp = find_confirmed_client_by_str(name, strhashval); | ||
3105 | return clp ? 1 : 0; | ||
3106 | } | ||
3107 | |||
3092 | /* | 3108 | /* |
3093 | * failure => all reset bets are off, nfserr_no_grace... | 3109 | * failure => all reset bets are off, nfserr_no_grace... |
3094 | */ | 3110 | */ |
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index 19481ab122df..a84a3fa99be1 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h | |||
@@ -131,6 +131,7 @@ struct nfs4_client { | |||
131 | nfs4_verifier cl_confirm; /* generated by server */ | 131 | nfs4_verifier cl_confirm; /* generated by server */ |
132 | struct nfs4_callback cl_callback; /* callback info */ | 132 | struct nfs4_callback cl_callback; /* callback info */ |
133 | atomic_t cl_count; /* ref count */ | 133 | atomic_t cl_count; /* ref count */ |
134 | u32 cl_firststate; /* recovery dir creation */ | ||
134 | }; | 135 | }; |
135 | 136 | ||
136 | /* struct nfs4_client_reset | 137 | /* struct nfs4_client_reset |
@@ -282,6 +283,10 @@ extern void nfsd4_init_recdir(char *recdir_name); | |||
282 | extern int nfsd4_recdir_load(void); | 283 | extern int nfsd4_recdir_load(void); |
283 | extern void nfsd4_shutdown_recdir(void); | 284 | extern void nfsd4_shutdown_recdir(void); |
284 | extern int nfs4_client_to_reclaim(const char *name); | 285 | extern int nfs4_client_to_reclaim(const char *name); |
286 | extern int nfs4_has_reclaimed_state(const char *name); | ||
287 | extern void nfsd4_recdir_purge_old(void); | ||
288 | extern int nfsd4_create_clid_dir(struct nfs4_client *clp); | ||
289 | extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); | ||
285 | 290 | ||
286 | static inline void | 291 | static inline void |
287 | nfs4_put_stateowner(struct nfs4_stateowner *so) | 292 | nfs4_put_stateowner(struct nfs4_stateowner *so) |