Skip to content

Commit

Permalink
CIFS: Implement caching mechanism for mandatory brlocks
Browse files Browse the repository at this point in the history
If we have an oplock and negotiate mandatory locking style we handle
all brlock requests on the client.

Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Acked-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
  • Loading branch information
Pavel Shilovsky authored and Steve French committed Oct 24, 2011
1 parent 42274bb commit 85160e0
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 11 deletions.
2 changes: 2 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@ extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);
*/
struct cifsLockInfo {
struct list_head llist; /* pointer to next cifsLockInfo */
struct list_head blist; /* pointer to locks blocked on this */
wait_queue_head_t block_q;
__u64 offset;
__u64 length;
__u32 pid;
Expand Down
206 changes: 195 additions & 11 deletions fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,11 +275,14 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file,
spin_unlock(&cifs_file_list_lock);

cifs_set_oplock_level(pCifsInode, oplock);
pCifsInode->can_cache_brlcks = pCifsInode->clientCanCacheAll;

file->private_data = pCifsFile;
return pCifsFile;
}

static void cifs_del_lock_waiters(struct cifsLockInfo *lock);

/*
* Release a reference on the file private data. This may involve closing
* the filehandle out on the server. Must be called without holding
Expand Down Expand Up @@ -335,6 +338,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
if (li->netfid != cifs_file->netfid)
continue;
list_del(&li->llist);
cifs_del_lock_waiters(li);
kfree(li);
}
mutex_unlock(&cifsi->lock_mutex);
Expand Down Expand Up @@ -640,24 +644,182 @@ int cifs_closedir(struct inode *inode, struct file *file)
return rc;
}

static int store_file_lock(struct cifsInodeInfo *cinode, __u64 len,
__u64 offset, __u8 type, __u16 netfid)
static struct cifsLockInfo *
cifs_lock_init(__u64 len, __u64 offset, __u8 type, __u16 netfid)
{
struct cifsLockInfo *li =
kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL);
if (li == NULL)
return -ENOMEM;
if (!li)
return li;
li->netfid = netfid;
li->offset = offset;
li->length = len;
li->type = type;
li->pid = current->tgid;
INIT_LIST_HEAD(&li->blist);
init_waitqueue_head(&li->block_q);
return li;
}

static void
cifs_del_lock_waiters(struct cifsLockInfo *lock)
{
struct cifsLockInfo *li, *tmp;
list_for_each_entry_safe(li, tmp, &lock->blist, blist) {
list_del_init(&li->blist);
wake_up(&li->block_q);
}
}

static bool
cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset,
__u64 length, __u8 type, __u16 netfid,
struct cifsLockInfo **conf_lock)
{
struct cifsLockInfo *li, *tmp;

list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
if (offset + length <= li->offset ||
offset >= li->offset + li->length)
continue;
else if ((type & LOCKING_ANDX_SHARED_LOCK) &&
((netfid == li->netfid && current->tgid == li->pid) ||
type == li->type))
continue;
else {
*conf_lock = li;
return true;
}
}
return false;
}

static int
cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length,
__u8 type, __u16 netfid, struct file_lock *flock)
{
int rc = 0;
struct cifsLockInfo *conf_lock;
bool exist;

mutex_lock(&cinode->lock_mutex);

exist = cifs_find_lock_conflict(cinode, offset, length, type, netfid,
&conf_lock);
if (exist) {
flock->fl_start = conf_lock->offset;
flock->fl_end = conf_lock->offset + conf_lock->length - 1;
flock->fl_pid = conf_lock->pid;
if (conf_lock->type & LOCKING_ANDX_SHARED_LOCK)
flock->fl_type = F_RDLCK;
else
flock->fl_type = F_WRLCK;
} else if (!cinode->can_cache_brlcks)
rc = 1;
else
flock->fl_type = F_UNLCK;

mutex_unlock(&cinode->lock_mutex);
return rc;
}

static int
cifs_lock_add(struct cifsInodeInfo *cinode, __u64 len, __u64 offset,
__u8 type, __u16 netfid)
{
struct cifsLockInfo *li;

li = cifs_lock_init(len, offset, type, netfid);
if (!li)
return -ENOMEM;

mutex_lock(&cinode->lock_mutex);
list_add_tail(&li->llist, &cinode->llist);
mutex_unlock(&cinode->lock_mutex);
return 0;
}

static int
cifs_lock_add_if(struct cifsInodeInfo *cinode, __u64 offset, __u64 length,
__u8 type, __u16 netfid, bool wait)
{
struct cifsLockInfo *lock, *conf_lock;
bool exist;
int rc = 0;

lock = cifs_lock_init(length, offset, type, netfid);
if (!lock)
return -ENOMEM;

try_again:
exist = false;
mutex_lock(&cinode->lock_mutex);

exist = cifs_find_lock_conflict(cinode, offset, length, type, netfid,
&conf_lock);
if (!exist && cinode->can_cache_brlcks) {
list_add_tail(&lock->llist, &cinode->llist);
mutex_unlock(&cinode->lock_mutex);
return rc;
}

if (!exist)
rc = 1;
else if (!wait)
rc = -EACCES;
else {
list_add_tail(&lock->blist, &conf_lock->blist);
mutex_unlock(&cinode->lock_mutex);
rc = wait_event_interruptible(lock->block_q,
(lock->blist.prev == &lock->blist) &&
(lock->blist.next == &lock->blist));
if (!rc)
goto try_again;
else {
mutex_lock(&cinode->lock_mutex);
list_del_init(&lock->blist);
mutex_unlock(&cinode->lock_mutex);
}
}

kfree(lock);
mutex_unlock(&cinode->lock_mutex);
return rc;
}

static int
cifs_push_locks(struct cifsFileInfo *cfile)
{
int xid, rc = 0, stored_rc;
struct cifsLockInfo *li, *tmp;
struct cifs_tcon *tcon;
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);

xid = GetXid();
tcon = tlink_tcon(cfile->tlink);

mutex_lock(&cinode->lock_mutex);
if (!cinode->can_cache_brlcks) {
mutex_unlock(&cinode->lock_mutex);
FreeXid(xid);
return rc;
}

list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
stored_rc = CIFSSMBLock(xid, tcon, cfile->netfid,
li->pid, li->length, li->offset,
0, 1, li->type, 0, 0);
if (stored_rc)
rc = stored_rc;
}

cinode->can_cache_brlcks = false;
mutex_unlock(&cinode->lock_mutex);

FreeXid(xid);
return rc;
}

static void
cifs_read_flock(struct file_lock *flock, __u8 *type, int *lock, int *unlock,
bool *wait_flag)
Expand Down Expand Up @@ -708,6 +870,7 @@ cifs_getlk(struct cifsFileInfo *cfile, struct file_lock *flock, __u8 type,
{
int rc = 0;
__u64 length = 1 + flock->fl_end - flock->fl_start;
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
__u16 netfid = cfile->netfid;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);

Expand All @@ -723,6 +886,11 @@ cifs_getlk(struct cifsFileInfo *cfile, struct file_lock *flock, __u8 type,
return rc;
}

rc = cifs_lock_test(cinode, flock->fl_start, length, type, netfid,
flock);
if (!rc)
return rc;

/* BB we could chain these into one lock request BB */
rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length,
flock->fl_start, 0, 1, type, 0, 0);
Expand Down Expand Up @@ -790,12 +958,19 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type,
}

