Commit aaf724ed authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'v6.16-rc3-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - Multichannel reconnect lock ordering deadlock fix

 - Fix for regression in handling native Windows symlinks

 - Three smbdirect fixes:
     - oops in RDMA response processing
     - smbdirect memcpy issue
     - fix smbdirect regression with large writes (smbdirect test cases
       now all passing)

 - Fix for "FAILED_TO_PARSE" warning in trace-cmd report output

* tag 'v6.16-rc3-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: Fix reading into an ITER_FOLIOQ from the smbdirect code
  cifs: Fix the smbd_response slab to allow usercopy
  smb: client: fix potential deadlock when reconnecting channels
  smb: client: remove \t from TP_printk statements
  smb: client: let smbd_post_send_iter() respect the peers max_send_size and transmit all data
  smb: client: fix regression with native SMB symlinks
parents 0fd39af2 263debec
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -709,6 +709,7 @@ inc_rfc1001_len(void *buf, int count)
struct TCP_Server_Info {
	struct list_head tcp_ses_list;
	struct list_head smb_ses_list;
	struct list_head rlist; /* reconnect list */
	spinlock_t srv_lock;  /* protect anything here that is not protected */
	__u64 conn_id; /* connection identifier (useful for debugging) */
	int srv_count; /* reference counter */
+36 −22
Original line number Diff line number Diff line
@@ -124,6 +124,14 @@ static void smb2_query_server_interfaces(struct work_struct *work)
			   (SMB_INTERFACE_POLL_INTERVAL * HZ));
}

#define set_need_reco(server) \
do { \
	spin_lock(&server->srv_lock); \
	if (server->tcpStatus != CifsExiting) \
		server->tcpStatus = CifsNeedReconnect; \
	spin_unlock(&server->srv_lock); \
} while (0)

/*
 * Update the tcpStatus for the server.
 * This is used to signal the cifsd thread to call cifs_reconnect
@@ -137,39 +145,45 @@ void
cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
				bool all_channels)
{
	struct TCP_Server_Info *pserver;
	struct TCP_Server_Info *nserver;
	struct cifs_ses *ses;
	LIST_HEAD(reco);
	int i;

	/* If server is a channel, select the primary channel */
	pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;

	/* if we need to signal just this channel */
	if (!all_channels) {
		spin_lock(&server->srv_lock);
		if (server->tcpStatus != CifsExiting)
			server->tcpStatus = CifsNeedReconnect;
		spin_unlock(&server->srv_lock);
		set_need_reco(server);
		return;
	}

	spin_lock(&cifs_tcp_ses_lock);
	list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
		if (cifs_ses_exiting(ses))
	if (SERVER_IS_CHAN(server))
		server = server->primary_server;
	scoped_guard(spinlock, &cifs_tcp_ses_lock) {
		set_need_reco(server);
		list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
			spin_lock(&ses->ses_lock);
			if (ses->ses_status == SES_EXITING) {
				spin_unlock(&ses->ses_lock);
				continue;
			}
			spin_lock(&ses->chan_lock);
		for (i = 0; i < ses->chan_count; i++) {
			if (!ses->chans[i].server)
			for (i = 1; i < ses->chan_count; i++) {
				nserver = ses->chans[i].server;
				if (!nserver)
					continue;

			spin_lock(&ses->chans[i].server->srv_lock);
			if (ses->chans[i].server->tcpStatus != CifsExiting)
				ses->chans[i].server->tcpStatus = CifsNeedReconnect;
			spin_unlock(&ses->chans[i].server->srv_lock);
				nserver->srv_count++;
				list_add(&nserver->rlist, &reco);
			}
			spin_unlock(&ses->chan_lock);
			spin_unlock(&ses->ses_lock);
		}
	}

	list_for_each_entry_safe(server, nserver, &reco, rlist) {
		list_del_init(&server->rlist);
		set_need_reco(server);
		cifs_put_tcp_session(server, 0);
	}
	spin_unlock(&cifs_tcp_ses_lock);
}

