six smb3 client fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmhfImcACgkQiiy9cAdy
 T1EMWwv+LSveqDxylD28bL12GuCudX2b894QzUhckC9DHerRDarLcWSEvRo2/dT1
 y5s//OL2NmvbfxfTnbQLSY7o+r3NOvF6XRAd8ooOSolyEQKWeRO5byBFFszb/PNC
 ajlXX+xgA4VxzZWWOXOFYAz5EIPF4avYxHmvF9BD/RhS6u2k9ZaDCvtOqzreOlfa
 t1V/eqcZwvlde7dObTEgJKgHD5Mej/+vlDRu+vQnrqOYnrLbZgWImOm05lJyKpat
 qibPrnOtejzcWChpjvv2OqF4Jsige/Bzko3W/v60ZOsFfc5eegxAHpGrdbFuI4fN
 E9vP/PA8n9gS8LZZkHTuHv7b78XOx06qhtiNcPNIpmJmlg1I2XlAwa1ifDd5F5Im
 C4sstB2ujJ28k5ReqkYnd5Q8SvVmoCcP+31bP6ibUrk+onYrXPHRJp+CvUrniYIp
 WNHkPwL2khrsRjHb2GnKwQQvdvyZa0/Vams2WtTHsK6CMOQMm14MHcJq1KNsd4oz
 UXNq2D5j
 =vWu+
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2025-06-27 20:38:05 -07:00
commit aaf724ed69
5 changed files with 110 additions and 154 deletions

View File

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

View File

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

View File

@ -875,15 +875,8 @@ globalroot:
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 @@ globalroot:
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 @@ globalroot:
* These paths have same format as Linux symlinks, so no
* conversion is needed.
*/
out_unhandled_target:
linux_target = smb_target;
smb_target = NULL;
}

View File

@ -907,8 +907,10 @@ wait_send_queue:
.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 @@ try_again:
}
/*
* 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 @@ again:
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 @@ again:
}
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 @@ read_rfc1002_done:
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;

View File

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