if (lock) {
rc = cifs_lock_add_if(cinode, flock->fl_start, length,
type, netfid, wait_flag);
if (rc < 0)
return rc;
else if (!rc)
goto out;

rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length,
flock->fl_start, 0, lock, type, wait_flag, 0);
flock->fl_start, 0, 1, type, wait_flag, 0);
if (rc == 0) {
/* For Windows locks we must store them. */
rc = store_file_lock(cinode, length, flock->fl_start,
type, netfid);
rc = cifs_lock_add(cinode, length, flock->fl_start,
type, netfid);
}
} else if (unlock) {
/*
Expand All @@ -816,14 +991,19 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type,
if (cfile->netfid != li->netfid)
continue;

stored_rc = CIFSSMBLock(xid, tcon, netfid,
current->tgid, li->length,
li->offset, 1, 0, li->type,
0, 0);
if (!cinode->can_cache_brlcks)
stored_rc = CIFSSMBLock(xid, tcon, netfid,
current->tgid,
li->length, li->offset,
1, 0, li->type, 0, 0);
else
stored_rc = 0;

if (stored_rc)
rc = stored_rc;
else {
list_del(&li->llist);
cifs_del_lock_waiters(li);
kfree(li);
}
}
Expand Down Expand Up @@ -2404,6 +2584,10 @@ void cifs_oplock_break(struct work_struct *work)
cFYI(1, "Oplock flush inode %p rc %d", inode, rc);
}

rc = cifs_push_locks(cfile);
if (rc)
cERROR(1, "Push locks rc = %d", rc);

/*
* releasing stale oplock after recent reconnect of smb session using
* a now incorrect file handle is not a data integrity issue but do
Expand Down

0 comments on commit 85160e0

Please sign in to comment.