Commit 6c6c1d63 authored by David Howells's avatar David Howells

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: default avatarDavid Howells <dhowells@redhat.com>
parent 80548b03
...@@ -409,7 +409,7 @@ static void afs_defer_unlock(struct afs_vnode *vnode) ...@@ -409,7 +409,7 @@ static void afs_defer_unlock(struct afs_vnode *vnode)
* whether we think that we have a locking permit. * whether we think that we have a locking permit.
*/ */
static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key,
afs_lock_type_t type, bool can_sleep) enum afs_flock_mode mode, afs_lock_type_t type)
{ {
afs_access_t access; afs_access_t access;
int ret; int ret;
...@@ -437,13 +437,9 @@ static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, ...@@ -437,13 +437,9 @@ static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key,
if (type == AFS_LOCK_READ) { if (type == AFS_LOCK_READ) {
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK))) if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK)))
return -EACCES; return -EACCES;
if (vnode->status.lock_count == -1 && !can_sleep)
return -EAGAIN; /* Write locked */
} else { } else {
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE))) if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE)))
return -EACCES; return -EACCES;
if (vnode->status.lock_count != 0 && !can_sleep)
return -EAGAIN; /* Locked */
} }
return 0; return 0;
...@@ -456,24 +452,48 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) ...@@ -456,24 +452,48 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
{ {
struct inode *inode = locks_inode(file); struct inode *inode = locks_inode(file);
struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_vnode *vnode = AFS_FS_I(inode);
enum afs_flock_mode mode = AFS_FS_S(inode->i_sb)->flock_mode;
afs_lock_type_t type; afs_lock_type_t type;
struct key *key = afs_file_key(file); struct key *key = afs_file_key(file);
bool partial, no_server_lock = false;
int ret; int ret;
_enter("{%llx:%llu},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); if (mode == afs_flock_mode_unset)
mode = afs_flock_mode_openafs;
_enter("{%llx:%llu},%llu-%llu,%u,%u",
vnode->fid.vid, vnode->fid.vnode,
fl->fl_start, fl->fl_end, fl->fl_type, mode);
fl->fl_ops = &afs_lock_ops; fl->fl_ops = &afs_lock_ops;
INIT_LIST_HEAD(&fl->fl_u.afs.link); INIT_LIST_HEAD(&fl->fl_u.afs.link);
fl->fl_u.afs.state = AFS_LOCK_PENDING; fl->fl_u.afs.state = AFS_LOCK_PENDING;
partial = (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX);
type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
if (mode == afs_flock_mode_write && partial)
type = AFS_LOCK_WRITE;
ret = afs_do_setlk_check(vnode, key, type, fl->fl_flags & FL_SLEEP); ret = afs_do_setlk_check(vnode, key, mode, type);
if (ret < 0) if (ret < 0)
return ret; return ret;
trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock); trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock);
/* AFS3 protocol only supports full-file locks and doesn't provide any
* method of upgrade/downgrade, so we need to emulate for partial-file
* locks.
*
* The OpenAFS client only gets a server lock for a full-file lock and
* keeps partial-file locks local. Allow this behaviour to be emulated
* (as the default).
*/
if (mode == afs_flock_mode_local ||
(partial && mode == afs_flock_mode_openafs)) {
no_server_lock = true;
goto skip_server_lock;
}
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks);
...@@ -502,6 +522,18 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) ...@@ -502,6 +522,18 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
} }
} }
if (vnode->lock_state == AFS_VNODE_LOCK_NONE &&
!(fl->fl_flags & FL_SLEEP)) {
ret = -EAGAIN;
if (type == AFS_LOCK_READ) {
if (vnode->status.lock_count == -1)
goto lock_is_contended; /* Write locked */
} else {
if (vnode->status.lock_count != 0)
goto lock_is_contended; /* Locked */
}
}
if (vnode->lock_state != AFS_VNODE_LOCK_NONE) if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
goto need_to_wait; goto need_to_wait;
...@@ -571,6 +603,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) ...@@ -571,6 +603,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
/* the lock has been granted by the server... */ /* the lock has been granted by the server... */
ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED); ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED);
skip_server_lock:
/* ... but the VFS still needs to distribute access on this client. */ /* ... but the VFS still needs to distribute access on this client. */
trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0); trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0);
ret = locks_lock_file_wait(file, fl); ret = locks_lock_file_wait(file, fl);
...@@ -649,6 +682,8 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) ...@@ -649,6 +682,8 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
* deal with. * deal with.
*/ */
_debug("vfs refused %d", ret); _debug("vfs refused %d", ret);
if (no_server_lock)
goto error;
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
list_del_init(&fl->fl_u.afs.link); list_del_init(&fl->fl_u.afs.link);
afs_defer_unlock(vnode); afs_defer_unlock(vnode);
......
...@@ -1902,7 +1902,7 @@ int afs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type) ...@@ -1902,7 +1902,7 @@ int afs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
*bp++ = htonl(type); *bp++ = htonl(type);
afs_use_fs_server(call, fc->cbi); afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid); trace_afs_make_fs_calli(call, &vnode->fid, type);
afs_make_call(&fc->ac, call, GFP_NOFS); afs_make_call(&fc->ac, call, GFP_NOFS);
return afs_wait_for_call_to_complete(call, &fc->ac); return afs_wait_for_call_to_complete(call, &fc->ac);
} }
......
...@@ -36,11 +36,24 @@ ...@@ -36,11 +36,24 @@
struct pagevec; struct pagevec;
struct afs_call; struct afs_call;
/*
* Partial file-locking emulation mode. (The problem being that AFS3 only
* allows whole-file locks and no upgrading/downgrading).
*/
enum afs_flock_mode {
afs_flock_mode_unset,
afs_flock_mode_local, /* Local locking only */
afs_flock_mode_openafs, /* Don't get server lock for a partial lock */
afs_flock_mode_strict, /* Always get a server lock for a partial lock */
afs_flock_mode_write, /* Get an exclusive server lock for a partial lock */
};
struct afs_fs_context { struct afs_fs_context {
bool force; /* T to force cell type */ bool force; /* T to force cell type */
bool autocell; /* T if set auto mount operation */ bool autocell; /* T if set auto mount operation */
bool dyn_root; /* T if dynamic root */ bool dyn_root; /* T if dynamic root */
bool no_cell; /* T if the source is "none" (for dynroot) */ bool no_cell; /* T if the source is "none" (for dynroot) */
enum afs_flock_mode flock_mode; /* Partial file-locking emulation mode */
afs_voltype_t type; /* type of volume requested */ afs_voltype_t type; /* type of volume requested */
unsigned int volnamesz; /* size of volume name */ unsigned int volnamesz; /* size of volume name */
const char *volname; /* name of volume to mount */ const char *volname; /* name of volume to mount */
...@@ -221,6 +234,7 @@ struct afs_super_info { ...@@ -221,6 +234,7 @@ struct afs_super_info {
struct net *net_ns; /* Network namespace */ struct net *net_ns; /* Network namespace */
struct afs_cell *cell; /* The cell in which the volume resides */ struct afs_cell *cell; /* The cell in which the volume resides */
struct afs_volume *volume; /* volume record */ struct afs_volume *volume; /* volume record */
enum afs_flock_mode flock_mode:8; /* File locking emulation mode */
bool dyn_root; /* True if dynamic root */ bool dyn_root; /* True if dynamic root */
}; };
......
...@@ -67,19 +67,30 @@ static atomic_t afs_count_active_inodes; ...@@ -67,19 +67,30 @@ static atomic_t afs_count_active_inodes;
enum afs_param { enum afs_param {
Opt_autocell, Opt_autocell,
Opt_dyn, Opt_dyn,
Opt_flock,
Opt_source, Opt_source,
}; };
static const struct fs_parameter_spec afs_param_specs[] = { static const struct fs_parameter_spec afs_param_specs[] = {
fsparam_flag ("autocell", Opt_autocell), fsparam_flag ("autocell", Opt_autocell),
fsparam_flag ("dyn", Opt_dyn), fsparam_flag ("dyn", Opt_dyn),
fsparam_enum ("flock", Opt_flock),
fsparam_string("source", Opt_source), fsparam_string("source", Opt_source),
{} {}
}; };
static const struct fs_parameter_enum afs_param_enums[] = {
{ Opt_flock, "local", afs_flock_mode_local },
{ Opt_flock, "openafs", afs_flock_mode_openafs },
{ Opt_flock, "strict", afs_flock_mode_strict },
{ Opt_flock, "write", afs_flock_mode_write },
{}
};
static const struct fs_parameter_description afs_fs_parameters = { static const struct fs_parameter_description afs_fs_parameters = {
.name = "kAFS", .name = "kAFS",
.specs = afs_param_specs, .specs = afs_param_specs,
.enums = afs_param_enums,
}; };
/* /*
...@@ -182,11 +193,22 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) ...@@ -182,11 +193,22 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root)
static int afs_show_options(struct seq_file *m, struct dentry *root) static int afs_show_options(struct seq_file *m, struct dentry *root)
{ {
struct afs_super_info *as = AFS_FS_S(root->d_sb); struct afs_super_info *as = AFS_FS_S(root->d_sb);
const char *p = NULL;
if (as->dyn_root) if (as->dyn_root)
seq_puts(m, ",dyn"); seq_puts(m, ",dyn");
if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags)) if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags))
seq_puts(m, ",autocell"); seq_puts(m, ",autocell");
switch (as->flock_mode) {
case afs_flock_mode_unset: break;
case afs_flock_mode_local: p = "local"; break;
case afs_flock_mode_openafs: p = "openafs"; break;
case afs_flock_mode_strict: p = "strict"; break;
case afs_flock_mode_write: p = "write"; break;
}
if (p)
seq_printf(m, ",flock=%s", p);
return 0; return 0;
} }
...@@ -315,6 +337,10 @@ static int afs_parse_param(struct fs_context *fc, struct fs_parameter *param) ...@@ -315,6 +337,10 @@ static int afs_parse_param(struct fs_context *fc, struct fs_parameter *param)
ctx->dyn_root = true; ctx->dyn_root = true;
break; break;
case Opt_flock:
ctx->flock_mode = result.uint_32;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -466,6 +492,7 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc) ...@@ -466,6 +492,7 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc)
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
if (as) { if (as) {
as->net_ns = get_net(fc->net_ns); as->net_ns = get_net(fc->net_ns);
as->flock_mode = ctx->flock_mode;
if (ctx->dyn_root) { if (ctx->dyn_root) {
as->dyn_root = true; as->dyn_root = true;
} else { } else {
......
...@@ -1860,7 +1860,7 @@ int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type) ...@@ -1860,7 +1860,7 @@ int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
yfs_check_req(call, bp); yfs_check_req(call, bp);
afs_use_fs_server(call, fc->cbi); afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid); trace_afs_make_fs_calli(call, &vnode->fid, type);
afs_make_call(&fc->ac, call, GFP_NOFS); afs_make_call(&fc->ac, call, GFP_NOFS);
return afs_wait_for_call_to_complete(call, &fc->ac); return afs_wait_for_call_to_complete(call, &fc->ac);
} }
......
...@@ -539,6 +539,41 @@ TRACE_EVENT(afs_make_fs_call, ...@@ -539,6 +539,41 @@ TRACE_EVENT(afs_make_fs_call,
__print_symbolic(__entry->op, afs_fs_operations)) __print_symbolic(__entry->op, afs_fs_operations))
); );
TRACE_EVENT(afs_make_fs_calli,
TP_PROTO(struct afs_call *call, const struct afs_fid *fid,
unsigned int i),
TP_ARGS(call, fid, i),
TP_STRUCT__entry(
__field(unsigned int, call )
__field(unsigned int, i )
__field(enum afs_fs_operation, op )
__field_struct(struct afs_fid, fid )
),
TP_fast_assign(
__entry->call = call->debug_id;
__entry->i = i;
__entry->op = call->operation_ID;
if (fid) {
__entry->fid = *fid;
} else {
__entry->fid.vid = 0;
__entry->fid.vnode = 0;
__entry->fid.unique = 0;
}
),
TP_printk("c=%08x %06llx:%06llx:%06x %s i=%u",
__entry->call,
__entry->fid.vid,
__entry->fid.vnode,
__entry->fid.unique,
__print_symbolic(__entry->op, afs_fs_operations),
__entry->i)
);
TRACE_EVENT(afs_make_fs_call1, TRACE_EVENT(afs_make_fs_call1,
TP_PROTO(struct afs_call *call, const struct afs_fid *fid, TP_PROTO(struct afs_call *call, const struct afs_fid *fid,
const char *name), const char *name),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment