Commit f80ac7ed authored by David Howells's avatar David Howells Committed by Steve French
Browse files

cifs: Add a tracepoint to log EIO errors



Add a tracepoint to log EIO errors and give it the capacity to convey up to
two integers of information.  This is then wrapped with three functions:

 int smb_EIO(enum smb_eio_trace trace)
 int smb_EIO1(enum smb_eio_trace trace, unsigned long info)
 int smb_EIO2(enum smb_eio_trace trace, unsigned long info,
	      unsigned long info2)

depending on how many bits of info are desired to be logged with any
particular trace.  The functions all return -EIO and can be used in place
of -EIO.

The trace argument is an enum value that gets translated to a string when
the trace is printed.

This makes is easier to log EIO instances when the client is under high
load than turning on a printk wrapper such as cifs_dbg().  Granted, EIO
could have its own separate EIO printing since EIO shouldn't happen.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Reviewed-by: default avatarPaulo Alcantara (Red Hat) <pc@manguebit.org>
cc: linux-cifs@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 3a7b6d0a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -176,7 +176,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
	server = cifs_pick_channel(ses);

	if (!server->ops->new_lease_key)
		return -EIO;
		return smb_EIO(smb_eio_trace_no_lease_key);

	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
	if (!utf16_path)
+6 −4
Original line number Diff line number Diff line
@@ -300,7 +300,7 @@ id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid)
			 __func__, sidtype == SIDOWNER ? 'u' : 'g', cid);
		goto out_revert_creds;
	} else if (sidkey->datalen < CIFS_SID_BASE_SIZE) {
		rc = -EIO;
		rc = smb_EIO1(smb_eio_trace_malformed_sid_key, sidkey->datalen);
		cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n",
			 __func__, sidkey->datalen);
		goto invalidate_key;
@@ -317,7 +317,8 @@ id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid)

	ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32));
	if (ksid_size > sidkey->datalen) {
		rc = -EIO;
		rc = smb_EIO2(smb_eio_trace_malformed_ksid_key,
			      ksid_size, sidkey->datalen);
		cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n",
			 __func__, sidkey->datalen, ksid_size);
		goto invalidate_key;
@@ -352,7 +353,8 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid,
	if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) {
		cifs_dbg(FYI, "%s: %u subauthorities is too many!\n",
			 __func__, psid->num_subauth);
		return -EIO;
		return smb_EIO2(smb_eio_trace_sid_too_many_auth,
				psid->num_subauth, SID_MAX_SUB_AUTHORITIES);
	}

	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) ||
@@ -1227,7 +1229,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
	__u32 dacloffset;

	if (pntsd == NULL)
		return -EIO;
		return smb_EIO(smb_eio_trace_null_pointers);

	owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
				le32_to_cpu(pntsd->osidoffset));
+8 −5
Original line number Diff line number Diff line
@@ -75,11 +75,13 @@ static int cifs_sig_iter(const struct iov_iter *iter, size_t maxsize,
			 struct cifs_calc_sig_ctx *ctx)
{
	struct iov_iter tmp_iter = *iter;
	int err = -EIO;
	size_t did;
	int err;

	if (iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, &err,
				       cifs_sig_step) != maxsize)
		return err;
	did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, &err,
					 cifs_sig_step);
	if (did != maxsize)
		return smb_EIO2(smb_eio_trace_sig_iter, did, maxsize);
	return 0;
}

@@ -96,7 +98,8 @@ int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
	iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, size);

	if (iov_iter_count(&iter) <= 4)
		return -EIO;
		return smb_EIO2(smb_eio_trace_sig_data_too_small,
				iov_iter_count(&iter), 4);

	rc = cifs_sig_iter(&iter, iov_iter_count(&iter), ctx);
	if (rc < 0)
+0 −88
Original line number Diff line number Diff line
@@ -2206,94 +2206,6 @@ static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const
	dst->FileNameLength = src->FileNameLength;
}

static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
				   int num_rqst,
				   const u8 *sig)
{
	unsigned int len, skip;
	unsigned int nents = 0;
	unsigned long addr;
	size_t data_size;
	int i, j;

	/*
	 * The first rqst has a transform header where the first 20 bytes are
	 * not part of the encrypted blob.
	 */
	skip = 20;

	/* Assumes the first rqst has a transform header as the first iov.
	 * I.e.
	 * rqst[0].rq_iov[0]  is transform header
	 * rqst[0].rq_iov[1+] data to be encrypted/decrypted
	 * rqst[1+].rq_iov[0+] data to be encrypted/decrypted
	 */
	for (i = 0; i < num_rqst; i++) {
		data_size = iov_iter_count(&rqst[i].rq_iter);

		/* We really don't want a mixture of pinned and unpinned pages
		 * in the sglist.  It's hard to keep track of which is what.
		 * Instead, we convert to a BVEC-type iterator higher up.
		 */
		if (data_size &&
		    WARN_ON_ONCE(user_backed_iter(&rqst[i].rq_iter)))
			return -EIO;

		/* We also don't want to have any extra refs or pins to clean
		 * up in the sglist.
		 */
		if (data_size &&
		    WARN_ON_ONCE(iov_iter_extract_will_pin(&rqst[i].rq_iter)))
			return -EIO;

		for (j = 0; j < rqst[i].rq_nvec; j++) {
			struct kvec *iov = &rqst[i].rq_iov[j];

			addr = (unsigned long)iov->iov_base + skip;
			if (is_vmalloc_or_module_addr((void *)addr)) {
				len = iov->iov_len - skip;
				nents += DIV_ROUND_UP(offset_in_page(addr) + len,
						      PAGE_SIZE);
			} else {
				nents++;
			}
			skip = 0;
		}
		if (data_size)
			nents += iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
	}
	nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE);
	return nents;
}