/*
+4 −16
Original line number Diff line number Diff line
@@ -875,15 +875,8 @@ int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
			abs_path += sizeof("\\DosDevices\\")-1;
		else if (strstarts(abs_path, "\\GLOBAL??\\"))
			abs_path += sizeof("\\GLOBAL??\\")-1;
		else {
			/* Unhandled absolute symlink, points outside of DOS/Win32 */
			cifs_dbg(VFS,
				 "absolute symlink '%s' cannot be converted from NT format "
				 "because points to unknown target\n",
				 smb_target);
			rc = -EIO;
			goto out;
		}
		else
			goto out_unhandled_target;

		/* Sometimes path separator after \?? is double backslash */
		if (abs_path[0] == '\\')
@@ -910,13 +903,7 @@ int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
			abs_path++;
			abs_path[0] = drive_letter;
		} else {
			/* Unhandled absolute symlink. Report an error. */
			cifs_dbg(VFS,
				 "absolute symlink '%s' cannot be converted from NT format "
				 "because points to unknown target\n",
				 smb_target);
			rc = -EIO;
			goto out;
			goto out_unhandled_target;
		}

		abs_path_len = strlen(abs_path)+1;
@@ -966,6 +953,7 @@ int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
		 * These paths have same format as Linux symlinks, so no
		 * conversion is needed.
		 */
out_unhandled_target:
		linux_target = smb_target;
		smb_target = NULL;
	}
+57 −104
Original line number Diff line number Diff line
@@ -907,8 +907,10 @@ static int smbd_post_send_iter(struct smbd_connection *info,
			.local_dma_lkey	= sc->ib.pd->local_dma_lkey,
			.direction	= DMA_TO_DEVICE,
		};
		size_t payload_len = umin(*_remaining_data_length,
					  sp->max_send_size - sizeof(*packet));

		rc = smb_extract_iter_to_rdma(iter, *_remaining_data_length,
		rc = smb_extract_iter_to_rdma(iter, payload_len,
					      &extract);
		if (rc < 0)
			goto err_dma;
@@ -1013,6 +1015,27 @@ static int smbd_post_send_empty(struct smbd_connection *info)
	return smbd_post_send_iter(info, NULL, &remaining_data_length);
}

static int smbd_post_send_full_iter(struct smbd_connection *info,
				    struct iov_iter *iter,
				    int *_remaining_data_length)
{
	int rc = 0;

	/*
	 * smbd_post_send_iter() respects the
	 * negotiated max_send_size, so we need to
	 * loop until the full iter is posted
	 */

	while (iov_iter_count(iter) > 0) {
		rc = smbd_post_send_iter(info, iter, _remaining_data_length);
		if (rc < 0)
			break;
	}

	return rc;
}

/*
 * Post a receive request to the transport
 * The remote peer can only send data when a receive request is posted
@@ -1452,6 +1475,9 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
	char name[MAX_NAME_LEN];
	int rc;

	if (WARN_ON_ONCE(sp->max_recv_size < sizeof(struct smbdirect_data_transfer)))
		return -ENOMEM;

	scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info);
	info->request_cache =
		kmem_cache_create(
@@ -1469,12 +1495,17 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
		goto out1;

	scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info);

	struct kmem_cache_args response_args = {
		.align		= __alignof__(struct smbd_response),
		.useroffset	= (offsetof(struct smbd_response, packet) +
				   sizeof(struct smbdirect_data_transfer)),
		.usersize	= sp->max_recv_size - sizeof(struct smbdirect_data_transfer),
	};
	info->response_cache =
		kmem_cache_create(
			name,
			sizeof(struct smbd_response) +
				sp->max_recv_size,
			0, SLAB_HWCACHE_ALIGN, NULL);
		kmem_cache_create(name,
				  sizeof(struct smbd_response) + sp->max_recv_size,
				  &response_args, SLAB_HWCACHE_ALIGN);
	if (!info->response_cache)
		goto out2;

@@ -1747,35 +1778,39 @@ struct smbd_connection *smbd_get_connection(
}

/*
 * Receive data from receive reassembly queue
 * Receive data from the transport's receive reassembly queue
 * All the incoming data packets are placed in reassembly queue
 * buf: the buffer to read data into
 * iter: the buffer to read data into
 * size: the length of data to read
 * return value: actual data read
 * Note: this implementation copies the data from reassebmly queue to receive
 *
 * Note: this implementation copies the data from reassembly queue to receive
 * buffers used by upper layer. This is not the optimal code path. A better way
 * to do it is to not have upper layer allocate its receive buffers but rather
 * borrow the buffer from reassembly queue, and return it after data is
 * consumed. But this will require more changes to upper layer code, and also
 * need to consider packet boundaries while they still being reassembled.
 */
