Skip to content
4 changes: 2 additions & 2 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,8 +422,8 @@ struct smb_version_operations {
/* check for STATUS_NETWORK_SESSION_EXPIRED */
bool (*is_session_expired)(char *);
/* send oplock break response */
int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *,
struct cifsInodeInfo *);
int (*oplock_response)(struct cifs_tcon *tcon, __u64 persistent_fid, __u64 volatile_fid,
__u16 net_fid, struct cifsInodeInfo *cifs_inode);
/* query remote filesystem */
int (*queryfs)(const unsigned int, struct cifs_tcon *,
struct cifs_sb_info *, struct kstatfs *);
Expand Down
70 changes: 44 additions & 26 deletions fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -5048,17 +5048,32 @@ void cifs_oplock_break(struct work_struct *work)
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
oplock_break);
struct inode *inode = d_inode(cfile->dentry);
struct super_block *sb = inode->i_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
struct tcon_link *tlink;
int rc = 0;
bool purge_cache = false;
bool is_deferred = false;
struct cifs_deferred_close *dclose;
bool purge_cache = false, oplock_break_cancelled;
__u64 persistent_fid, volatile_fid;
__u16 net_fid;

/*
* Hold a reference to the superblock to prevent it and its inodes from
* being freed while we are accessing cinode. Otherwise, _cifsFileInfo_put()
* may release the last reference to the sb and trigger inode eviction.
*/
cifs_sb_active(sb);
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
TASK_UNINTERRUPTIBLE);

tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
goto out;
tcon = tlink_tcon(tlink);
server = tcon->ses->server;

server->ops->downgrade_oplock(server, cinode, cfile->oplock_level,
cfile->oplock_epoch, &purge_cache);

Expand Down Expand Up @@ -5095,31 +5110,34 @@ void cifs_oplock_break(struct work_struct *work)
* file handles but cached, then schedule deferred close immediately.
* So, new open will not use cached handle.
*/
spin_lock(&CIFS_I(inode)->deferred_lock);
is_deferred = cifs_is_deferred_close(cfile, &dclose);
spin_unlock(&CIFS_I(inode)->deferred_lock);
if (is_deferred &&
cfile->deferred_close_scheduled &&
delayed_work_pending(&cfile->deferred)) {
if (cancel_delayed_work(&cfile->deferred)) {
_cifsFileInfo_put(cfile, false, false);
goto oplock_break_done;
}
}

if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes))
cifs_close_deferred_file(cinode);

persistent_fid = cfile->fid.persistent_fid;
volatile_fid = cfile->fid.volatile_fid;
net_fid = cfile->fid.netfid;
oplock_break_cancelled = cfile->oplock_break_cancelled;

_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
/*
* releasing stale oplock after recent reconnect of smb session using
* a now incorrect file handle is not a data integrity issue but do
* not bother sending an oplock release if session to server still is
* disconnected since oplock already released by the server
* MS-SMB2 3.2.5.19.1 and 3.2.5.19.2 (and MS-CIFS 3.2.5.42) do not require
* an acknowledgment to be sent when the file has already been closed.
*/
if (!cfile->oplock_break_cancelled) {
rc = tcon->ses->server->ops->oplock_response(tcon, &cfile->fid,
cinode);
spin_lock(&cinode->open_file_lock);
/* check list empty since can race with kill_sb calling tree disconnect */
if (!oplock_break_cancelled && !list_empty(&cinode->openFileList)) {
spin_unlock(&cinode->open_file_lock);
rc = server->ops->oplock_response(tcon, persistent_fid,
volatile_fid, net_fid, cinode);
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
}
oplock_break_done:
_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
} else
spin_unlock(&cinode->open_file_lock);

cifs_put_tlink(tlink);
out:
cifs_done_oplock_break(cinode);
cifs_sb_deactive(sb);
}

/*
Expand Down
2 changes: 1 addition & 1 deletion fs/cifs/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
spin_unlock(&cifs_inode->open_file_lock);

list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
_cifsFileInfo_put(tmp_list->cfile, true, false);
_cifsFileInfo_put(tmp_list->cfile, false, false);
list_del(&tmp_list->list);
kfree(tmp_list);
}
Expand Down
9 changes: 4 additions & 5 deletions fs/cifs/smb1ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -872,12 +872,11 @@ cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
}

static int
cifs_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
struct cifsInodeInfo *cinode)
cifs_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid,
__u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode)
{
return CIFSSMBLock(0, tcon, fid->netfid, current->tgid, 0, 0, 0, 0,
LOCKING_ANDX_OPLOCK_RELEASE, false,
CIFS_CACHE_READ(cinode) ? 1 : 0);
return CIFSSMBLock(0, tcon, net_fid, current->tgid, 0, 0, 0, 0,
LOCKING_ANDX_OPLOCK_RELEASE, false, CIFS_CACHE_READ(cinode) ? 1 : 0);
}

static int
Expand Down
7 changes: 3 additions & 4 deletions fs/cifs/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -2309,15 +2309,14 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
}

static int
smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
struct cifsInodeInfo *cinode)
smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid,
__u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode)
{
if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
return SMB2_lease_break(0, tcon, cinode->lease_key,
smb2_get_lease_state(cinode));

return SMB2_oplock_break(0, tcon, fid->persistent_fid,
fid->volatile_fid,
return SMB2_oplock_break(0, tcon, persistent_fid, volatile_fid,
CIFS_CACHE_READ(cinode) ? 1 : 0);
}

Expand Down
Loading