Commit 4b4f8580 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'locks-v3.20-1' of git://git.samba.org/jlayton/linux

Pull file locking related changes #1 from Jeff Layton:
 "This patchset contains a fairly major overhaul of how file locks are
  tracked within the inode.  Rather than a single list, we now create a
  per-inode "lock context" that contains individual lists for the file
  locks, and a new dedicated spinlock for them.

  There are changes in other trees that are based on top of this set so
  it may be easiest to pull this in early"

* tag 'locks-v3.20-1' of git://git.samba.org/jlayton/linux:
  locks: update comments that refer to inode->i_flock
  locks: consolidate NULL i_flctx checks in locks_remove_file
  locks: keep a count of locks on the flctx lists
  locks: clean up the lm_change prototype
  locks: add a dedicated spinlock to protect i_flctx lists
  locks: remove i_flock field from struct inode
  locks: convert lease handling to file_lock_context
  locks: convert posix locks to file_lock_context
  locks: move flock locks to file_lock_context
  ceph: move spinlocking into ceph_encode_locks_to_buffer and ceph_count_locks
  locks: add a new struct file_locking_context pointer to struct inode
  locks: have locks_release_file use flock_lock_file to release generic flock locks
  locks: add new struct list_head to struct file_lock
parents 87291235 8116bf4c
...@@ -239,23 +239,21 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl) ...@@ -239,23 +239,21 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl)
return err; return err;
} }
/** /*
* Must be called with lock_flocks() already held. Fills in the passed * Fills in the passed counter variables, so you can prepare pagelist metadata
* counter variables, so you can prepare pagelist metadata before calling * before calling ceph_encode_locks.
* ceph_encode_locks.
*/ */
void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count) void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count)
{ {
struct file_lock *lock; struct file_lock_context *ctx;
*fcntl_count = 0; *fcntl_count = 0;
*flock_count = 0; *flock_count = 0;
for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) { ctx = inode->i_flctx;
if (lock->fl_flags & FL_POSIX) if (ctx) {
++(*fcntl_count); *fcntl_count = ctx->flc_posix_cnt;
else if (lock->fl_flags & FL_FLOCK) *flock_count = ctx->flc_flock_cnt;
++(*flock_count);
} }
dout("counted %d flock locks and %d fcntl locks", dout("counted %d flock locks and %d fcntl locks",
*flock_count, *fcntl_count); *flock_count, *fcntl_count);
...@@ -271,6 +269,7 @@ int ceph_encode_locks_to_buffer(struct inode *inode, ...@@ -271,6 +269,7 @@ int ceph_encode_locks_to_buffer(struct inode *inode,
int num_fcntl_locks, int num_flock_locks) int num_fcntl_locks, int num_flock_locks)
{ {
struct file_lock *lock; struct file_lock *lock;
struct file_lock_context *ctx = inode->i_flctx;
int err = 0; int err = 0;
int seen_fcntl = 0; int seen_fcntl = 0;
int seen_flock = 0; int seen_flock = 0;
...@@ -279,8 +278,11 @@ int ceph_encode_locks_to_buffer(struct inode *inode, ...@@ -279,8 +278,11 @@ int ceph_encode_locks_to_buffer(struct inode *inode,
dout("encoding %d flock and %d fcntl locks", num_flock_locks, dout("encoding %d flock and %d fcntl locks", num_flock_locks,
num_fcntl_locks); num_fcntl_locks);
for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) { if (!ctx)
if (lock->fl_flags & FL_POSIX) { return 0;
spin_lock(&ctx->flc_lock);
list_for_each_entry(lock, &ctx->flc_flock, fl_list) {
++seen_fcntl; ++seen_fcntl;
if (seen_fcntl > num_fcntl_locks) { if (seen_fcntl > num_fcntl_locks) {
err = -ENOSPC; err = -ENOSPC;
...@@ -291,9 +293,7 @@ int ceph_encode_locks_to_buffer(struct inode *inode, ...@@ -291,9 +293,7 @@ int ceph_encode_locks_to_buffer(struct inode *inode,
goto fail; goto fail;
++l; ++l;
} }
} list_for_each_entry(lock, &ctx->flc_flock, fl_list) {
for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) {
if (lock->fl_flags & FL_FLOCK) {
++seen_flock; ++seen_flock;
if (seen_flock > num_flock_locks) { if (seen_flock > num_flock_locks) {
err = -ENOSPC; err = -ENOSPC;
...@@ -304,8 +304,8 @@ int ceph_encode_locks_to_buffer(struct inode *inode, ...@@ -304,8 +304,8 @@ int ceph_encode_locks_to_buffer(struct inode *inode,
goto fail; goto fail;
++l; ++l;
} }
}
fail: fail:
spin_unlock(&ctx->flc_lock);
return err; return err;
} }
......
...@@ -2700,20 +2700,16 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, ...@@ -2700,20 +2700,16 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap,
struct ceph_filelock *flocks; struct ceph_filelock *flocks;
encode_again: encode_again:
spin_lock(&inode->i_lock);
ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks); ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks);
spin_unlock(&inode->i_lock);
flocks = kmalloc((num_fcntl_locks+num_flock_locks) * flocks = kmalloc((num_fcntl_locks+num_flock_locks) *
sizeof(struct ceph_filelock), GFP_NOFS); sizeof(struct ceph_filelock), GFP_NOFS);
if (!flocks) { if (!flocks) {
err = -ENOMEM; err = -ENOMEM;
goto out_free; goto out_free;
} }
spin_lock(&inode->i_lock);
err = ceph_encode_locks_to_buffer(inode, flocks, err = ceph_encode_locks_to_buffer(inode, flocks,
num_fcntl_locks, num_fcntl_locks,
num_flock_locks); num_flock_locks);
spin_unlock(&inode->i_lock);
if (err) { if (err) {
kfree(flocks); kfree(flocks);
if (err == -ENOSPC) if (err == -ENOSPC)
......
...@@ -1113,11 +1113,6 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile) ...@@ -1113,11 +1113,6 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
return rc; return rc;
} }
/* copied from fs/locks.c with a name change */
#define cifs_for_each_lock(inode, lockp) \
for (lockp = &inode->i_flock; *lockp != NULL; \
lockp = &(*lockp)->fl_next)
struct lock_to_push { struct lock_to_push {
struct list_head llist; struct list_head llist;
__u64 offset; __u64 offset;
...@@ -1132,8 +1127,9 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) ...@@ -1132,8 +1127,9 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
{ {
struct inode *inode = cfile->dentry->d_inode; struct inode *inode = cfile->dentry->d_inode;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct file_lock *flock, **before; struct file_lock *flock;
unsigned int count = 0, i = 0; struct file_lock_context *flctx = inode->i_flctx;
unsigned int i;
int rc = 0, xid, type; int rc = 0, xid, type;
struct list_head locks_to_send, *el; struct list_head locks_to_send, *el;
struct lock_to_push *lck, *tmp; struct lock_to_push *lck, *tmp;
...@@ -1141,21 +1137,17 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) ...@@ -1141,21 +1137,17 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
xid = get_xid(); xid = get_xid();
spin_lock(&inode->i_lock); if (!flctx)
cifs_for_each_lock(inode, before) { goto out;
if ((*before)->fl_flags & FL_POSIX)
count++;
}
spin_unlock(&inode->i_lock);
INIT_LIST_HEAD(&locks_to_send); INIT_LIST_HEAD(&locks_to_send);
/* /*
* Allocating count locks is enough because no FL_POSIX locks can be * Allocating flc_posix_cnt locks is enough because no FL_POSIX locks
* added to the list while we are holding cinode->lock_sem that * can be added to the list while we are holding cinode->lock_sem that
* protects locking operations of this inode. * protects locking operations of this inode.
*/ */
for (; i < count; i++) { for (i = 0; i < flctx->flc_posix_cnt; i++) {
lck = kmalloc(sizeof(struct lock_to_push), GFP_KERNEL); lck = kmalloc(sizeof(struct lock_to_push), GFP_KERNEL);
if (!lck) { if (!lck) {
rc = -ENOMEM; rc = -ENOMEM;
...@@ -1165,11 +1157,8 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) ...@@ -1165,11 +1157,8 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
} }
el = locks_to_send.next; el = locks_to_send.next;
spin_lock(&inode->i_lock); spin_lock(&flctx->flc_lock);
cifs_for_each_lock(inode, before) { list_for_each_entry(flock, &flctx->flc_posix, fl_list) {
flock = *before;
if ((flock->fl_flags & FL_POSIX) == 0)
continue;
if (el == &locks_to_send) { if (el == &locks_to_send) {
/* /*
* The list ended. We don't have enough allocated * The list ended. We don't have enough allocated
...@@ -1189,9 +1178,8 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) ...@@ -1189,9 +1178,8 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
lck->length = length; lck->length = length;
lck->type = type; lck->type = type;
lck->offset = flock->fl_start; lck->offset = flock->fl_start;
el = el->next;
} }
spin_unlock(&inode->i_lock); spin_unlock(&flctx->flc_lock);
list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) {
int stored_rc; int stored_rc;
......
...@@ -194,7 +194,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode) ...@@ -194,7 +194,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
#ifdef CONFIG_FSNOTIFY #ifdef CONFIG_FSNOTIFY
inode->i_fsnotify_mask = 0; inode->i_fsnotify_mask = 0;
#endif #endif
inode->i_flctx = NULL;
this_cpu_inc(nr_inodes); this_cpu_inc(nr_inodes);
return 0; return 0;
...@@ -237,6 +237,7 @@ void __destroy_inode(struct inode *inode) ...@@ -237,6 +237,7 @@ void __destroy_inode(struct inode *inode)
BUG_ON(inode_has_buffers(inode)); BUG_ON(inode_has_buffers(inode));
security_inode_free(inode); security_inode_free(inode);
fsnotify_inode_delete(inode); fsnotify_inode_delete(inode);
locks_free_lock_context(inode->i_flctx);
if (!inode->i_nlink) { if (!inode->i_nlink) {
WARN_ON(atomic_long_read(&inode->i_sb->s_remove_count) == 0); WARN_ON(atomic_long_read(&inode->i_sb->s_remove_count) == 0);
atomic_long_dec(&inode->i_sb->s_remove_count); atomic_long_dec(&inode->i_sb->s_remove_count);
......
...@@ -164,12 +164,15 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, ...@@ -164,12 +164,15 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
{ {
struct inode *inode = nlmsvc_file_inode(file); struct inode *inode = nlmsvc_file_inode(file);
struct file_lock *fl; struct file_lock *fl;
struct file_lock_context *flctx = inode->i_flctx;
struct nlm_host *lockhost; struct nlm_host *lockhost;
if (!flctx || list_empty_careful(&flctx->flc_posix))
return 0;
again: again:
file->f_locks = 0; file->f_locks = 0;
spin_lock(&inode->i_lock); spin_lock(&flctx->flc_lock);
for (fl = inode->i_flock; fl; fl = fl->fl_next) { list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
if (fl->fl_lmops != &nlmsvc_lock_operations) if (fl->fl_lmops != &nlmsvc_lock_operations)
continue; continue;
...@@ -180,7 +183,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, ...@@ -180,7 +183,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
if (match(lockhost, host)) { if (match(lockhost, host)) {
struct file_lock lock = *fl; struct file_lock lock = *fl;
spin_unlock(&inode->i_lock); spin_unlock(&flctx->flc_lock);
lock.fl_type = F_UNLCK; lock.fl_type = F_UNLCK;
lock.fl_start = 0; lock.fl_start = 0;
lock.fl_end = OFFSET_MAX; lock.fl_end = OFFSET_MAX;
...@@ -192,7 +195,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, ...@@ -192,7 +195,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
goto again; goto again;
} }
} }
spin_unlock(&inode->i_lock); spin_unlock(&flctx->flc_lock);
return 0; return 0;
} }
...@@ -223,18 +226,21 @@ nlm_file_inuse(struct nlm_file *file) ...@@ -223,18 +226,21 @@ nlm_file_inuse(struct nlm_file *file)
{ {
struct inode *inode = nlmsvc_file_inode(file); struct inode *inode = nlmsvc_file_inode(file);
struct file_lock *fl; struct file_lock *fl;
struct file_lock_context *flctx = inode->i_flctx;
if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares) if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
return 1; return 1;
spin_lock(&inode->i_lock); if (flctx && !list_empty_careful(&flctx->flc_posix)) {
for (fl = inode->i_flock; fl; fl = fl->fl_next) { spin_lock(&flctx->flc_lock);
list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
if (fl->fl_lmops == &nlmsvc_lock_operations) { if (fl->fl_lmops == &nlmsvc_lock_operations) {
spin_unlock(&inode->i_lock); spin_unlock(&flctx->flc_lock);
return 1; return 1;
} }
} }
spin_unlock(&inode->i_lock); spin_unlock(&flctx->flc_lock);
}
file->f_locks = 0; file->f_locks = 0;
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -85,25 +85,30 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ ...@@ -85,25 +85,30 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
{ {
struct inode *inode = state->inode; struct inode *inode = state->inode;
struct file_lock *fl; struct file_lock *fl;
struct file_lock_context *flctx = inode->i_flctx;
struct list_head *list;
int status = 0; int status = 0;
if (inode->i_flock == NULL) if (flctx == NULL)
goto out; goto out;
/* Protect inode->i_flock using the i_lock */ list = &flctx->flc_posix;
spin_lock(&inode->i_lock); spin_lock(&flctx->flc_lock);
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { restart:
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) list_for_each_entry(fl, list, fl_list) {
continue;
if (nfs_file_open_context(fl->fl_file) != ctx) if (nfs_file_open_context(fl->fl_file) != ctx)
continue; continue;
spin_unlock(&inode->i_lock); spin_unlock(&flctx->flc_lock);
status = nfs4_lock_delegation_recall(fl, state, stateid); status = nfs4_lock_delegation_recall(fl, state, stateid);
if (status < 0) if (status < 0)
goto out; goto out;
spin_lock(&inode->i_lock); spin_lock(&flctx->flc_lock);
} }
spin_unlock(&inode->i_lock); if (list == &flctx->flc_posix) {
list = &flctx->flc_flock;
goto restart;
}
spin_unlock(&flctx->flc_lock);
out: out:
return status; return status;
} }
......
...@@ -1366,20 +1366,22 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ ...@@ -1366,20 +1366,22 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct file_lock *fl; struct file_lock *fl;
int status = 0; int status = 0;
struct file_lock_context *flctx = inode->i_flctx;
struct list_head *list;
if (inode->i_flock == NULL) if (flctx == NULL)
return 0; return 0;
list = &flctx->flc_posix;
/* Guard against delegation returns and new lock/unlock calls */ /* Guard against delegation returns and new lock/unlock calls */
down_write(&nfsi->rwsem); down_write(&nfsi->rwsem);
/* Protect inode->i_flock using the BKL */ spin_lock(&flctx->flc_lock);
spin_lock(&inode->i_lock); restart:
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { list_for_each_entry(fl, list, fl_list) {
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
continue;
if (nfs_file_open_context(fl->fl_file)->state != state) if (nfs_file_open_context(fl->fl_file)->state != state)
continue; continue;
spin_unlock(&inode->i_lock); spin_unlock(&flctx->flc_lock);
status = ops->recover_lock(state, fl); status = ops->recover_lock(state, fl);
switch (status) { switch (status) {
case 0: case 0:
...@@ -1397,7 +1399,7 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ ...@@ -1397,7 +1399,7 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
goto out; goto out;
default: default:
printk(KERN_ERR "NFS: %s: unhandled error %d\n", pr_err("NFS: %s: unhandled error %d\n",
__func__, status); __func__, status);
case -ENOMEM: case -ENOMEM:
case -NFS4ERR_DENIED: case -NFS4ERR_DENIED:
...@@ -1406,9 +1408,13 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ ...@@ -1406,9 +1408,13 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
/* kill_proc(fl->fl_pid, SIGLOST, 1); */ /* kill_proc(fl->fl_pid, SIGLOST, 1); */
status = 0; status = 0;
} }
spin_lock(&inode->i_lock); spin_lock(&flctx->flc_lock);
} }
spin_unlock(&inode->i_lock); if (list == &flctx->flc_posix) {
list = &flctx->flc_flock;
goto restart;
}
spin_unlock(&flctx->flc_lock);
out: out:
up_write(&nfsi->rwsem); up_write(&nfsi->rwsem);
return status; return status;
......
...@@ -826,11 +826,15 @@ static bool nfs_can_coalesce_requests(struct nfs_page *prev, ...@@ -826,11 +826,15 @@ static bool nfs_can_coalesce_requests(struct nfs_page *prev,
struct nfs_pageio_descriptor *pgio) struct nfs_pageio_descriptor *pgio)
{ {
size_t size; size_t size;
struct file_lock_context *flctx;
if (prev) { if (prev) {
if (!nfs_match_open_context(req->wb_context, prev->wb_context)) if (!nfs_match_open_context(req->wb_context, prev->wb_context))
return false; return false;
if (req->wb_context->dentry->d_inode->i_flock != NULL && flctx = req->wb_context->dentry->d_inode->i_flctx;
if (flctx != NULL &&
!(list_empty_careful(&flctx->flc_posix) &&
list_empty_careful(&flctx->flc_flock)) &&
!nfs_match_lock_context(req->wb_lock_context, !nfs_match_lock_context(req->wb_lock_context,
prev->wb_lock_context)) prev->wb_lock_context))
return false; return false;
......
...@@ -1091,6 +1091,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page) ...@@ -1091,6 +1091,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
{ {
struct nfs_open_context *ctx = nfs_file_open_context(file); struct nfs_open_context *ctx = nfs_file_open_context(file);
struct nfs_lock_context *l_ctx; struct nfs_lock_context *l_ctx;
struct file_lock_context *flctx = file_inode(file)->i_flctx;
struct nfs_page *req; struct nfs_page *req;
int do_flush, status; int do_flush, status;
/* /*
...@@ -1109,7 +1110,9 @@ int nfs_flush_incompatible(struct file *file, struct page *page) ...@@ -1109,7 +1110,9 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
do_flush = req->wb_page != page || req->wb_context != ctx; do_flush = req->wb_page != page || req->wb_context != ctx;
/* for now, flush if more than 1 request in page_group */ /* for now, flush if more than 1 request in page_group */
do_flush |= req->wb_this_page != req; do_flush |= req->wb_this_page != req;
if (l_ctx && ctx->dentry->d_inode->i_flock != NULL) { if (l_ctx && flctx &&
!(list_empty_careful(&flctx->flc_posix) &&
list_empty_careful(&flctx->flc_flock))) {
do_flush |= l_ctx->lockowner.l_owner != current->files do_flush |= l_ctx->lockowner.l_owner != current->files
|| l_ctx->lockowner.l_pid != current->tgid; || l_ctx->lockowner.l_pid != current->tgid;
} }
...@@ -1170,6 +1173,13 @@ static bool nfs_write_pageuptodate(struct page *page, struct inode *inode) ...@@ -1170,6 +1173,13 @@ static bool nfs_write_pageuptodate(struct page *page, struct inode *inode)
return PageUptodate(page) != 0; return PageUptodate(page) != 0;
} }
static bool
is_whole_file_wrlock(struct file_lock *fl)
{
return fl->fl_start == 0 && fl->fl_end == OFFSET_MAX &&
fl->fl_type == F_WRLCK;
}
/* If we know the page is up to date, and we're not using byte range locks (or /* If we know the page is up to date, and we're not using byte range locks (or
* if we have the whole file locked for writing), it may be more efficient to * if we have the whole file locked for writing), it may be more efficient to
* extend the write to cover the entire page in order to avoid fragmentation * extend the write to cover the entire page in order to avoid fragmentation
...@@ -1180,17 +1190,36 @@ static bool nfs_write_pageuptodate(struct page *page, struct inode *inode) ...@@ -1180,17 +1190,36 @@ static bool nfs_write_pageuptodate(struct page *page, struct inode *inode)
*/ */
static int nfs_can_extend_write(struct file *file, struct page *page, struct inode *inode) static int nfs_can_extend_write(struct file *file, struct page *page, struct inode *inode)
{ {
int ret;
struct file_lock_context *flctx = inode->i_flctx;
struct file_lock *fl;
if (file->f_flags & O_DSYNC) if (file->f_flags & O_DSYNC)
return 0; return 0;
if (!nfs_write_pageuptodate(page, inode)) if (!nfs_write_pageuptodate(page, inode))
return 0; return 0;
if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE)) if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
return 1; return 1;
if (inode->i_flock == NULL || (inode->i_flock->fl_start == 0 && if (!flctx || (list_empty_careful(&flctx->flc_flock) &&
inode->i_flock->fl_end == OFFSET_MAX && list_empty_careful(&flctx->flc_posix)))
inode->i_flock->fl_type != F_RDLCK))
return 1;
return 0; return 0;
/* Check to see if there are whole file write locks */
ret = 0;
spin_lock(&flctx->flc_lock);
if (!list_empty(&flctx->flc_posix)) {
fl = list_first_entry(&flctx->flc_posix, struct file_lock,
fl_list);
if (is_whole_file_wrlock(fl))
ret = 1;
} else if (!list_empty(&flctx->flc_flock)) {
fl = list_first_entry(&flctx->flc_flock, struct file_lock,
fl_list);
if (fl->fl_type == F_WRLCK)
ret = 1;
}
spin_unlock(&flctx->flc_lock);
return ret;
} }
/* /*
......
...@@ -3477,7 +3477,8 @@ nfsd_break_deleg_cb(struct file_lock *fl) ...@@ -3477,7 +3477,8 @@ nfsd_break_deleg_cb(struct file_lock *fl)
} }
static int static int
nfsd_change_deleg_cb(struct file_lock **onlist, int arg, struct list_head *dispose) nfsd_change_deleg_cb(struct file_lock *onlist, int arg,
struct list_head *dispose)
{ {
if (arg & F_UNLCK) if (arg & F_UNLCK)
return lease_modify(onlist, arg, dispose); return lease_modify(onlist, arg, dispose);
...@@ -5556,10 +5557,11 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -5556,10 +5557,11 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
static bool static bool
check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner) check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner)
{ {
struct file_lock **flpp; struct file_lock *fl;
int status = false; int status = false;
struct file *filp = find_any_file(fp); struct file *filp = find_any_file(fp);
struct inode *inode; struct inode *inode;
struct file_lock_context *flctx;
if (!filp) { if (!filp) {
/* Any valid lock stateid should have some sort of access */ /* Any valid lock stateid should have some sort of access */
...@@ -5568,15 +5570,18 @@ check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner) ...@@ -5568,15 +5570,18 @@ check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner)
} }
inode = file_inode(filp); inode = file_inode(filp);
flctx = inode->i_flctx;
spin_lock(&inode->i_lock); if (flctx && !list_empty_careful(&flctx->flc_posix)) {
for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) { spin_lock(&flctx->flc_lock);
if ((*flpp)->fl_owner == (fl_owner_t)lowner) { list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
if (fl->fl_owner == (fl_owner_t)lowner) {
status = true; status = true;
break; break;
} }
} }
spin_unlock(&inode->i_lock); spin_unlock(&flctx->flc_lock);
}
fput(filp); fput(filp);
return status; return status;
} }
......
...@@ -358,7 +358,7 @@ int rw_verify_area(int read_write, struct file *file, const loff_t *ppos, size_t ...@@ -358,7 +358,7 @@ int rw_verify_area(int read_write, struct file *file, const loff_t *ppos, size_t
return retval; return retval;
} }
if (unlikely(inode->i_flock && mandatory_lock(inode))) { if (unlikely(inode->i_flctx && mandatory_lock(inode))) {
retval = locks_mandatory_area( retval = locks_mandatory_area(
read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
inode, file, pos, count); inode, file, pos, count);
......
...@@ -625,7 +625,7 @@ struct inode { ...@@ -625,7 +625,7 @@ struct inode {
atomic_t i_readcount; /* struct files open RO */ atomic_t i_readcount; /* struct files open RO */
#endif #endif
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct file_lock *i_flock; struct file_lock_context *i_flctx;
struct address_space i_data; struct address_space i_data;
struct list_head i_devices; struct list_head i_devices;
union { union {
...@@ -885,6 +885,8 @@ static inline struct file *get_file(struct file *f) ...@@ -885,6 +885,8 @@ static inline struct file *get_file(struct file *f)
/* legacy typedef, should eventually be removed */ /* legacy typedef, should eventually be removed */
typedef void *fl_owner_t; typedef void *fl_owner_t;
struct file_lock;
struct file_lock_operations { struct file_lock_operations {
void (*fl_copy_lock)(struct file_lock *, struct file_lock *); void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
void (*fl_release_private)(struct file_lock *); void (*fl_release_private)(struct file_lock *);
...@@ -898,7 +900,7 @@ struct lock_manager_operations { ...@@ -898,7 +900,7 @@ struct lock_manager_operations {
void (*lm_notify)(struct file_lock *); /* unblock callback */ void (*lm_notify)(struct file_lock *); /* unblock callback */
int (*lm_grant)(struct file_lock *, int); int (*lm_grant)(struct file_lock *, int);
bool (*lm_break)(struct file_lock *); bool (*lm_break)(struct file_lock *);
int (*lm_change)(struct file_lock **, int, struct list_head *); int (*lm_change)(struct file_lock *, int, struct list_head *);
void (*lm_setup)(struct file_lock *, void **); void (*lm_setup)(struct file_lock *, void **);
}; };
...@@ -923,17 +925,17 @@ int locks_in_grace(struct net *); ...@@ -923,17 +925,17 @@ int locks_in_grace(struct net *);
* FIXME: should we create a separate "struct lock_request" to help distinguish * FIXME: should we create a separate "struct lock_request" to help distinguish
* these two uses? * these two uses?
* *
* The i_flock list is ordered by: * The varous i_flctx lists are ordered by:
* *
* 1) lock type -- FL_LEASEs first, then FL_FLOCK, and finally FL_POSIX * 1) lock owner
* 2) lock owner * 2) lock range start
* 3) lock range start * 3) lock range end
* 4) lock range end
* *
* Obviously, the last two criteria only matter for POSIX locks. * Obviously, the last two criteria only matter for POSIX locks.
*/ */
struct file_lock { struct file_lock {
struct file_lock *fl_next; /* singly linked list for this inode */ struct file_lock *fl_next; /* singly linked list for this inode */
struct list_head fl_list; /* link into file_lock_context */
struct hlist_node fl_link; /* node in global lists */ struct hlist_node fl_link; /* node in global lists */
struct list_head fl_block; /* circular list of blocked processes */ struct list_head fl_block; /* circular list of blocked processes */
fl_owner_t fl_owner; fl_owner_t fl_owner;
...@@ -964,6 +966,16 @@ struct file_lock { ...@@ -964,6 +966,16 @@ struct file_lock {
} fl_u; } fl_u;
}; };
struct file_lock_context {
spinlock_t flc_lock;
struct list_head flc_flock;
struct list_head flc_posix;
struct list_head flc_lease;
int flc_flock_cnt;
int flc_posix_cnt;
int flc_lease_cnt;
};
/* The following constant reflects the upper bound of the file/locking space */ /* The following constant reflects the upper bound of the file/locking space */
#ifndef OFFSET_MAX #ifndef OFFSET_MAX
#define INT_LIMIT(x) (~((x)1 << (sizeof(x)*8 - 1))) #define INT_LIMIT(x) (~((x)1 << (sizeof(x)*8 - 1)))
...@@ -990,6 +1002,7 @@ extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg); ...@@ -990,6 +1002,7 @@ extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg);
extern int fcntl_getlease(struct file *filp); extern int fcntl_getlease(struct file *filp);
/* fs/locks.c */ /* fs/locks.c */
void locks_free_lock_context(struct file_lock_context *ctx);
void locks_free_lock(struct file_lock *fl); void locks_free_lock(struct file_lock *fl);
extern void locks_init_lock(struct file_lock *); extern void locks_init_lock(struct file_lock *);
extern struct file_lock * locks_alloc_lock(void); extern struct file_lock * locks_alloc_lock(void);
...@@ -1010,7 +1023,7 @@ extern int __break_lease(struct inode *inode, unsigned int flags, unsigned int t ...@@ -1010,7 +1023,7 @@ extern int __break_lease(struct inode *inode, unsigned int flags, unsigned int t
extern void lease_get_mtime(struct inode *, struct timespec *time); extern void lease_get_mtime(struct inode *, struct timespec *time);
extern int generic_setlease(struct file *, long, struct file_lock **, void **priv); extern int generic_setlease(struct file *, long, struct file_lock **, void **priv);
extern int vfs_setlease(struct file *, long, struct file_lock **, void **); extern int vfs_setlease(struct file *, long, struct file_lock **, void **);
extern int lease_modify(struct file_lock **, int, struct list_head *); extern int lease_modify(struct file_lock *, int, struct list_head *);
#else /* !CONFIG_FILE_LOCKING */ #else /* !CONFIG_FILE_LOCKING */
static inline int fcntl_getlk(struct file *file, unsigned int cmd, static inline int fcntl_getlk(struct file *file, unsigned int cmd,
struct flock __user *user) struct flock __user *user)
...@@ -1047,6 +1060,11 @@ static inline int fcntl_getlease(struct file *filp) ...@@ -1047,6 +1060,11 @@ static inline int fcntl_getlease(struct file *filp)
return F_UNLCK; return F_UNLCK;
} }
static inline void
locks_free_lock_context(struct file_lock_context *ctx)
{
}
static inline void locks_init_lock(struct file_lock *fl) static inline void locks_init_lock(struct file_lock *fl)
{ {
return; return;
...@@ -1137,7 +1155,7 @@ static inline int vfs_setlease(struct file *filp, long arg, ...@@ -1137,7 +1155,7 @@ static inline int vfs_setlease(struct file *filp, long arg,
return -EINVAL; return -EINVAL;
} }
static inline int lease_modify(struct file_lock **before, int arg, static inline int lease_modify(struct file_lock *fl, int arg,
struct list_head *dispose) struct list_head *dispose)
{ {
return -EINVAL; return -EINVAL;
...@@ -1959,7 +1977,7 @@ static inline int locks_verify_truncate(struct inode *inode, ...@@ -1959,7 +1977,7 @@ static inline int locks_verify_truncate(struct inode *inode,
struct file *filp, struct file *filp,
loff_t size) loff_t size)
{ {
if (inode->i_flock && mandatory_lock(inode)) if (inode->i_flctx && mandatory_lock(inode))
return locks_mandatory_area( return locks_mandatory_area(
FLOCK_VERIFY_WRITE, inode, filp, FLOCK_VERIFY_WRITE, inode, filp,
size < inode->i_size ? size : inode->i_size, size < inode->i_size ? size : inode->i_size,
...@@ -1973,11 +1991,12 @@ static inline int break_lease(struct inode *inode, unsigned int mode) ...@@ -1973,11 +1991,12 @@ static inline int break_lease(struct inode *inode, unsigned int mode)
{ {
/* /*
* Since this check is lockless, we must ensure that any refcounts * Since this check is lockless, we must ensure that any refcounts
* taken are done before checking inode->i_flock. Otherwise, we could * taken are done before checking i_flctx->flc_lease. Otherwise, we
* end up racing with tasks trying to set a new lease on this file. * could end up racing with tasks trying to set a new lease on this
* file.
*/ */
smp_mb(); smp_mb();
if (inode->i_flock) if (inode->i_flctx && !list_empty_careful(&inode->i_flctx->flc_lease))
return __break_lease(inode, mode, FL_LEASE); return __break_lease(inode, mode, FL_LEASE);
return 0; return 0;
} }
...@@ -1986,11 +2005,12 @@ static inline int break_deleg(struct inode *inode, unsigned int mode) ...@@ -1986,11 +2005,12 @@ static inline int break_deleg(struct inode *inode, unsigned int mode)
{ {
/* /*
* Since this check is lockless, we must ensure that any refcounts * Since this check is lockless, we must ensure that any refcounts
* taken are done before checking inode->i_flock. Otherwise, we could * taken are done before checking i_flctx->flc_lease. Otherwise, we
* end up racing with tasks trying to set a new lease on this file. * could end up racing with tasks trying to set a new lease on this
* file.
*/ */
smp_mb(); smp_mb();
if (inode->i_flock) if (inode->i_flctx && !list_empty_careful(&inode->i_flctx->flc_lease))
return __break_lease(inode, mode, FL_DELEG); return __break_lease(inode, mode, FL_DELEG);
return 0; return 0;
} }
......
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