Skip to content

Commit

Permalink
CIFS: Fix fast lease break after open problem
Browse files Browse the repository at this point in the history
Now we walk though cifsFileInfo's list for every incoming lease
break and look for an equivalent there. That approach misses lease
breaks that come just after an open response - we don't have time
to populate new cifsFileInfo structure to the list. Fix this by
adding new list of pending opens and look for a lease there if we
didn't find it in the list of cifsFileInfo structures.

Signed-off-by: Pavel Shilovsky <pshilovsky@etersoft.ru>
Signed-off-by: Steve French <sfrench@us.ibm.com>
  • Loading branch information
Pavel Shilovsky authored and Steve French committed Sep 25, 2012
1 parent 0822f51 commit 233839b
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 10 deletions.
12 changes: 12 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,7 @@ struct cifs_ses {
__u16 session_flags;
#endif /* CONFIG_CIFS_SMB2 */
};

/* no more than one of the following three session flags may be set */
#define CIFS_SES_NT4 1
#define CIFS_SES_OS2 2
Expand Down Expand Up @@ -821,6 +822,7 @@ struct cifs_tcon {
u64 resource_id; /* server resource id */
struct fscache_cookie *fscache; /* cookie for share */
#endif
struct list_head pending_opens; /* list of incomplete opens */
/* BB add field for back pointer to sb struct(s)? */
};

Expand Down Expand Up @@ -863,6 +865,15 @@ cifs_get_tlink(struct tcon_link *tlink)
/* This function is always expected to succeed */
extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);

#define CIFS_OPLOCK_NO_CHANGE 0xfe

struct cifs_pending_open {
struct list_head olist;
struct tcon_link *tlink;
__u8 lease_key[16];
__u32 oplock;
};

/*
* This info hangs off the cifsFileInfo structure, pointed to by llist.
* This is used to track byte stream locks on the file
Expand Down Expand Up @@ -903,6 +914,7 @@ struct cifs_fid {
__u64 volatile_fid; /* volatile file id for smb2 */
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
#endif
struct cifs_pending_open *pending_open;
};

struct cifs_fid_locks {
Expand Down
7 changes: 7 additions & 0 deletions fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
__u64 length, __u8 type,
struct cifsLockInfo **conf_lock,
bool rw_check);
extern void cifs_add_pending_open(struct cifs_fid *fid,
struct tcon_link *tlink,
struct cifs_pending_open *open);
extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
struct tcon_link *tlink,
struct cifs_pending_open *open);
extern void cifs_del_pending_open(struct cifs_pending_open *open);

#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
extern void cifs_dfs_release_automount_timer(void);
Expand Down
1 change: 1 addition & 0 deletions fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -2645,6 +2645,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
tcon->retry = volume_info->retry;
tcon->nocase = volume_info->nocase;
tcon->local_lease = volume_info->local_lease;
INIT_LIST_HEAD(&tcon->pending_opens);

spin_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &ses->tcon_list);
Expand Down
9 changes: 8 additions & 1 deletion fs/cifs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
struct cifs_fid fid;
struct cifs_pending_open open;
__u32 oplock;
struct cifsFileInfo *file_info;

Expand Down Expand Up @@ -423,23 +424,29 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
if (server->ops->new_lease_key)
server->ops->new_lease_key(&fid);

cifs_add_pending_open(&fid, tlink, &open);

rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
&oplock, &fid, opened);

if (rc)
if (rc) {
cifs_del_pending_open(&open);
goto out;
}

rc = finish_open(file, direntry, generic_file_open, opened);
if (rc) {
if (server->ops->close)
server->ops->close(xid, tcon, &fid);
cifs_del_pending_open(&open);
goto out;
}

file_info = cifs_new_fileinfo(&fid, file, tlink, oplock);
if (file_info == NULL) {
if (server->ops->close)
server->ops->close(xid, tcon, &fid);
cifs_del_pending_open(&open);
rc = -ENOMEM;
}

Expand Down
35 changes: 31 additions & 4 deletions fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifsFileInfo *cfile;
struct cifs_fid_locks *fdlocks;
struct cifs_tcon *tcon = tlink_tcon(tlink);

cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
if (cfile == NULL)
Expand Down Expand Up @@ -274,10 +275,15 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cfile->tlink = cifs_get_tlink(tlink);
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
mutex_init(&cfile->fh_mutex);
tlink_tcon(tlink)->ses->server->ops->set_fid(cfile, fid, oplock);

spin_lock(&cifs_file_list_lock);
list_add(&cfile->tlist, &(tlink_tcon(tlink)->openFileList));
if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE)
oplock = fid->pending_open->oplock;
list_del(&fid->pending_open->olist);

tlink_tcon(tlink)->ses->server->ops->set_fid(cfile, fid, oplock);

list_add(&cfile->tlist, &tcon->openFileList);
/* if readable file instance put first in list*/
if (file->f_mode & FMODE_READ)
list_add(&cfile->flist, &cinode->openFileList);
Expand Down Expand Up @@ -307,16 +313,25 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{
struct inode *inode = cifs_file->dentry->d_inode;
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsLockInfo *li, *tmp;
struct cifs_fid fid;
struct cifs_pending_open open;

spin_lock(&cifs_file_list_lock);
if (--cifs_file->count > 0) {
spin_unlock(&cifs_file_list_lock);
return;
}

if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &fid);

/* store open in pending opens to make sure we don't miss lease break */
cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open);

/* remove it from the lists */
list_del(&cifs_file->flist);
list_del(&cifs_file->tlist);
Expand Down Expand Up @@ -348,6 +363,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
free_xid(xid);
}

cifs_del_pending_open(&open);

