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>
This commit is contained in:
Paulo Alcantara 2023-11-25 23:55:05 -03:00 committed by Steve French
parent 102466f303
commit 67ec9949b0
6 changed files with 119 additions and 31 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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);
}

View File

@ -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. */

View File

@ -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 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
}
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 @@ int smb311_posix_query_path_info(const unsigned int xid,
}
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;
}

View File

@ -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);