diff options
| author | Enzo Matsumiya <ematsumiya@suse.de> | 2025-07-03 12:29:14 -0300 |
|---|---|---|
| committer | Enzo Matsumiya <ematsumiya@suse.de> | 2025-07-03 14:27:53 -0300 |
| commit | 556055e008ee0054151085fbf3f9fbf182cf0b98 (patch) | |
| tree | 0e310098ad417c66d21d01257138b1a0ef52e4ad | |
| parent | 33ef9fe668688c3c390c0048dce382b846bc22c5 (diff) | |
| download | linux-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.c | 21 | ||||
| -rw-r--r-- | fs/smb/client/dir.c | 22 | ||||
| -rw-r--r-- | fs/smb/client/smb2pdu.c | 60 |
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); |
