diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /fs/nfs/unlink.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'fs/nfs/unlink.c')
-rw-r--r-- | fs/nfs/unlink.c | 281 |
1 files changed, 278 insertions, 3 deletions
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 2f84adaad427..8d6864c2a5fa 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c | |||
@@ -13,9 +13,12 @@ | |||
13 | #include <linux/nfs_fs.h> | 13 | #include <linux/nfs_fs.h> |
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/wait.h> | 15 | #include <linux/wait.h> |
16 | #include <linux/namei.h> | ||
16 | 17 | ||
17 | #include "internal.h" | 18 | #include "internal.h" |
18 | #include "nfs4_fs.h" | 19 | #include "nfs4_fs.h" |
20 | #include "iostat.h" | ||
21 | #include "delegation.h" | ||
19 | 22 | ||
20 | struct nfs_unlinkdata { | 23 | struct nfs_unlinkdata { |
21 | struct hlist_node list; | 24 | struct hlist_node list; |
@@ -145,6 +148,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n | |||
145 | alias = d_lookup(parent, &data->args.name); | 148 | alias = d_lookup(parent, &data->args.name); |
146 | if (alias != NULL) { | 149 | if (alias != NULL) { |
147 | int ret = 0; | 150 | int ret = 0; |
151 | void *devname_garbage = NULL; | ||
148 | 152 | ||
149 | /* | 153 | /* |
150 | * Hey, we raced with lookup... See if we need to transfer | 154 | * Hey, we raced with lookup... See if we need to transfer |
@@ -154,6 +158,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n | |||
154 | spin_lock(&alias->d_lock); | 158 | spin_lock(&alias->d_lock); |
155 | if (alias->d_inode != NULL && | 159 | if (alias->d_inode != NULL && |
156 | !(alias->d_flags & DCACHE_NFSFS_RENAMED)) { | 160 | !(alias->d_flags & DCACHE_NFSFS_RENAMED)) { |
161 | devname_garbage = alias->d_fsdata; | ||
157 | alias->d_fsdata = data; | 162 | alias->d_fsdata = data; |
158 | alias->d_flags |= DCACHE_NFSFS_RENAMED; | 163 | alias->d_flags |= DCACHE_NFSFS_RENAMED; |
159 | ret = 1; | 164 | ret = 1; |
@@ -161,6 +166,13 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n | |||
161 | spin_unlock(&alias->d_lock); | 166 | spin_unlock(&alias->d_lock); |
162 | nfs_dec_sillycount(dir); | 167 | nfs_dec_sillycount(dir); |
163 | dput(alias); | 168 | dput(alias); |
169 | /* | ||
170 | * If we'd displaced old cached devname, free it. At that | ||
171 | * point dentry is definitely not a root, so we won't need | ||
172 | * that anymore. | ||
173 | */ | ||
174 | if (devname_garbage) | ||
175 | kfree(devname_garbage); | ||
164 | return ret; | 176 | return ret; |
165 | } | 177 | } |
166 | data->dir = igrab(dir); | 178 | data->dir = igrab(dir); |
@@ -177,7 +189,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n | |||
177 | task_setup_data.rpc_client = NFS_CLIENT(dir); | 189 | task_setup_data.rpc_client = NFS_CLIENT(dir); |
178 | task = rpc_run_task(&task_setup_data); | 190 | task = rpc_run_task(&task_setup_data); |
179 | if (!IS_ERR(task)) | 191 | if (!IS_ERR(task)) |
180 | rpc_put_task(task); | 192 | rpc_put_task_async(task); |
181 | return 1; | 193 | return 1; |
182 | } | 194 | } |
183 | 195 | ||
@@ -244,11 +256,12 @@ void nfs_unblock_sillyrename(struct dentry *dentry) | |||
244 | * @dir: parent directory of dentry | 256 | * @dir: parent directory of dentry |
245 | * @dentry: dentry to unlink | 257 | * @dentry: dentry to unlink |
246 | */ | 258 | */ |
247 | int | 259 | static int |
248 | nfs_async_unlink(struct inode *dir, struct dentry *dentry) | 260 | nfs_async_unlink(struct inode *dir, struct dentry *dentry) |
249 | { | 261 | { |
250 | struct nfs_unlinkdata *data; | 262 | struct nfs_unlinkdata *data; |
251 | int status = -ENOMEM; | 263 | int status = -ENOMEM; |
264 | void *devname_garbage = NULL; | ||
252 | 265 | ||
253 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 266 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
254 | if (data == NULL) | 267 | if (data == NULL) |
@@ -259,7 +272,6 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) | |||
259 | status = PTR_ERR(data->cred); | 272 | status = PTR_ERR(data->cred); |
260 | goto out_free; | 273 | goto out_free; |
261 | } | 274 | } |
262 | data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
263 | data->res.dir_attr = &data->dir_attr; | 275 | data->res.dir_attr = &data->dir_attr; |
264 | 276 | ||
265 | status = -EBUSY; | 277 | status = -EBUSY; |
@@ -267,8 +279,16 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) | |||
267 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | 279 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) |
268 | goto out_unlock; | 280 | goto out_unlock; |
269 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; | 281 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; |
282 | devname_garbage = dentry->d_fsdata; | ||
270 | dentry->d_fsdata = data; | 283 | dentry->d_fsdata = data; |
271 | spin_unlock(&dentry->d_lock); | 284 | spin_unlock(&dentry->d_lock); |
285 | /* | ||
286 | * If we'd displaced old cached devname, free it. At that | ||
287 | * point dentry is definitely not a root, so we won't need | ||
288 | * that anymore. | ||
289 | */ | ||
290 | if (devname_garbage) | ||
291 | kfree(devname_garbage); | ||
272 | return 0; | 292 | return 0; |
273 | out_unlock: | 293 | out_unlock: |
274 | spin_unlock(&dentry->d_lock); | 294 | spin_unlock(&dentry->d_lock); |
@@ -297,9 +317,264 @@ nfs_complete_unlink(struct dentry *dentry, struct inode *inode) | |||
297 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { | 317 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { |
298 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; | 318 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; |
299 | data = dentry->d_fsdata; | 319 | data = dentry->d_fsdata; |
320 | dentry->d_fsdata = NULL; | ||
300 | } | 321 | } |
301 | spin_unlock(&dentry->d_lock); | 322 | spin_unlock(&dentry->d_lock); |
302 | 323 | ||
303 | if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) | 324 | if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) |
304 | nfs_free_unlinkdata(data); | 325 | nfs_free_unlinkdata(data); |
305 | } | 326 | } |
327 | |||
328 | /* Cancel a queued async unlink. Called when a sillyrename run fails. */ | ||
329 | static void | ||
330 | nfs_cancel_async_unlink(struct dentry *dentry) | ||
331 | { | ||
332 | spin_lock(&dentry->d_lock); | ||
333 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { | ||
334 | struct nfs_unlinkdata *data = dentry->d_fsdata; | ||
335 | |||
336 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; | ||
337 | dentry->d_fsdata = NULL; | ||
338 | spin_unlock(&dentry->d_lock); | ||
339 | nfs_free_unlinkdata(data); | ||
340 | return; | ||
341 | } | ||
342 | spin_unlock(&dentry->d_lock); | ||
343 | } | ||
344 | |||
345 | struct nfs_renamedata { | ||
346 | struct nfs_renameargs args; | ||
347 | struct nfs_renameres res; | ||
348 | struct rpc_cred *cred; | ||
349 | struct inode *old_dir; | ||
350 | struct dentry *old_dentry; | ||
351 | struct nfs_fattr old_fattr; | ||
352 | struct inode *new_dir; | ||
353 | struct dentry *new_dentry; | ||
354 | struct nfs_fattr new_fattr; | ||
355 | }; | ||
356 | |||
357 | /** | ||
358 | * nfs_async_rename_done - Sillyrename post-processing | ||
359 | * @task: rpc_task of the sillyrename | ||
360 | * @calldata: nfs_renamedata for the sillyrename | ||
361 | * | ||
362 | * Do the directory attribute updates and the d_move | ||
363 | */ | ||
364 | static void nfs_async_rename_done(struct rpc_task *task, void *calldata) | ||
365 | { | ||
366 | struct nfs_renamedata *data = calldata; | ||
367 | struct inode *old_dir = data->old_dir; | ||
368 | struct inode *new_dir = data->new_dir; | ||
369 | |||
370 | if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) { | ||
371 | nfs_restart_rpc(task, NFS_SERVER(old_dir)->nfs_client); | ||
372 | return; | ||
373 | } | ||
374 | |||
375 | if (task->tk_status != 0) { | ||
376 | nfs_cancel_async_unlink(data->old_dentry); | ||
377 | return; | ||
378 | } | ||
379 | |||
380 | nfs_set_verifier(data->old_dentry, nfs_save_change_attribute(old_dir)); | ||
381 | d_move(data->old_dentry, data->new_dentry); | ||
382 | } | ||
383 | |||
384 | /** | ||
385 | * nfs_async_rename_release - Release the sillyrename data. | ||
386 | * @calldata: the struct nfs_renamedata to be released | ||
387 | */ | ||
388 | static void nfs_async_rename_release(void *calldata) | ||
389 | { | ||
390 | struct nfs_renamedata *data = calldata; | ||
391 | struct super_block *sb = data->old_dir->i_sb; | ||
392 | |||
393 | if (data->old_dentry->d_inode) | ||
394 | nfs_mark_for_revalidate(data->old_dentry->d_inode); | ||
395 | |||
396 | dput(data->old_dentry); | ||
397 | dput(data->new_dentry); | ||
398 | iput(data->old_dir); | ||
399 | iput(data->new_dir); | ||
400 | nfs_sb_deactive(sb); | ||
401 | put_rpccred(data->cred); | ||
402 | kfree(data); | ||
403 | } | ||
404 | |||
405 | #if defined(CONFIG_NFS_V4_1) | ||
406 | static void nfs_rename_prepare(struct rpc_task *task, void *calldata) | ||
407 | { | ||
408 | struct nfs_renamedata *data = calldata; | ||
409 | struct nfs_server *server = NFS_SERVER(data->old_dir); | ||
410 | |||
411 | if (nfs4_setup_sequence(server, &data->args.seq_args, | ||
412 | &data->res.seq_res, 1, task)) | ||
413 | return; | ||
414 | rpc_call_start(task); | ||
415 | } | ||
416 | #endif /* CONFIG_NFS_V4_1 */ | ||
417 | |||
418 | static const struct rpc_call_ops nfs_rename_ops = { | ||
419 | .rpc_call_done = nfs_async_rename_done, | ||
420 | .rpc_release = nfs_async_rename_release, | ||
421 | #if defined(CONFIG_NFS_V4_1) | ||
422 | .rpc_call_prepare = nfs_rename_prepare, | ||
423 | #endif /* CONFIG_NFS_V4_1 */ | ||
424 | }; | ||
425 | |||
426 | /** | ||
427 | * nfs_async_rename - perform an asynchronous rename operation | ||
428 | * @old_dir: directory that currently holds the dentry to be renamed | ||
429 | * @new_dir: target directory for the rename | ||
430 | * @old_dentry: original dentry to be renamed | ||
431 | * @new_dentry: dentry to which the old_dentry should be renamed | ||
432 | * | ||
433 | * It's expected that valid references to the dentries and inodes are held | ||
434 | */ | ||
435 | static struct rpc_task * | ||
436 | nfs_async_rename(struct inode *old_dir, struct inode *new_dir, | ||
437 | struct dentry *old_dentry, struct dentry *new_dentry) | ||
438 | { | ||
439 | struct nfs_renamedata *data; | ||
440 | struct rpc_message msg = { }; | ||
441 | struct rpc_task_setup task_setup_data = { | ||
442 | .rpc_message = &msg, | ||
443 | .callback_ops = &nfs_rename_ops, | ||
444 | .workqueue = nfsiod_workqueue, | ||
445 | .rpc_client = NFS_CLIENT(old_dir), | ||
446 | .flags = RPC_TASK_ASYNC, | ||
447 | }; | ||
448 | |||
449 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
450 | if (data == NULL) | ||
451 | return ERR_PTR(-ENOMEM); | ||
452 | task_setup_data.callback_data = data; | ||
453 | |||
454 | data->cred = rpc_lookup_cred(); | ||
455 | if (IS_ERR(data->cred)) { | ||
456 | struct rpc_task *task = ERR_CAST(data->cred); | ||
457 | kfree(data); | ||
458 | return task; | ||
459 | } | ||
460 | |||
461 | msg.rpc_argp = &data->args; | ||
462 | msg.rpc_resp = &data->res; | ||
463 | msg.rpc_cred = data->cred; | ||
464 | |||
465 | /* set up nfs_renamedata */ | ||
466 | data->old_dir = old_dir; | ||
467 | ihold(old_dir); | ||
468 | data->new_dir = new_dir; | ||
469 | ihold(new_dir); | ||
470 | data->old_dentry = dget(old_dentry); | ||
471 | data->new_dentry = dget(new_dentry); | ||
472 | nfs_fattr_init(&data->old_fattr); | ||
473 | nfs_fattr_init(&data->new_fattr); | ||
474 | |||
475 | /* set up nfs_renameargs */ | ||
476 | data->args.old_dir = NFS_FH(old_dir); | ||
477 | data->args.old_name = &old_dentry->d_name; | ||
478 | data->args.new_dir = NFS_FH(new_dir); | ||
479 | data->args.new_name = &new_dentry->d_name; | ||
480 | |||
481 | /* set up nfs_renameres */ | ||
482 | data->res.old_fattr = &data->old_fattr; | ||
483 | data->res.new_fattr = &data->new_fattr; | ||
484 | |||
485 | nfs_sb_active(old_dir->i_sb); | ||
486 | |||
487 | NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dir); | ||
488 | |||
489 | return rpc_run_task(&task_setup_data); | ||
490 | } | ||
491 | |||
492 | /** | ||
493 | * nfs_sillyrename - Perform a silly-rename of a dentry | ||
494 | * @dir: inode of directory that contains dentry | ||
495 | * @dentry: dentry to be sillyrenamed | ||
496 | * | ||
497 | * NFSv2/3 is stateless and the server doesn't know when the client is | ||
498 | * holding a file open. To prevent application problems when a file is | ||
499 | * unlinked while it's still open, the client performs a "silly-rename". | ||
500 | * That is, it renames the file to a hidden file in the same directory, | ||
501 | * and only performs the unlink once the last reference to it is put. | ||
502 | * | ||
503 | * The final cleanup is done during dentry_iput. | ||
504 | */ | ||
505 | int | ||
506 | nfs_sillyrename(struct inode *dir, struct dentry *dentry) | ||
507 | { | ||
508 | static unsigned int sillycounter; | ||
509 | const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; | ||
510 | const int countersize = sizeof(sillycounter)*2; | ||
511 | const int slen = sizeof(".nfs")+fileidsize+countersize-1; | ||
512 | char silly[slen+1]; | ||
513 | struct dentry *sdentry; | ||
514 | struct rpc_task *task; | ||
515 | int error = -EIO; | ||
516 | |||
517 | dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", | ||
518 | dentry->d_parent->d_name.name, dentry->d_name.name, | ||
519 | dentry->d_count); | ||
520 | nfs_inc_stats(dir, NFSIOS_SILLYRENAME); | ||
521 | |||
522 | /* | ||
523 | * We don't allow a dentry to be silly-renamed twice. | ||
524 | */ | ||
525 | error = -EBUSY; | ||
526 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
527 | goto out; | ||
528 | |||
529 | sprintf(silly, ".nfs%*.*Lx", | ||
530 | fileidsize, fileidsize, | ||
531 | (unsigned long long)NFS_FILEID(dentry->d_inode)); | ||
532 | |||
533 | /* Return delegation in anticipation of the rename */ | ||
534 | nfs_inode_return_delegation(dentry->d_inode); | ||
535 | |||
536 | sdentry = NULL; | ||
537 | do { | ||
538 | char *suffix = silly + slen - countersize; | ||
539 | |||
540 | dput(sdentry); | ||
541 | sillycounter++; | ||
542 | sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); | ||
543 | |||
544 | dfprintk(VFS, "NFS: trying to rename %s to %s\n", | ||
545 | dentry->d_name.name, silly); | ||
546 | |||
547 | sdentry = lookup_one_len(silly, dentry->d_parent, slen); | ||
548 | /* | ||
549 | * N.B. Better to return EBUSY here ... it could be | ||
550 | * dangerous to delete the file while it's in use. | ||
551 | */ | ||
552 | if (IS_ERR(sdentry)) | ||
553 | goto out; | ||
554 | } while (sdentry->d_inode != NULL); /* need negative lookup */ | ||
555 | |||
556 | /* queue unlink first. Can't do this from rpc_release as it | ||
557 | * has to allocate memory | ||
558 | */ | ||
559 | error = nfs_async_unlink(dir, dentry); | ||
560 | if (error) | ||
561 | goto out_dput; | ||
562 | |||
563 | /* run the rename task, undo unlink if it fails */ | ||
564 | task = nfs_async_rename(dir, dir, dentry, sdentry); | ||
565 | if (IS_ERR(task)) { | ||
566 | error = -EBUSY; | ||
567 | nfs_cancel_async_unlink(dentry); | ||
568 | goto out_dput; | ||
569 | } | ||
570 | |||
571 | /* wait for the RPC task to complete, unless a SIGKILL intervenes */ | ||
572 | error = rpc_wait_for_completion_task(task); | ||
573 | if (error == 0) | ||
574 | error = task->tk_status; | ||
575 | rpc_put_task(task); | ||
576 | out_dput: | ||
577 | dput(sdentry); | ||
578 | out: | ||
579 | return error; | ||
580 | } | ||