static int smbd_recv_buf(struct smbd_connection *info, char *buf,
		unsigned int size)
int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
{
	struct smbdirect_socket *sc = &info->socket;
	struct smbd_response *response;
	struct smbdirect_data_transfer *data_transfer;
	size_t size = iov_iter_count(&msg->msg_iter);
	int to_copy, to_read, data_read, offset;
	u32 data_length, remaining_data_length, data_offset;
	int rc;

	if (WARN_ON_ONCE(iov_iter_rw(&msg->msg_iter) == WRITE))
		return -EINVAL; /* It's a bug in upper layer to get there */

again:
	/*
	 * No need to hold the reassembly queue lock all the time as we are
	 * the only one reading from the front of the queue. The transport
	 * may add more entries to the back of the queue at the same time
	 */
	log_read(INFO, "size=%d info->reassembly_data_length=%d\n", size,
	log_read(INFO, "size=%zd info->reassembly_data_length=%d\n", size,
		info->reassembly_data_length);
	if (info->reassembly_data_length >= size) {
		int queue_length;
@@ -1813,7 +1848,10 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf,
			if (response->first_segment && size == 4) {
				unsigned int rfc1002_len =
					data_length + remaining_data_length;
				*((__be32 *)buf) = cpu_to_be32(rfc1002_len);
				__be32 rfc1002_hdr = cpu_to_be32(rfc1002_len);
				if (copy_to_iter(&rfc1002_hdr, sizeof(rfc1002_hdr),
						 &msg->msg_iter) != sizeof(rfc1002_hdr))
					return -EFAULT;
				data_read = 4;
				response->first_segment = false;
				log_read(INFO, "returning rfc1002 length %d\n",
@@ -1822,10 +1860,9 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf,
			}

			to_copy = min_t(int, data_length - offset, to_read);
			memcpy(
				buf + data_read,
				(char *)data_transfer + data_offset + offset,
				to_copy);
			if (copy_to_iter((char *)data_transfer + data_offset + offset,
					 to_copy, &msg->msg_iter) != to_copy)
				return -EFAULT;

			/* move on to the next buffer? */
			if (to_copy == data_length - offset) {
@@ -1890,90 +1927,6 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf,
	goto again;
}

/*
 * Receive a page from receive reassembly queue
 * page: the page to read data into
 * to_read: the length of data to read
 * return value: actual data read
 */
static int smbd_recv_page(struct smbd_connection *info,
		struct page *page, unsigned int page_offset,
		unsigned int to_read)
{
	struct smbdirect_socket *sc = &info->socket;
	int ret;
	char *to_address;
	void *page_address;

	/* make sure we have the page ready for read */
	ret = wait_event_interruptible(
		info->wait_reassembly_queue,
		info->reassembly_data_length >= to_read ||
			sc->status != SMBDIRECT_SOCKET_CONNECTED);
	if (ret)
		return ret;

	/* now we can read from reassembly queue and not sleep */
	page_address = kmap_atomic(page);
	to_address = (char *) page_address + page_offset;

	log_read(INFO, "reading from page=%p address=%p to_read=%d\n",
		page, to_address, to_read);

	ret = smbd_recv_buf(info, to_address, to_read);
	kunmap_atomic(page_address);

	return ret;
}

/*
 * Receive data from transport
 * msg: a msghdr point to the buffer, can be ITER_KVEC or ITER_BVEC
 * return: total bytes read, or 0. SMB Direct will not do partial read.
 */
int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
{
	char *buf;
	struct page *page;
	unsigned int to_read, page_offset;
	int rc;

	if (iov_iter_rw(&msg->msg_iter) == WRITE) {
		/* It's a bug in upper layer to get there */
		cifs_dbg(VFS, "Invalid msg iter dir %u\n",
			 iov_iter_rw(&msg->msg_iter));
		rc = -EINVAL;
		goto out;
	}

	switch (iov_iter_type(&msg->msg_iter)) {
	case ITER_KVEC:
		buf = msg->msg_iter.kvec->iov_base;
		to_read = msg->msg_iter.kvec->iov_len;
		rc = smbd_recv_buf(info, buf, to_read);
		break;

	case ITER_BVEC:
		page = msg->msg_iter.bvec->bv_page;
		page_offset = msg->msg_iter.bvec->bv_offset;
		to_read = msg->msg_iter.bvec->bv_len;
		rc = smbd_recv_page(info, page, page_offset, to_read);
		break;

	default:
		/* It's a bug in upper layer to get there */
		cifs_dbg(VFS, "Invalid msg type %d\n",
			 iov_iter_type(&msg->msg_iter));
		rc = -EINVAL;
	}

out:
	/* SMBDirect will read it all or nothing */
	if (rc > 0)
		msg->msg_iter.count = 0;
	return rc;
}

/*
 * Send data to transport
 * Each rqst is transported as a SMBDirect payload
@@ -2032,13 +1985,13 @@ int smbd_send(struct TCP_Server_Info *server,
			klen += rqst->rq_iov[i].iov_len;
		iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, klen);

		rc = smbd_post_send_iter(info, &iter, &remaining_data_length);
		rc = smbd_post_send_full_iter(info, &iter, &remaining_data_length);
		if (rc < 0)
			break;

		if (iov_iter_count(&rqst->rq_iter) > 0) {
			/* And then the data pages if there are any */
			rc = smbd_post_send_iter(info, &rqst->rq_iter,
			rc = smbd_post_send_full_iter(info, &rqst->rq_iter,
						      &remaining_data_length);
			if (rc < 0)
				break;
+12 −12
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ DECLARE_EVENT_CLASS(smb3_rw_err_class,
		__entry->len = len;
		__entry->rc = rc;
	),
	TP_printk("\tR=%08x[%x] xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d",
	TP_printk("R=%08x[%x] xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d",
		  __entry->rreq_debug_id, __entry->rreq_debug_index,
		  __entry->xid, __entry->sesid, __entry->tid, __entry->fid,
		  __entry->offset, __entry->len, __entry->rc)
@@ -190,7 +190,7 @@ DECLARE_EVENT_CLASS(smb3_other_err_class,
		__entry->len = len;
		__entry->rc = rc;
	),
	TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d",
	TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d",
		__entry->xid, __entry->sesid, __entry->tid, __entry->fid,
		__entry->offset, __entry->len, __entry->rc)
)
@@ -247,7 +247,7 @@ DECLARE_EVENT_CLASS(smb3_copy_range_err_class,
		__entry->len = len;
		__entry->rc = rc;
	),
	TP_printk("\txid=%u sid=0x%llx tid=0x%x source fid=0x%llx source offset=0x%llx target fid=0x%llx target offset=0x%llx len=0x%x rc=%d",
	TP_printk("xid=%u sid=0x%llx tid=0x%x source fid=0x%llx source offset=0x%llx target fid=0x%llx target offset=0x%llx len=0x%x rc=%d",
		__entry->xid, __entry->sesid, __entry->tid, __entry->target_fid,
		__entry->src_offset, __entry->target_fid, __entry->target_offset, __entry->len, __entry->rc)
)
@@ -298,7 +298,7 @@ DECLARE_EVENT_CLASS(smb3_copy_range_done_class,
		__entry->target_offset = target_offset;
		__entry->len = len;
	),
	TP_printk("\txid=%u sid=0x%llx tid=0x%x source fid=0x%llx source offset=0x%llx target fid=0x%llx target offset=0x%llx len=0x%x",
	TP_printk("xid=%u sid=0x%llx tid=0x%x source fid=0x%llx source offset=0x%llx target fid=0x%llx target offset=0x%llx len=0x%x",
		__entry->xid, __entry->sesid, __entry->tid, __entry->target_fid,
		__entry->src_offset, __entry->target_fid, __entry->target_offset, __entry->len)
)
@@ -482,7 +482,7 @@ DECLARE_EVENT_CLASS(smb3_fd_class,
		__entry->tid = tid;
		__entry->sesid = sesid;
	),
	TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx",
	TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx",
		__entry->xid, __entry->sesid, __entry->tid, __entry->fid)
)

@@ -521,7 +521,7 @@ DECLARE_EVENT_CLASS(smb3_fd_err_class,
		__entry->sesid = sesid;
		__entry->rc = rc;
	),
	TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx rc=%d",
	TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx rc=%d",
		__entry->xid, __entry->sesid, __entry->tid, __entry->fid,
		__entry->rc)
)
@@ -794,7 +794,7 @@ DECLARE_EVENT_CLASS(smb3_cmd_err_class,
		__entry->status = status;
		__entry->rc = rc;
	),
	TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu status=0x%x rc=%d",
	TP_printk("sid=0x%llx tid=0x%x cmd=%u mid=%llu status=0x%x rc=%d",
		__entry->sesid, __entry->tid, __entry->cmd, __entry->mid,
		__entry->status, __entry->rc)
)
@@ -829,7 +829,7 @@ DECLARE_EVENT_CLASS(smb3_cmd_done_class,
		__entry->cmd = cmd;
		__entry->mid = mid;
	),
	TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu",
	TP_printk("sid=0x%llx tid=0x%x cmd=%u mid=%llu",
		__entry->sesid, __entry->tid,
		__entry->cmd, __entry->mid)
)
@@ -867,7 +867,7 @@ DECLARE_EVENT_CLASS(smb3_mid_class,
		__entry->when_sent = when_sent;
		__entry->when_received = when_received;
	),
	TP_printk("\tcmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu",
	TP_printk("cmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu",
		__entry->cmd, __entry->mid, __entry->pid, __entry->when_sent,
		__entry->when_received)
)
@@ -898,7 +898,7 @@ DECLARE_EVENT_CLASS(smb3_exit_err_class,
		__assign_str(func_name);
		__entry->rc = rc;
	),
	TP_printk("\t%s: xid=%u rc=%d",
	TP_printk("%s: xid=%u rc=%d",
		__get_str(func_name), __entry->xid, __entry->rc)
)

@@ -924,7 +924,7 @@ DECLARE_EVENT_CLASS(smb3_sync_err_class,
		__entry->ino = ino;
		__entry->rc = rc;
	),
	TP_printk("\tino=%lu rc=%d",
	TP_printk("ino=%lu rc=%d",
		__entry->ino, __entry->rc)
)

@@ -950,7 +950,7 @@ DECLARE_EVENT_CLASS(smb3_enter_exit_class,
		__entry->xid = xid;
		__assign_str(func_name);
	),
	TP_printk("\t%s: xid=%u",
	TP_printk("%s: xid=%u",
		__get_str(func_name), __entry->xid)
)