summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2025-07-03 12:29:14 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2025-07-03 14:27:53 -0300
commit556055e008ee0054151085fbf3f9fbf182cf0b98 (patch)
tree0e310098ad417c66d21d01257138b1a0ef52e4ad
parent33ef9fe668688c3c390c0048dce382b846bc22c5 (diff)
downloadlinux-556055e008ee0054151085fbf3f9fbf182cf0b98.tar.gz
linux-556055e008ee0054151085fbf3f9fbf182cf0b98.tar.bz2
linux-556055e008ee0054151085fbf3f9fbf182cf0b98.zip
smb: client: don't break parent lease on Create
On SMB2_open_init() we have the chance to lookup for a cached parent directory of the target file/dir, which means we can use the it's lease key to avoid unnecessary (*) lease breaks. (*) unnecessary because setting ParentLeaseKey is still a hint -- the server will still break the parent's lease whenever it sees fit. Minor cleanup: remove @req arg from add_lease_context(), set RequestedOplocklevel outside. Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
-rw-r--r--fs/smb/client/cached_dir.c21
-rw-r--r--fs/smb/client/dir.c22
-rw-r--r--fs/smb/client/smb2pdu.c60
3 files changed, 46 insertions, 57 deletions
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index 56bafd96cc5e..696a1d224dee 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -161,7 +161,6 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, const char *path,
struct cached_fids *cfids;
const char *npath;
int retries = 0, cur_sleep = 1;
- __le32 lease_flags = 0;
if (cifs_sb->root == NULL)
return -ENOENT;
@@ -233,25 +232,6 @@ replay_again:
rc = -ENOENT;
goto out;
}
- if (dentry->d_parent && server->dialect >= SMB30_PROT_ID) {
- struct cached_fid *parent_cfid;
-
- spin_lock(&cfids->cfid_list_lock);
- list_for_each_entry(parent_cfid, &cfids->entries, entry) {
- if (parent_cfid->dentry == dentry->d_parent) {
- cifs_dbg(FYI, "found a parent cached file handle\n");
- if (parent_cfid->has_lease && parent_cfid->time) {
- lease_flags
- |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
- memcpy(pfid->parent_lease_key,
- parent_cfid->fid.lease_key,
- SMB2_LEASE_KEY_SIZE);
- }
- break;
- }
- }
- spin_unlock(&cfids->cfid_list_lock);
- }
}
cfid->dentry = dentry;
cfid->tcon = tcon;
@@ -285,7 +265,6 @@ replay_again:
FILE_READ_EA,
.disposition = FILE_OPEN,
.fid = pfid,
- .lease_flags = lease_flags,
.replay = !!(retries),
};
diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
index 1c6e5389c51f..23412156d12a 100644
--- a/fs/smb/client/dir.c
+++ b/fs/smb/client/dir.c
@@ -191,7 +191,6 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
int rdwr_for_fscache = 0;
- __le32 lease_flags = 0;
*oplock = 0;
if (tcon->ses->server->oplocks)
@@ -314,26 +313,6 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
create_options |= CREATE_OPTION_READONLY;
retry_open:
- if (tcon->cfids && direntry->d_parent && server->dialect >= SMB30_PROT_ID) {
- struct cached_fid *parent_cfid;
-
- spin_lock(&tcon->cfids->cfid_list_lock);
- list_for_each_entry(parent_cfid, &tcon->cfids->entries, entry) {
- if (parent_cfid->dentry == direntry->d_parent) {
- cifs_dbg(FYI, "found a parent cached file handle\n");
- if (parent_cfid->has_lease && parent_cfid->time) {
- lease_flags
- |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
- memcpy(fid->parent_lease_key,
- parent_cfid->fid.lease_key,
- SMB2_LEASE_KEY_SIZE);
- }
- break;
- }
- }
- spin_unlock(&tcon->cfids->cfid_list_lock);
- }
-
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb,
@@ -342,7 +321,6 @@ retry_open:
.disposition = disposition,
.path = full_path,
.fid = fid,
- .lease_flags = lease_flags,
.mode = mode,
};
rc = server->ops->open(xid, &oparms, oplock, buf);
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 13e1362d6575..d478fc58ad70 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -2404,22 +2404,20 @@ int smb2_parse_contexts(struct TCP_Server_Info *server,
static int
add_lease_context(struct TCP_Server_Info *server,
- struct smb2_create_req *req,
struct kvec *iov,
unsigned int *num_iovec,
u8 *lease_key,
- __u8 *oplock,
+ __u8 oplock,
u8 *parent_lease_key,
__le32 flags)
{
unsigned int num = *num_iovec;
- iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock,
+ iov[num].iov_base = server->ops->create_lease_buf(lease_key, oplock,
parent_lease_key, flags);
if (iov[num].iov_base == NULL)
return -ENOMEM;
iov[num].iov_len = server->vals->create_lease_size;
- req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
*num_iovec = num + 1;
return 0;
}
@@ -3077,24 +3075,58 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
iov[1].iov_len = uni_path_len;
iov[1].iov_base = path;
- if ((!server->oplocks) || (tcon->no_lease))
+ if ((!server->oplocks) || (tcon->no_lease) || !(server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
+ (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) &&
+ (oparms->create_options & CREATE_NOT_FILE)))
*oplock = SMB2_OPLOCK_LEVEL_NONE;
- if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
- *oplock == SMB2_OPLOCK_LEVEL_NONE)
- req->RequestedOplockLevel = *oplock;
- else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) &&
- (oparms->create_options & CREATE_NOT_FILE))
- req->RequestedOplockLevel = *oplock; /* no srv lease support */
- else {
- rc = add_lease_context(server, req, iov, &n_iov,
- oparms->fid->lease_key, oplock,
+ if (*oplock != SMB2_OPLOCK_LEVEL_NONE) {
+ /*
+ * If server has dir leases support, we can always set ParentLeaseKey -- it's only a
+ * hint to the server that we're not modifying parent itself, only the child.
+ *
+ * This avoids a leasebreak of the parent dir in most cases, but the server will
+ * still emit one when required (e.g. concurrent opens with different options/modes).
+ */
+ memset(oparms->fid->parent_lease_key, 0, SMB2_LEASE_KEY_SIZE);
+
+ if (*oparms->path) {
+ char *tmp_path;
+ int sep = oparms->cifs_sb ? CIFS_DIR_SEP(oparms->cifs_sb) : '\\';
+
+ tmp_path = strrchr(oparms->path, sep);
+ if (tmp_path) {
+ struct cached_fid *pcfid;
+ size_t i, len = strlen(tmp_path);
+
+ tmp_path = kstrdup(oparms->path, GFP_ATOMIC);
+ if (!tmp_path)
+ return -ENOMEM;
+
+ i = strlen(oparms->path) - len;
+ memset(&tmp_path[i], 0, len);
+
+ pcfid = lookup_cached_dir(tcon->cfids, tmp_path, CFID_LOOKUP_PATH);
+ kfree(tmp_path);
+ if (pcfid) {
+ memcpy(oparms->fid->parent_lease_key, pcfid->fid.lease_key,
+ SMB2_LEASE_KEY_SIZE);
+ oparms->lease_flags |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
+ close_cached_dir(pcfid);
+ }
+ }
+ }
+
+ rc = add_lease_context(server, iov, &n_iov,
+ oparms->fid->lease_key, *oplock,
oparms->fid->parent_lease_key,
oparms->lease_flags);
if (rc)
return rc;
}
+ req->RequestedOplockLevel = *oplock;
+
if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
rc = add_durable_context(iov, &n_iov, oparms,
tcon->use_persistent);