diff options
author | David Howells <dhowells@redhat.com> | 2019-04-25 09:26:52 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2019-04-25 09:26:52 -0400 |
commit | 6c6c1d63c243025956f061e67fff3a615aa0f6be (patch) | |
tree | 2dc16f2dfa389efcc4b53197a33a9654cf02f794 | |
parent | 80548b03991f58758a336424a90bf9f988e3b077 (diff) |
afs: Provide mount-time configurable byte-range file locking emulation
Provide byte-range file locking emulation that can be configured at mount
time to one of four modes:
(1) flock=local. Locking is done locally only and no reference is made to
the server.
(2) flock=openafs. Byte-range locking is done locally only; whole-file
locking is done with reference to the server. Whole-file locks cannot
be upgraded unless the client holds an exclusive lock.
(3) flock=strict. Byte-range and whole-file locking both require a
sufficient whole-file lock on the server.
(4) flock=write. As strict, but the client always gets an exclusive
whole-file lock on the server.
Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r-- | fs/afs/flock.c | 49 | ||||
-rw-r--r-- | fs/afs/fsclient.c | 2 | ||||
-rw-r--r-- | fs/afs/internal.h | 14 | ||||
-rw-r--r-- | fs/afs/super.c | 27 | ||||
-rw-r--r-- | fs/afs/yfsclient.c | 2 | ||||
-rw-r--r-- | include/trace/events/afs.h | 35 |
6 files changed, 120 insertions, 9 deletions
diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 325bf731d8dd..ef313f4c1d11 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c | |||
@@ -409,7 +409,7 @@ static void afs_defer_unlock(struct afs_vnode *vnode) | |||
409 | * whether we think that we have a locking permit. | 409 | * whether we think that we have a locking permit. |
410 | */ | 410 | */ |
411 | static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, | 411 | static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, |
412 | afs_lock_type_t type, bool can_sleep) | 412 | enum afs_flock_mode mode, afs_lock_type_t type) |
413 | { | 413 | { |
414 | afs_access_t access; | 414 | afs_access_t access; |
415 | int ret; | 415 | int ret; |
@@ -437,13 +437,9 @@ static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, | |||
437 | if (type == AFS_LOCK_READ) { | 437 | if (type == AFS_LOCK_READ) { |
438 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK))) | 438 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK))) |
439 | return -EACCES; | 439 | return -EACCES; |
440 | if (vnode->status.lock_count == -1 && !can_sleep) | ||
441 | return -EAGAIN; /* Write locked */ | ||
442 | } else { | 440 | } else { |
443 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE))) | 441 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE))) |
444 | return -EACCES; | 442 | return -EACCES; |
445 | if (vnode->status.lock_count != 0 && !can_sleep) | ||
446 | return -EAGAIN; /* Locked */ | ||
447 | } | 443 | } |
448 | 444 | ||
449 | return 0; | 445 | return 0; |
@@ -456,24 +452,48 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) | |||
456 | { | 452 | { |
457 | struct inode *inode = locks_inode(file); | 453 | struct inode *inode = locks_inode(file); |
458 | struct afs_vnode *vnode = AFS_FS_I(inode); | 454 | struct afs_vnode *vnode = AFS_FS_I(inode); |
455 | enum afs_flock_mode mode = AFS_FS_S(inode->i_sb)->flock_mode; | ||
459 | afs_lock_type_t type; | 456 | afs_lock_type_t type; |
460 | struct key *key = afs_file_key(file); | 457 | struct key *key = afs_file_key(file); |
458 | bool partial, no_server_lock = false; | ||
461 | int ret; | 459 | int ret; |
462 | 460 | ||
463 | _enter("{%llx:%llu},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); | 461 | if (mode == afs_flock_mode_unset) |
462 | mode = afs_flock_mode_openafs; | ||
463 | |||
464 | _enter("{%llx:%llu},%llu-%llu,%u,%u", | ||
465 | vnode->fid.vid, vnode->fid.vnode, | ||
466 | fl->fl_start, fl->fl_end, fl->fl_type, mode); | ||
464 | 467 | ||
465 | fl->fl_ops = &afs_lock_ops; | 468 | fl->fl_ops = &afs_lock_ops; |
466 | INIT_LIST_HEAD(&fl->fl_u.afs.link); | 469 | INIT_LIST_HEAD(&fl->fl_u.afs.link); |
467 | fl->fl_u.afs.state = AFS_LOCK_PENDING; | 470 | fl->fl_u.afs.state = AFS_LOCK_PENDING; |
468 | 471 | ||
472 | partial = (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX); | ||
469 | type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; | 473 | type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; |
474 | if (mode == afs_flock_mode_write && partial) | ||
475 | type = AFS_LOCK_WRITE; | ||
470 | 476 | ||
471 | ret = afs_do_setlk_check(vnode, key, type, fl->fl_flags & FL_SLEEP); | 477 | ret = afs_do_setlk_check(vnode, key, mode, type); |
472 | if (ret < 0) | 478 | if (ret < 0) |
473 | return ret; | 479 | return ret; |
474 | 480 | ||
475 | trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock); | 481 | trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock); |
476 | 482 | ||
483 | /* AFS3 protocol only supports full-file locks and doesn't provide any | ||
484 | * method of upgrade/downgrade, so we need to emulate for partial-file | ||
485 | * locks. | ||
486 | * | ||
487 | * The OpenAFS client only gets a server lock for a full-file lock and | ||
488 | * keeps partial-file locks local. Allow this behaviour to be emulated | ||
489 | * (as the default). | ||
490 | */ | ||
491 | if (mode == afs_flock_mode_local || | ||
492 | (partial && mode == afs_flock_mode_openafs)) { | ||
493 | no_server_lock = true; | ||
494 | goto skip_server_lock; | ||
495 | } | ||
496 | |||
477 | spin_lock(&vnode->lock); | 497 | spin_lock(&vnode->lock); |
478 | list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); | 498 | list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); |
479 | 499 | ||
@@ -502,6 +522,18 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) | |||
502 | } | 522 | } |
503 | } | 523 | } |
504 | 524 | ||
525 | if (vnode->lock_state == AFS_VNODE_LOCK_NONE && | ||
526 | !(fl->fl_flags & FL_SLEEP)) { | ||
527 | ret = -EAGAIN; | ||
528 | if (type == AFS_LOCK_READ) { | ||
529 | if (vnode->status.lock_count == -1) | ||
530 | goto lock_is_contended; /* Write locked */ | ||
531 | } else { | ||
532 | if (vnode->status.lock_count != 0) | ||
533 | goto lock_is_contended; /* Locked */ | ||
534 | } | ||
535 | } | ||
536 | |||
505 | if (vnode->lock_state != AFS_VNODE_LOCK_NONE) | 537 | if (vnode->lock_state != AFS_VNODE_LOCK_NONE) |
506 | goto need_to_wait; | 538 | goto need_to_wait; |
507 | 539 | ||
@@ -571,6 +603,7 @@ vnode_is_locked: | |||
571 | /* the lock has been granted by the server... */ | 603 | /* the lock has been granted by the server... */ |
572 | ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED); | 604 | ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED); |
573 | 605 | ||
606 | skip_server_lock: | ||
574 | /* ... but the VFS still needs to distribute access on this client. */ | 607 | /* ... but the VFS still needs to distribute access on this client. */ |
575 | trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0); | 608 | trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0); |
576 | ret = locks_lock_file_wait(file, fl); | 609 | ret = locks_lock_file_wait(file, fl); |
@@ -649,6 +682,8 @@ vfs_rejected_lock: | |||
649 | * deal with. | 682 | * deal with. |
650 | */ | 683 | */ |
651 | _debug("vfs refused %d", ret); | 684 | _debug("vfs refused %d", ret); |
685 | if (no_server_lock) | ||
686 | goto error; | ||
652 | spin_lock(&vnode->lock); | 687 | spin_lock(&vnode->lock); |
653 | list_del_init(&fl->fl_u.afs.link); | 688 | list_del_init(&fl->fl_u.afs.link); |
654 | afs_defer_unlock(vnode); | 689 | afs_defer_unlock(vnode); |
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index be4520eb4965..9b73a57aa5cb 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c | |||
@@ -1902,7 +1902,7 @@ int afs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type) | |||
1902 | *bp++ = htonl(type); | 1902 | *bp++ = htonl(type); |
1903 | 1903 | ||
1904 | afs_use_fs_server(call, fc->cbi); | 1904 | afs_use_fs_server(call, fc->cbi); |
1905 | trace_afs_make_fs_call(call, &vnode->fid); | 1905 | trace_afs_make_fs_calli(call, &vnode->fid, type); |
1906 | afs_make_call(&fc->ac, call, GFP_NOFS); | 1906 | afs_make_call(&fc->ac, call, GFP_NOFS); |
1907 | return afs_wait_for_call_to_complete(call, &fc->ac); | 1907 | return afs_wait_for_call_to_complete(call, &fc->ac); |
1908 | } | 1908 | } |
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 20fd44de26ac..91204e1428f2 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -36,11 +36,24 @@ | |||
36 | struct pagevec; | 36 | struct pagevec; |
37 | struct afs_call; | 37 | struct afs_call; |
38 | 38 | ||
39 | /* | ||
40 | * Partial file-locking emulation mode. (The problem being that AFS3 only | ||
41 | * allows whole-file locks and no upgrading/downgrading). | ||
42 | */ | ||
43 | enum afs_flock_mode { | ||
44 | afs_flock_mode_unset, | ||
45 | afs_flock_mode_local, /* Local locking only */ | ||
46 | afs_flock_mode_openafs, /* Don't get server lock for a partial lock */ | ||
47 | afs_flock_mode_strict, /* Always get a server lock for a partial lock */ | ||
48 | afs_flock_mode_write, /* Get an exclusive server lock for a partial lock */ | ||
49 | }; | ||
50 | |||
39 | struct afs_fs_context { | 51 | struct afs_fs_context { |
40 | bool force; /* T to force cell type */ | 52 | bool force; /* T to force cell type */ |
41 | bool autocell; /* T if set auto mount operation */ | 53 | bool autocell; /* T if set auto mount operation */ |
42 | bool dyn_root; /* T if dynamic root */ | 54 | bool dyn_root; /* T if dynamic root */ |
43 | bool no_cell; /* T if the source is "none" (for dynroot) */ | 55 | bool no_cell; /* T if the source is "none" (for dynroot) */ |
56 | enum afs_flock_mode flock_mode; /* Partial file-locking emulation mode */ | ||
44 | afs_voltype_t type; /* type of volume requested */ | 57 | afs_voltype_t type; /* type of volume requested */ |
45 | unsigned int volnamesz; /* size of volume name */ | 58 | unsigned int volnamesz; /* size of volume name */ |
46 | const char *volname; /* name of volume to mount */ | 59 | const char *volname; /* name of volume to mount */ |
@@ -221,6 +234,7 @@ struct afs_super_info { | |||
221 | struct net *net_ns; /* Network namespace */ | 234 | struct net *net_ns; /* Network namespace */ |
222 | struct afs_cell *cell; /* The cell in which the volume resides */ | 235 | struct afs_cell *cell; /* The cell in which the volume resides */ |
223 | struct afs_volume *volume; /* volume record */ | 236 | struct afs_volume *volume; /* volume record */ |
237 | enum afs_flock_mode flock_mode:8; /* File locking emulation mode */ | ||
224 | bool dyn_root; /* True if dynamic root */ | 238 | bool dyn_root; /* True if dynamic root */ |
225 | }; | 239 | }; |
226 | 240 | ||
diff --git a/fs/afs/super.c b/fs/afs/super.c index ce85ae61f12d..18334fa1a0d2 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c | |||
@@ -67,19 +67,30 @@ static atomic_t afs_count_active_inodes; | |||
67 | enum afs_param { | 67 | enum afs_param { |
68 | Opt_autocell, | 68 | Opt_autocell, |
69 | Opt_dyn, | 69 | Opt_dyn, |
70 | Opt_flock, | ||
70 | Opt_source, | 71 | Opt_source, |
71 | }; | 72 | }; |
72 | 73 | ||
73 | static const struct fs_parameter_spec afs_param_specs[] = { | 74 | static const struct fs_parameter_spec afs_param_specs[] = { |
74 | fsparam_flag ("autocell", Opt_autocell), | 75 | fsparam_flag ("autocell", Opt_autocell), |
75 | fsparam_flag ("dyn", Opt_dyn), | 76 | fsparam_flag ("dyn", Opt_dyn), |
77 | fsparam_enum ("flock", Opt_flock), | ||
76 | fsparam_string("source", Opt_source), | 78 | fsparam_string("source", Opt_source), |
77 | {} | 79 | {} |
78 | }; | 80 | }; |
79 | 81 | ||
82 | static const struct fs_parameter_enum afs_param_enums[] = { | ||
83 | { Opt_flock, "local", afs_flock_mode_local }, | ||
84 | { Opt_flock, "openafs", afs_flock_mode_openafs }, | ||
85 | { Opt_flock, "strict", afs_flock_mode_strict }, | ||
86 | { Opt_flock, "write", afs_flock_mode_write }, | ||
87 | {} | ||
88 | }; | ||
89 | |||
80 | static const struct fs_parameter_description afs_fs_parameters = { | 90 | static const struct fs_parameter_description afs_fs_parameters = { |
81 | .name = "kAFS", | 91 | .name = "kAFS", |
82 | .specs = afs_param_specs, | 92 | .specs = afs_param_specs, |
93 | .enums = afs_param_enums, | ||
83 | }; | 94 | }; |
84 | 95 | ||
85 | /* | 96 | /* |
@@ -182,11 +193,22 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) | |||
182 | static int afs_show_options(struct seq_file *m, struct dentry *root) | 193 | static int afs_show_options(struct seq_file *m, struct dentry *root) |
183 | { | 194 | { |
184 | struct afs_super_info *as = AFS_FS_S(root->d_sb); | 195 | struct afs_super_info *as = AFS_FS_S(root->d_sb); |
196 | const char *p = NULL; | ||
185 | 197 | ||
186 | if (as->dyn_root) | 198 | if (as->dyn_root) |
187 | seq_puts(m, ",dyn"); | 199 | seq_puts(m, ",dyn"); |
188 | if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags)) | 200 | if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags)) |
189 | seq_puts(m, ",autocell"); | 201 | seq_puts(m, ",autocell"); |
202 | switch (as->flock_mode) { | ||
203 | case afs_flock_mode_unset: break; | ||
204 | case afs_flock_mode_local: p = "local"; break; | ||
205 | case afs_flock_mode_openafs: p = "openafs"; break; | ||
206 | case afs_flock_mode_strict: p = "strict"; break; | ||
207 | case afs_flock_mode_write: p = "write"; break; | ||
208 | } | ||
209 | if (p) | ||
210 | seq_printf(m, ",flock=%s", p); | ||
211 | |||
190 | return 0; | 212 | return 0; |
191 | } | 213 | } |
192 | 214 | ||
@@ -315,6 +337,10 @@ static int afs_parse_param(struct fs_context *fc, struct fs_parameter *param) | |||
315 | ctx->dyn_root = true; | 337 | ctx->dyn_root = true; |
316 | break; | 338 | break; |
317 | 339 | ||
340 | case Opt_flock: | ||
341 | ctx->flock_mode = result.uint_32; | ||
342 | break; | ||
343 | |||
318 | default: | 344 | default: |
319 | return -EINVAL; | 345 | return -EINVAL; |
320 | } | 346 | } |
@@ -466,6 +492,7 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc) | |||
466 | as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); | 492 | as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); |
467 | if (as) { | 493 | if (as) { |
468 | as->net_ns = get_net(fc->net_ns); | 494 | as->net_ns = get_net(fc->net_ns); |
495 | as->flock_mode = ctx->flock_mode; | ||
469 | if (ctx->dyn_root) { | 496 | if (ctx->dyn_root) { |
470 | as->dyn_root = true; | 497 | as->dyn_root = true; |
471 | } else { | 498 | } else { |
diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index 5ea0350dc9dd..055840aa07f6 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c | |||
@@ -1860,7 +1860,7 @@ int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type) | |||
1860 | yfs_check_req(call, bp); | 1860 | yfs_check_req(call, bp); |
1861 | 1861 | ||
1862 | afs_use_fs_server(call, fc->cbi); | 1862 | afs_use_fs_server(call, fc->cbi); |
1863 | trace_afs_make_fs_call(call, &vnode->fid); | 1863 | trace_afs_make_fs_calli(call, &vnode->fid, type); |
1864 | afs_make_call(&fc->ac, call, GFP_NOFS); | 1864 | afs_make_call(&fc->ac, call, GFP_NOFS); |
1865 | return afs_wait_for_call_to_complete(call, &fc->ac); | 1865 | return afs_wait_for_call_to_complete(call, &fc->ac); |
1866 | } | 1866 | } |
diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index e81d6a50781f..f1373c29bf7d 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h | |||
@@ -539,6 +539,41 @@ TRACE_EVENT(afs_make_fs_call, | |||
539 | __print_symbolic(__entry->op, afs_fs_operations)) | 539 | __print_symbolic(__entry->op, afs_fs_operations)) |
540 | ); | 540 | ); |
541 | 541 | ||
542 | TRACE_EVENT(afs_make_fs_calli, | ||
543 | TP_PROTO(struct afs_call *call, const struct afs_fid *fid, | ||
544 | unsigned int i), | ||
545 | |||
546 | TP_ARGS(call, fid, i), | ||
547 | |||
548 | TP_STRUCT__entry( | ||
549 | __field(unsigned int, call ) | ||
550 | __field(unsigned int, i ) | ||
551 | __field(enum afs_fs_operation, op ) | ||
552 | __field_struct(struct afs_fid, fid ) | ||
553 | ), | ||
554 | |||
555 | TP_fast_assign( | ||
556 | __entry->call = call->debug_id; | ||
557 | __entry->i = i; | ||
558 | __entry->op = call->operation_ID; | ||
559 | if (fid) { | ||
560 | __entry->fid = *fid; | ||
561 | } else { | ||
562 | __entry->fid.vid = 0; | ||
563 | __entry->fid.vnode = 0; | ||
564 | __entry->fid.unique = 0; | ||
565 | } | ||
566 | ), | ||
567 | |||
568 | TP_printk("c=%08x %06llx:%06llx:%06x %s i=%u", | ||
569 | __entry->call, | ||
570 | __entry->fid.vid, | ||
571 | __entry->fid.vnode, | ||
572 | __entry->fid.unique, | ||
573 | __print_symbolic(__entry->op, afs_fs_operations), | ||
574 | __entry->i) | ||
575 | ); | ||
576 | |||
542 | TRACE_EVENT(afs_make_fs_call1, | 577 | TRACE_EVENT(afs_make_fs_call1, |
543 | TP_PROTO(struct afs_call *call, const struct afs_fid *fid, | 578 | TP_PROTO(struct afs_call *call, const struct afs_fid *fid, |
544 | const char *name), | 579 | const char *name), |