/*
* Delete any outstanding lock records. We'll lose them when the file
* is closed anyway.
Expand All @@ -368,6 +385,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
}

int cifs_open(struct inode *inode, struct file *file)

{
int rc = -EACCES;
unsigned int xid;
Expand All @@ -380,6 +398,7 @@ int cifs_open(struct inode *inode, struct file *file)
char *full_path = NULL;
bool posix_open_ok = false;
struct cifs_fid fid;
struct cifs_pending_open open;

xid = get_xid();

Expand All @@ -401,7 +420,7 @@ int cifs_open(struct inode *inode, struct file *file)
cFYI(1, "inode = 0x%p file flags are 0x%x for %s",
inode, file->f_flags, full_path);

if (tcon->ses->server->oplocks)
if (server->oplocks)
oplock = REQ_OPLOCK;
else
oplock = 0;
Expand Down Expand Up @@ -434,20 +453,28 @@ int cifs_open(struct inode *inode, struct file *file)
*/
}

if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &fid);

cifs_add_pending_open(&fid, tlink, &open);

if (!posix_open_ok) {
if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &fid);

rc = cifs_nt_open(full_path, inode, cifs_sb, tcon,
file->f_flags, &oplock, &fid, xid);
if (rc)
if (rc) {
cifs_del_pending_open(&open);
goto out;
}
}

cfile = cifs_new_fileinfo(&fid, file, tlink, oplock);
if (cfile == NULL) {
if (server->ops->close)
server->ops->close(xid, tcon, &fid);
cifs_del_pending_open(&open);
rc = -ENOMEM;
goto out;
}
Expand Down
30 changes: 30 additions & 0 deletions fs/cifs/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -579,3 +579,33 @@ backup_cred(struct cifs_sb_info *cifs_sb)

return false;
}

void
cifs_del_pending_open(struct cifs_pending_open *open)
{
spin_lock(&cifs_file_list_lock);
list_del(&open->olist);
spin_unlock(&cifs_file_list_lock);
}

void
cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink,
struct cifs_pending_open *open)
{
#ifdef CONFIG_CIFS_SMB2
memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
#endif
open->oplock = CIFS_OPLOCK_NO_CHANGE;
open->tlink = tlink;
fid->pending_open = open;
list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens);
}

void
cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
struct cifs_pending_open *open)
{
spin_lock(&cifs_file_list_lock);
cifs_add_pending_open_locked(fid, tlink, open);
spin_unlock(&cifs_file_list_lock);
}
74 changes: 69 additions & 5 deletions fs/cifs/smb2misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,27 @@ __u8 smb2_map_lease_to_oplock(__le32 lease_state)
return 0;
}

struct smb2_lease_break_work {
struct work_struct lease_break;
struct tcon_link *tlink;
__u8 lease_key[16];
__le32 lease_state;
};

static void
cifs_ses_oplock_break(struct work_struct *work)
{
struct smb2_lease_break_work *lw = container_of(work,
struct smb2_lease_break_work, lease_break);
int rc;

rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key,
lw->lease_state);
cFYI(1, "Lease release rc %d", rc);
cifs_put_tlink(lw->tlink);
kfree(lw);
}

static bool
smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
{
Expand All @@ -398,35 +419,49 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
struct cifs_tcon *tcon;
struct cifsInodeInfo *cinode;
struct cifsFileInfo *cfile;
struct cifs_pending_open *open;
struct smb2_lease_break_work *lw;
bool found;
int ack_req = rsp->Flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;

lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
if (!lw) {
cERROR(1, "Memory allocation failed during lease break check");
return false;
}

INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
lw->lease_state = rsp->NewLeaseState;

cFYI(1, "Checking for lease break");

/* look up tcon based on tid & uid */
spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &server->smb_ses_list) {
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);

spin_lock(&cifs_file_list_lock);
list_for_each(tmp1, &ses->tcon_list) {
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);

cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
spin_lock(&cifs_file_list_lock);
list_for_each(tmp2, &tcon->openFileList) {
cfile = list_entry(tmp2, struct cifsFileInfo,
tlist);
tlist);
cinode = CIFS_I(cfile->dentry->d_inode);

if (memcmp(cinode->lease_key, rsp->LeaseKey,
SMB2_LEASE_KEY_SIZE))
continue;

cFYI(1, "found in the open list");
cFYI(1, "lease key match, lease break 0x%d",
le32_to_cpu(rsp->NewLeaseState));

smb2_set_oplock_level(cinode,
smb2_map_lease_to_oplock(rsp->NewLeaseState));

if (rsp->Flags &
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED)
if (ack_req)
cfile->oplock_break_cancelled = false;
else
cfile->oplock_break_cancelled = true;
Expand All @@ -437,10 +472,39 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
spin_unlock(&cifs_file_list_lock);

found = false;
list_for_each_entry(open, &tcon->pending_opens, olist) {
if (memcmp(open->lease_key, rsp->LeaseKey,
SMB2_LEASE_KEY_SIZE))
continue;

if (!found && ack_req) {
found = true;
memcpy(lw->lease_key, open->lease_key,
SMB2_LEASE_KEY_SIZE);
lw->tlink = cifs_get_tlink(open->tlink);
queue_work(cifsiod_wq,
&lw->lease_break);
}

cFYI(1, "found in the pending open list");
cFYI(1, "lease key match, lease break 0x%d",
le32_to_cpu(rsp->NewLeaseState));

open->oplock =
smb2_map_lease_to_oplock(rsp->NewLeaseState);
}
if (found) {
spin_unlock(&cifs_file_list_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
}
spin_unlock(&cifs_file_list_lock);
}
spin_unlock(&cifs_tcp_ses_lock);
kfree(lw);
cFYI(1, "Can not process lease break - no lease matched");
return false;
}
Expand Down

0 comments on commit 233839b

Please sign in to comment.