File locking related changes for v3.20 (pile #1)
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJU1MYmAAoJEAAOaEEZVoIV/rAQAKoHj/PCOATTy05lF/NDhJlS 6NbNjupnC8HrbNPv6Z/cQ902eC1YRVH96gf6we4FeAm9Tjctpje6uEqvPQCUxpot 2jWgCG+g95OeEaQEjXQvR3x5ZfXvPUtwKVOnMF423L1p5Xfbj3kJfGi+dv2k8XOi GArsUB7uCwqLyyz+L47RJ2Cz7s47M9O25HkVRfWlgYOv+4afq5OpADGKQAhMLL/s CPhYgqw/7r1p+pLkjUE/x+5BAliDzUinFtDatgD4CeHOdq0RKlxzQ1rFg6uJVg/k 3ZttGOxWUtGIeGM4v5cosDFReLPCESax/TUzn58jxxFR702MjHAA+lHRgjZoWvW/ 9EnShl0XlznQX1ns6f0rI1seWe4M5R3CWus8AcG0kDmdbTp8nARo+pBLFhCME/kZ 15GHLz4tDSRt5SNow6aqJdlYJR7p3WrsceKyM5aH9M7odM3eaB5vJxIJ0fljsZbS Qtz4t+Ua1oVSYD7TX3y7EUiQVPVo8VKS3o6Ua73wCHIXNbSH7hZLOvPLFs6V1Psi RKqRiad5iO3+iavVGuDDcs12zXZ5hmksE8oMh0NkjFZ6wJlO4Hf5iOt5thABNDmT Km+40IBq1DYwclPTofaRpB+ytDOnWedMxdWfWdEWQ710zuuNY3cfi/XMXEX34kBY fLhUMabqcyfUegpA6S0R =6+UV -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
4b4f8580a4
13 changed files with 510 additions and 405 deletions
|
@ -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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
#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)
|
||||||
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,
|
||||||
{
|
{
|
||||||
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 @@ again:
|
||||||
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 @@ again:
|
||||||
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)
|
||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
533
fs/locks.c
533
fs/locks.c
File diff suppressed because it is too large
Load diff
|
@ -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_
|
||||||
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_
|
||||||
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_
|
||||||
/* 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,
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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 @@ out:
|
||||||
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 @@ out:
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@ out_nfserr:
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
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 {
|
||||||
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)
|
||||||
/* 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 {
|
||||||
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 *);
|
||||||
* 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 {
|
||||||
} 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);
|
||||||
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
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* 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)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* 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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue