summaryrefslogtreecommitdiff
path: root/fs/smb
diff options
context:
space:
mode:
authorPaulo Alcantara <pc@manguebit.com>2023-11-26 05:55:05 +0300
committerSteve French <stfrench@microsoft.com>2024-01-08 00:46:05 +0300
commit67ec9949b0dfe78c99e110dd975eb7dc5645630c (patch)
tree5953ae20533a50ad3990bdcea0a7f6fd79b7208b /fs/smb
parent102466f303ffcd5cff207b3c122557f73f1041e6 (diff)
downloadlinux-67ec9949b0dfe78c99e110dd975eb7dc5645630c.tar.xz
smb: client: optimise reparse point querying
Reduce number of roundtrips to server when querying reparse points in ->query_path_info() by sending a single compound request of create+get_reparse+get_info+close. Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/smb')
-rw-r--r--fs/smb/client/cifsglob.h10
-rw-r--r--fs/smb/client/cifsproto.h7
-rw-r--r--fs/smb/client/inode.c5
-rw-r--r--fs/smb/client/smb2glob.h3
-rw-r--r--fs/smb/client/smb2inode.c122
-rw-r--r--fs/smb/client/trace.h3
6 files changed, 119 insertions, 31 deletions
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index fd9aba5f4fda..541e8b88b4c1 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -192,6 +192,11 @@ struct cifs_open_info_data {
bool symlink;
};
struct {
+ /* ioctl response buffer */
+ struct {
+ int buftype;
+ struct kvec iov;
+ } io;
__u32 tag;
union {
struct reparse_data_buffer *buf;
@@ -209,11 +214,6 @@ struct cifs_open_info_data {
((d)->reparse_point || \
(le32_to_cpu((d)->fi.Attributes) & ATTR_REPARSE))
-static inline void cifs_free_open_info(struct cifs_open_info_data *data)
-{
- kfree(data->symlink_target);
-}
-
/*
*****************************************************************
* Except the CIFS PDUs themselves all the
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 0adeaa84b662..4c5d533d98a3 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -764,4 +764,11 @@ static inline void release_mid(struct mid_q_entry *mid)
kref_put(&mid->refcount, __release_mid);
}
+static inline void cifs_free_open_info(struct cifs_open_info_data *data)
+{
+ kfree(data->symlink_target);
+ free_rsp_buf(data->reparse.io.buftype, data->reparse.io.iov.iov_base);
+ memset(data, 0, sizeof(*data));
+}
+
#endif /* _CIFSPROTO_H */
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index b73bf8a93443..8d6282bbc7ed 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -1078,6 +1078,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
&rsp_iov, &rsp_buftype);
if (!rc)
iov = &rsp_iov;
+ } else if (data->reparse.io.buftype != CIFS_NO_BUFFER &&
+ data->reparse.io.iov.iov_base) {
+ iov = &data->reparse.io.iov;
}
rc = -EOPNOTSUPP;
@@ -1097,7 +1100,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
/* Check for cached reparse point data */
if (data->symlink_target || data->reparse.buf) {
rc = 0;
- } else if (server->ops->parse_reparse_point) {
+ } else if (iov && server->ops->parse_reparse_point) {
rc = server->ops->parse_reparse_point(cifs_sb,
iov, data);
}
diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h
index ca87a0011c33..a0c156996fc5 100644
--- a/fs/smb/client/smb2glob.h
+++ b/fs/smb/client/smb2glob.h
@@ -35,7 +35,8 @@ enum smb2_compound_ops {
SMB2_OP_SET_EOF,
SMB2_OP_RMDIR,
SMB2_OP_POSIX_QUERY_INFO,
- SMB2_OP_SET_REPARSE
+ SMB2_OP_SET_REPARSE,
+ SMB2_OP_GET_REPARSE
};
/* Used when constructing chained read requests. */
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index d662bad3b703..0f096fb41b3e 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -26,6 +26,24 @@
#include "cached_dir.h"
#include "smb2status.h"
+static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
+{
+ struct reparse_data_buffer *buf;
+ struct smb2_ioctl_rsp *io = iov->iov_base;
+ u32 off, count, len;
+
+ count = le32_to_cpu(io->OutputCount);
+ off = le32_to_cpu(io->OutputOffset);
+ if (check_add_overflow(off, count, &len) || len > iov->iov_len)
+ return ERR_PTR(-EIO);
+
+ buf = (struct reparse_data_buffer *)((u8 *)io + off);
+ len = sizeof(*buf);
+ if (count < len || count < le16_to_cpu(buf->ReparseDataLength) + len)
+ return ERR_PTR(-EIO);
+ return buf;
+}
+
/*
* note: If cfile is passed, the reference to it is dropped here.
* So make sure that you do not reuse cfile after return from this func.
@@ -42,8 +60,10 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
__u8 **extbuf, size_t *extbuflen,
struct kvec *out_iov, int *out_buftype)
{
+
+ struct reparse_data_buffer *rbuf;
struct smb2_compound_vars *vars = NULL;
- struct kvec *rsp_iov;
+ struct kvec *rsp_iov, *iov;
struct smb_rqst *rqst;
int rc;
__le16 *utf16_path = NULL;
@@ -363,6 +383,21 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
tcon->tid, full_path);
break;
+ case SMB2_OP_GET_REPARSE:
+ rqst[num_rqst].rq_iov = vars->io_iov;
+ rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
+
+ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
+ COMPOUND_FID, COMPOUND_FID,
+ FSCTL_GET_REPARSE_POINT,
+ NULL, 0, CIFSMaxBufSize);
+ if (rc)
+ goto finished;
+ smb2_set_next_command(tcon, &rqst[num_rqst]);
+ smb2_set_related(&rqst[num_rqst++]);
+ trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
+ tcon->tid, full_path);
+ break;
default:
cifs_dbg(VFS, "Invalid command\n");
rc = -EINVAL;
@@ -529,6 +564,30 @@ finished:
}
SMB2_ioctl_free(&rqst[num_rqst++]);
break;
+ case SMB2_OP_GET_REPARSE:
+ if (!rc) {
+ iov = &rsp_iov[i + 1];
+ idata = in_iov[i].iov_base;
+ idata->reparse.io.iov = *iov;
+ idata->reparse.io.buftype = resp_buftype[i + 1];
+ rbuf = reparse_buf_ptr(iov);
+ if (IS_ERR(rbuf)) {
+ rc = PTR_ERR(rbuf);
+ trace_smb3_set_reparse_compound_err(xid, ses->Suid,
+ tcon->tid, rc);
+ } else {
+ idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag);
+ trace_smb3_set_reparse_compound_done(xid, ses->Suid,
+ tcon->tid);
+ }
+ memset(iov, 0, sizeof(*iov));
+ resp_buftype[i + 1] = CIFS_NO_BUFFER;
+ } else {
+ trace_smb3_set_reparse_compound_err(xid, ses->Suid,
+ tcon->tid, rc);
+ }
+ SMB2_ioctl_free(&rqst[num_rqst++]);
+ break;
}
}
SMB2_close_free(&rqst[num_rqst]);
@@ -589,10 +648,11 @@ int smb2_query_path_info(const unsigned int xid,
struct cifsFileInfo *cfile;
struct cached_fid *cfid = NULL;
struct smb2_hdr *hdr;
- struct kvec in_iov, out_iov[3] = {};
+ struct kvec in_iov[2], out_iov[3] = {};
int out_buftype[3] = {};
+ int cmds[2] = { SMB2_OP_QUERY_INFO, };
bool islink;
- int cmd = SMB2_OP_QUERY_INFO;
+ int i, num_cmds;
int rc, rc2;
data->adjust_tz = false;
@@ -614,14 +674,16 @@ int smb2_query_path_info(const unsigned int xid,
return rc;
}
- in_iov.iov_base = data;
- in_iov.iov_len = sizeof(*data);
+ in_iov[0].iov_base = data;
+ in_iov[0].iov_len = sizeof(*data);
+ in_iov[1] = in_iov[0];
cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN,
- create_options, ACL_NO_MODE, &in_iov,
- &cmd, 1, cfile, NULL, NULL, out_iov, out_buftype);
+ create_options, ACL_NO_MODE,
+ in_iov, cmds, 1, cfile,
+ NULL, NULL, out_iov, out_buftype);
hdr = out_iov[0].iov_base;
/*
* If first iov is unset, then SMB session was dropped or we've got a
@@ -637,13 +699,19 @@ int smb2_query_path_info(const unsigned int xid,
if (rc || !data->reparse_point)
goto out;
+ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
+ /* symlink already parsed in create response */
+ num_cmds = 1;
+ } else {
+ cmds[1] = SMB2_OP_GET_REPARSE;
+ num_cmds = 2;
+ }
create_options |= OPEN_REPARSE_POINT;
- /* Failed on a symbolic link - query a reparse point info */
cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN,
- create_options, ACL_NO_MODE, &in_iov,
- &cmd, 1, cfile, NULL, NULL, NULL, NULL);
+ create_options, ACL_NO_MODE, in_iov, cmds,
+ num_cmds, cfile, NULL, NULL, NULL, NULL);
break;
case -EREMOTE:
break;
@@ -661,9 +729,8 @@ int smb2_query_path_info(const unsigned int xid,
}
out:
- free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
- free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
- free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
+ for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
+ free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
return rc;
}
@@ -678,13 +745,14 @@ int smb311_posix_query_path_info(const unsigned int xid,
int rc;
__u32 create_options = 0;
struct cifsFileInfo *cfile;
- struct kvec in_iov, out_iov[3] = {};
+ struct kvec in_iov[2], out_iov[3] = {};
int out_buftype[3] = {};
__u8 *sidsbuf = NULL;
__u8 *sidsbuf_end = NULL;
size_t sidsbuflen = 0;
size_t owner_len, group_len;
- int cmd = SMB2_OP_POSIX_QUERY_INFO;
+ int cmds[2] = { SMB2_OP_POSIX_QUERY_INFO, };
+ int i, num_cmds;
data->adjust_tz = false;
data->reparse_point = false;
@@ -695,13 +763,14 @@ int smb311_posix_query_path_info(const unsigned int xid,
* when we already have an open file handle for this. For now this is fast enough
* (always using the compounded version).
*/
- in_iov.iov_base = data;
- in_iov.iov_len = sizeof(*data);
+ in_iov[0].iov_base = data;
+ in_iov[0].iov_len = sizeof(*data);
+ in_iov[1] = in_iov[0];
cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN,
- create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
+ create_options, ACL_NO_MODE, in_iov, cmds, 1,
cfile, &sidsbuf, &sidsbuflen, out_iov, out_buftype);
/*
* If first iov is unset, then SMB session was dropped or we've got a
@@ -718,13 +787,19 @@ int smb311_posix_query_path_info(const unsigned int xid,
if (rc || !data->reparse_point)
goto out;
+ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
+ /* symlink already parsed in create response */
+ num_cmds = 1;
+ } else {
+ cmds[1] = SMB2_OP_GET_REPARSE;
+ num_cmds = 2;
+ }
create_options |= OPEN_REPARSE_POINT;
- /* Failed on a symbolic link - query a reparse point info */
cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN,
- create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
- cfile, &sidsbuf, &sidsbuflen, NULL, NULL);
+ create_options, ACL_NO_MODE, in_iov, cmds,
+ num_cmds, cfile, &sidsbuf, &sidsbuflen, NULL, NULL);
break;
}
@@ -749,9 +824,8 @@ out:
}
kfree(sidsbuf);
- free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
- free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
- free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
+ for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
+ free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
return rc;
}
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index 34f507584274..522fa387fcfd 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -371,6 +371,7 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_reparse_compound_enter);
+DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(get_reparse_compound_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter);
@@ -409,6 +410,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done);
+DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done);
@@ -453,6 +455,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err);
+DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err);