Skip to content

Commit 6c6c1d6

Browse files
committed
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 <[email protected]>
1 parent 80548b0 commit 6c6c1d6

File tree

6 files changed

+120
-9
lines changed

6 files changed

+120
-9
lines changed

fs/afs/flock.c

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ static void afs_defer_unlock(struct afs_vnode *vnode)
409409
* whether we think that we have a locking permit.
410410
*/
411411
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)
413413
{
414414
afs_access_t access;
415415
int ret;
@@ -437,13 +437,9 @@ static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key,
437437
if (type == AFS_LOCK_READ) {
438438
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK)))
439439
return -EACCES;
440-
if (vnode->status.lock_count == -1 && !can_sleep)
441-
return -EAGAIN; /* Write locked */
442440
} else {
443441
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE)))
444442
return -EACCES;
445-
if (vnode->status.lock_count != 0 && !can_sleep)
446-
return -EAGAIN; /* Locked */
447443
}
448444

449445
return 0;
@@ -456,24 +452,48 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
456452
{
457453
struct inode *inode = locks_inode(file);
458454
struct afs_vnode *vnode = AFS_FS_I(inode);
455+
enum afs_flock_mode mode = AFS_FS_S(inode->i_sb)->flock_mode;
459456
afs_lock_type_t type;
460457
struct key *key = afs_file_key(file);
458+
bool partial, no_server_lock = false;
461459
int ret;
462460

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);
464467

465468
fl->fl_ops = &afs_lock_ops;
466469
INIT_LIST_HEAD(&fl->fl_u.afs.link);
467470
fl->fl_u.afs.state = AFS_LOCK_PENDING;
468471

472+
partial = (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX);
469473
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;
470476

471-
ret = afs_do_setlk_check(vnode, key, type, fl->fl_flags & FL_SLEEP);
477+
ret = afs_do_setlk_check(vnode, key, mode, type);
472478
if (ret < 0)
473479
return ret;
474480

475481
trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock);
476482

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+
477497
spin_lock(&vnode->lock);
478498
list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks);
479499

@@ -502,6 +522,18 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
502522
}
503523
}
504524

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+
505537
if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
506538
goto need_to_wait;
507539

@@ -571,6 +603,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
571603
/* the lock has been granted by the server... */
572604
ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED);
573605

606+
skip_server_lock:
574607
/* ... but the VFS still needs to distribute access on this client. */
575608
trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0);
576609
ret = locks_lock_file_wait(file, fl);
@@ -649,6 +682,8 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
649682
* deal with.
650683
*/
651684
_debug("vfs refused %d", ret);
685+
if (no_server_lock)
686+
goto error;
652687
spin_lock(&vnode->lock);
653688
list_del_init(&fl->fl_u.afs.link);
654689
afs_defer_unlock(vnode);

fs/afs/fsclient.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1902,7 +1902,7 @@ int afs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
19021902
*bp++ = htonl(type);
19031903

19041904
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);
19061906
afs_make_call(&fc->ac, call, GFP_NOFS);
19071907
return afs_wait_for_call_to_complete(call, &fc->ac);
19081908
}

fs/afs/internal.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,24 @@
3636
struct pagevec;
3737
struct afs_call;
3838

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+
3951
struct afs_fs_context {
4052
bool force; /* T to force cell type */
4153
bool autocell; /* T if set auto mount operation */
4254
bool dyn_root; /* T if dynamic root */
4355
bool no_cell; /* T if the source is "none" (for dynroot) */
56+
enum afs_flock_mode flock_mode; /* Partial file-locking emulation mode */
4457
afs_voltype_t type; /* type of volume requested */
4558
unsigned int volnamesz; /* size of volume name */
4659
const char *volname; /* name of volume to mount */
@@ -221,6 +234,7 @@ struct afs_super_info {
221234
struct net *net_ns; /* Network namespace */
222235
struct afs_cell *cell; /* The cell in which the volume resides */
223236
struct afs_volume *volume; /* volume record */
237+
enum afs_flock_mode flock_mode:8; /* File locking emulation mode */
224238
bool dyn_root; /* True if dynamic root */
225239
};
226240

fs/afs/super.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,30 @@ static atomic_t afs_count_active_inodes;
6767
enum afs_param {
6868
Opt_autocell,
6969
Opt_dyn,
70+
Opt_flock,
7071
Opt_source,
7172
};
7273

7374
static const struct fs_parameter_spec afs_param_specs[] = {
7475
fsparam_flag ("autocell", Opt_autocell),
7576
fsparam_flag ("dyn", Opt_dyn),
77+
fsparam_enum ("flock", Opt_flock),
7678
fsparam_string("source", Opt_source),
7779
{}
7880
};
7981

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+
8090
static const struct fs_parameter_description afs_fs_parameters = {
8191
.name = "kAFS",
8292
.specs = afs_param_specs,
93+
.enums = afs_param_enums,
8394
};
8495

8596
/*
@@ -182,11 +193,22 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root)
182193
static int afs_show_options(struct seq_file *m, struct dentry *root)
183194
{
184195
struct afs_super_info *as = AFS_FS_S(root->d_sb);
196+
const char *p = NULL;
185197

186198
if (as->dyn_root)
187199
seq_puts(m, ",dyn");
188200
if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags))
189201
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+
190212
return 0;
191213
}
192214

@@ -315,6 +337,10 @@ static int afs_parse_param(struct fs_context *fc, struct fs_parameter *param)
315337
ctx->dyn_root = true;
316338
break;
317339

340+
case Opt_flock:
341+
ctx->flock_mode = result.uint_32;
342+
break;
343+
318344
default:
319345
return -EINVAL;
320346
}
@@ -466,6 +492,7 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc)
466492
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
467493
if (as) {
468494
as->net_ns = get_net(fc->net_ns);
495+
as->flock_mode = ctx->flock_mode;
469496
if (ctx->dyn_root) {
470497
as->dyn_root = true;
471498
} else {

fs/afs/yfsclient.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1860,7 +1860,7 @@ int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
18601860
yfs_check_req(call, bp);
18611861

18621862
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);
18641864
afs_make_call(&fc->ac, call, GFP_NOFS);
18651865
return afs_wait_for_call_to_complete(call, &fc->ac);
18661866
}

include/trace/events/afs.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,41 @@ TRACE_EVENT(afs_make_fs_call,
539539
__print_symbolic(__entry->op, afs_fs_operations))
540540
);
541541

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+
542577
TRACE_EVENT(afs_make_fs_call1,
543578
TP_PROTO(struct afs_call *call, const struct afs_fid *fid,
544579
const char *name),

0 commit comments

Comments
 (0)