/* We can not use the normal sg_set_buf() as we will sometimes pass a
 * stack object as buf.
 */
static inline void cifs_sg_set_buf(struct sg_table *sgtable,
				   const void *buf,
				   unsigned int buflen)
{
	unsigned long addr = (unsigned long)buf;
	unsigned int off = offset_in_page(addr);

	addr &= PAGE_MASK;
	if (is_vmalloc_or_module_addr((void *)addr)) {
		do {
			unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off);

			sg_set_page(&sgtable->sgl[sgtable->nents++],
				    vmalloc_to_page((void *)addr), len, off);

			off = 0;
			addr += PAGE_SIZE;
			buflen -= len;
		} while (buflen);
	} else {
		sg_set_page(&sgtable->sgl[sgtable->nents++],
			    virt_to_page((void *)addr), buflen, off);
	}
}

#define CIFS_OPARMS(_cifs_sb, _tcon, _path, _da, _cd, _co, _mode) \
	((struct cifs_open_parms) { \
		.tcon = _tcon, \
+106 −0
Original line number Diff line number Diff line
@@ -786,4 +786,110 @@ static inline void cifs_free_open_info(struct cifs_open_info_data *data)
	memset(data, 0, sizeof(*data));
}

static inline int smb_EIO(enum smb_eio_trace trace)
{
	trace_smb3_eio(trace, 0, 0);
	return -EIO;
}

static inline int smb_EIO1(enum smb_eio_trace trace, unsigned long info)
{
	trace_smb3_eio(trace, info, 0);
	return -EIO;
}

static inline int smb_EIO2(enum smb_eio_trace trace, unsigned long info, unsigned long info2)
{
	trace_smb3_eio(trace, info, info2);
	return -EIO;
}

static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
				   int num_rqst,
				   const u8 *sig)
{
	unsigned int len, skip;
	unsigned int nents = 0;
	unsigned long addr;
	size_t data_size;
	int i, j;

	/*
	 * The first rqst has a transform header where the first 20 bytes are
	 * not part of the encrypted blob.
	 */
	skip = 20;

	/* Assumes the first rqst has a transform header as the first iov.
	 * I.e.
	 * rqst[0].rq_iov[0]  is transform header
	 * rqst[0].rq_iov[1+] data to be encrypted/decrypted
	 * rqst[1+].rq_iov[0+] data to be encrypted/decrypted
	 */
	for (i = 0; i < num_rqst; i++) {
		data_size = iov_iter_count(&rqst[i].rq_iter);

		/* We really don't want a mixture of pinned and unpinned pages
		 * in the sglist.  It's hard to keep track of which is what.
		 * Instead, we convert to a BVEC-type iterator higher up.
		 */
		if (data_size &&
		    WARN_ON_ONCE(user_backed_iter(&rqst[i].rq_iter)))
			return smb_EIO(smb_eio_trace_user_iter);

		/* We also don't want to have any extra refs or pins to clean
		 * up in the sglist.
		 */
		if (data_size &&
		    WARN_ON_ONCE(iov_iter_extract_will_pin(&rqst[i].rq_iter)))
			return smb_EIO(smb_eio_trace_extract_will_pin);

		for (j = 0; j < rqst[i].rq_nvec; j++) {
			struct kvec *iov = &rqst[i].rq_iov[j];

			addr = (unsigned long)iov->iov_base + skip;
			if (is_vmalloc_or_module_addr((void *)addr)) {
				len = iov->iov_len - skip;
				nents += DIV_ROUND_UP(offset_in_page(addr) + len,
						      PAGE_SIZE);
			} else {
				nents++;
			}
			skip = 0;
		}
		if (data_size)
			nents += iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
	}
	nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE);
	return nents;
}

/* We can not use the normal sg_set_buf() as we will sometimes pass a
 * stack object as buf.
 */
static inline void cifs_sg_set_buf(struct sg_table *sgtable,
				   const void *buf,
				   unsigned int buflen)
{
	unsigned long addr = (unsigned long)buf;
	unsigned int off = offset_in_page(addr);

	addr &= PAGE_MASK;
	if (is_vmalloc_or_module_addr((void *)addr)) {
		do {
			unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off);

			sg_set_page(&sgtable->sgl[sgtable->nents++],
				    vmalloc_to_page((void *)addr), len, off);

			off = 0;
			addr += PAGE_SIZE;
			buflen -= len;
		} while (buflen);
	} else {
		sg_set_page(&sgtable->sgl[sgtable->nents++],
			    virt_to_page((void *)addr), buflen, off);
	}
}

#endif			/* _CIFSPROTO_H */
Loading