Commit 67ec9949 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

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: default avatarPaulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 102466f3
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -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
+7 −0
Original line number Diff line number Diff line
@@ -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 */
+4 −1
Original line number Diff line number Diff line
@@ -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);
		}
+2 −1
Original line number Diff line number Diff line
@@ -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. */
+98 −24
Original line number Diff line number Diff line
@@ -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;
}

Loading