33 smb3/cifs client changesets, mostly smbdirect cleanup

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmiWLjwACgkQiiy9cAdy
 T1HvVAwAjW4BJ5QOGaDqRXaTtCZ5TvrdtlG3C9J7+YwGuUACT/m3+OffJEVsyWsA
 /FP9r7oJ1T/4tNfD6V/4b8uEScVRMdSkdKedikFBH0UlV/Y2gWCdEXOgZOw19WKb
 HBR35scMBmcFu/v+dJKpkAduNEJTQ35Is+RynY9PX9iJNTGYRWG3Oj1sod9tDHrA
 suWAsNW6+xV6kQthyZmCWqVjz1lgkLp2MaYOGtoYBZZ5Z1RVoDu/+bwJBUhiGnzW
 68PywL8ogqaszZLs3lv7vAXTjEeVzKOD43Tffs5/762eI7+VvMxJBR4aLfZGY5nE
 uYK/9doGto3hlraQ5EHtOe11DRN3xqoHs6AbBlfvq95Ex2WksoWsmnRlwseUzuEy
 0uvHxk7sptNiG2RP/yeCNGKKLPNJIXwPSHuedkk8K01tJf+J9JVZp2hllucGzrYb
 3q7g623XJJUvCybBCq/gmPZMunhqxCxS+d5ZLwO3OL8xT9WB4ryRum7GoPotZUsp
 HqP2NDug
 =eX1a
 -----END PGP SIGNATURE-----

Merge tag 'v6.17rc-part2-SMB3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull more smb client updates from Steve French:
 "Non-smbdirect:
   - Fix null ptr deref caused by delay in global spinlock
     initialization
   - Two fixes for native symlink creation with SMB3.1.1 POSIX
     Extensions
   - Fix for socket special file creation with SMB3.1.1 POSIX Exensions
   - Reduce lock contention by splitting out mid_counter_lock
   - move SMB1 transport code to separate file to reduce module size
     when support for legacy servers is disabled
   - Two cleanup patches: rename mid_lock to make it clearer what it
     protects and one to convert mid flags to bool to make clearer

  Smbdirect/RDMA restructuring and fixes:
   - Fix for error handling in send done
   - Remove unneeded empty packet queue
   - Fix put_receive_buffer error path
   - Two fixes to recv_done error paths
   - Remove unused variable
   - Improve response and recvmsg type handling
   - Fix handling of incoming message type
   - Two cleanup fixes for better handling smbdirect recv io
   - Two cleanup fixes for socket spinlock
   - Two patches that add socket reassembly struct
   - Remove unused connection_status enum
   - Use flag in common header for SMBDIRECT_RECV_IO_MAX_SGE
   - Two cleanup patches to introduce and use smbdirect send io
   - Two cleanup patches to introduce and use smbdirect send_io struct
   - Fix to return error if rdma connect takes longer than 5 seconds
   - Error logging improvements
   - Fix redundand call to init_waitqueue_head
   - Remove unneeded wait queue"

* tag 'v6.17rc-part2-SMB3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: (33 commits)
  smb: client: only use a single wait_queue to monitor smbdirect connection status
  smb: client: don't call init_waitqueue_head(&info->conn_wait) twice in _smbd_get_connection
  smb: client: improve logging in smbd_conn_upcall()
  smb: client: return an error if rdma_connect does not return within 5 seconds
  smb: client: make use of smbdirect_socket.{send,recv}_io.mem.{cache,pool}
  smb: smbdirect: add smbdirect_socket.{send,recv}_io.mem.{cache,pool}
  smb: client: make use of struct smbdirect_send_io
  smb: smbdirect: introduce struct smbdirect_send_io
  smb: client: make use of SMBDIRECT_RECV_IO_MAX_SGE
  smb: smbdirect: add SMBDIRECT_RECV_IO_MAX_SGE
  smb: client: remove unused enum smbd_connection_status
  smb: client: make use of smbdirect_socket.recv_io.reassembly.*
  smb: smbdirect: introduce smbdirect_socket.recv_io.reassembly.*
  smb: client: make use of smb: smbdirect_socket.recv_io.free.{list,lock}
  smb: smbdirect: introduce smbdirect_socket.recv_io.free.{list,lock}
  smb: client: make use of struct smbdirect_recv_io
  smb: smbdirect: introduce struct smbdirect_recv_io
  smb: client: make use of smbdirect_socket->recv_io.expected
  smb: smbdirect: introduce smbdirect_socket.recv_io.expected
  smb: client: remove unused smbd_connection->fragment_reassembly_remaining
  ...
This commit is contained in:
Linus Torvalds 2025-08-09 07:12:43 +03:00
commit cfaf773b79
20 changed files with 1066 additions and 1031 deletions

View file

@ -32,6 +32,6 @@ cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o
cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o
cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o cifstransport.o
cifs-$(CONFIG_CIFS_COMPRESSION) += compress.o compress/lz77.o

View file

@ -60,7 +60,7 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
return;
cifs_dbg(VFS, "Dump pending requests:\n");
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n",
mid_entry->mid_state,
@ -83,7 +83,7 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
mid_entry->resp_buf, 62);
}
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
#endif /* CONFIG_CIFS_DEBUG2 */
}
@ -412,6 +412,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
#ifdef CONFIG_CIFS_SMB_DIRECT
struct smbdirect_socket *sc;
struct smbdirect_socket_parameters *sp;
#endif
@ -436,7 +437,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\nSMBDirect transport not available");
goto skip_rdma;
}
sp = &server->smbd_conn->socket.parameters;
sc = &server->smbd_conn->socket;
sp = &sc->parameters;
seq_printf(m, "\nSMBDirect (in hex) protocol version: %x "
"transport status: %x",
@ -465,15 +467,13 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\nRead Queue count_reassembly_queue: %x "
"count_enqueue_reassembly_queue: %x "
"count_dequeue_reassembly_queue: %x "
"fragment_reassembly_remaining: %x "
"reassembly_data_length: %x "
"reassembly_queue_length: %x",
server->smbd_conn->count_reassembly_queue,
server->smbd_conn->count_enqueue_reassembly_queue,
server->smbd_conn->count_dequeue_reassembly_queue,
server->smbd_conn->fragment_reassembly_remaining,
server->smbd_conn->reassembly_data_length,
server->smbd_conn->reassembly_queue_length);
sc->recv_io.reassembly.data_length,
sc->recv_io.reassembly.queue_length);
seq_printf(m, "\nCurrent Credits send_credits: %x "
"receive_credits: %x receive_credit_target: %x",
atomic_read(&server->smbd_conn->send_credits),
@ -481,10 +481,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
server->smbd_conn->receive_credit_target);
seq_printf(m, "\nPending send_pending: %x ",
atomic_read(&server->smbd_conn->send_pending));
seq_printf(m, "\nReceive buffers count_receive_queue: %x "
"count_empty_packet_queue: %x",
server->smbd_conn->count_receive_queue,
server->smbd_conn->count_empty_packet_queue);
seq_printf(m, "\nReceive buffers count_receive_queue: %x ",
server->smbd_conn->count_receive_queue);
seq_printf(m, "\nMR responder_resources: %x "
"max_frmr_depth: %x mr_type: %x",
server->smbd_conn->responder_resources,
@ -672,7 +670,7 @@ skip_rdma:
seq_printf(m, "\n\tServer ConnectionId: 0x%llx",
chan_server->conn_id);
spin_lock(&chan_server->mid_lock);
spin_lock(&chan_server->mid_queue_lock);
list_for_each_entry(mid_entry, &chan_server->pending_mid_q, qhead) {
seq_printf(m, "\n\t\tState: %d com: %d pid: %d cbdata: %p mid %llu",
mid_entry->mid_state,
@ -681,7 +679,7 @@ skip_rdma:
mid_entry->callback_data,
mid_entry->mid);
}
spin_unlock(&chan_server->mid_lock);
spin_unlock(&chan_server->mid_queue_lock);
}
spin_unlock(&ses->chan_lock);
seq_puts(m, "\n--\n");

View file

@ -77,7 +77,7 @@ unsigned int global_secflags = CIFSSEC_DEF;
unsigned int GlobalCurrentXid; /* protected by GlobalMid_Lock */
unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Lock */
unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Lock */
spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */
DEFINE_SPINLOCK(GlobalMid_Lock); /* protects above & list operations on midQ entries */
/*
* Global counters, updated atomically
@ -97,7 +97,7 @@ atomic_t total_buf_alloc_count;
atomic_t total_small_buf_alloc_count;
#endif/* STATS2 */
struct list_head cifs_tcp_ses_list;
spinlock_t cifs_tcp_ses_lock;
DEFINE_SPINLOCK(cifs_tcp_ses_lock);
static const struct super_operations cifs_super_ops;
unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
module_param(CIFSMaxBufSize, uint, 0444);
@ -723,7 +723,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
else
seq_puts(s, ",nativesocket");
seq_show_option(s, "symlink",
cifs_symlink_type_str(get_cifs_symlink_type(cifs_sb)));
cifs_symlink_type_str(cifs_symlink_type(cifs_sb)));
seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize);
seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize);
@ -1863,8 +1863,6 @@ init_cifs(void)
GlobalCurrentXid = 0;
GlobalTotalActiveXid = 0;
GlobalMaxActiveXid = 0;
spin_lock_init(&cifs_tcp_ses_lock);
spin_lock_init(&GlobalMid_Lock);
cifs_lock_secret = get_random_u32();

View file

@ -732,7 +732,8 @@ struct TCP_Server_Info {
#endif
wait_queue_head_t response_q;
wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
spinlock_t mid_lock; /* protect mid queue and it's entries */
spinlock_t mid_queue_lock; /* protect mid queue */
spinlock_t mid_counter_lock;
struct list_head pending_mid_q;
bool noblocksnd; /* use blocking sendmsg */
bool noautotune; /* do not autotune send buf sizes */
@ -770,7 +771,7 @@ struct TCP_Server_Info {
/* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */
unsigned int capabilities; /* selective disabling of caps by smb sess */
int timeAdj; /* Adjust for difference in server time zone in sec */
__u64 CurrentMid; /* multiplex id - rotating counter, protected by GlobalMid_Lock */
__u64 current_mid; /* multiplex id - rotating counter, protected by mid_counter_lock */
char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */
/* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
@ -1729,9 +1730,10 @@ struct mid_q_entry {
unsigned int resp_buf_size;
int mid_state; /* wish this were enum but can not pass to wait_event */
int mid_rc; /* rc for MID_RC */
unsigned int mid_flags;
__le16 command; /* smb command code */
unsigned int optype; /* operation type */
bool wait_cancelled:1; /* Cancelled while waiting for response */
bool deleted_from_q:1; /* Whether Mid has been dequeued frem pending_mid_q */
bool large_buf:1; /* if valid response, is pointer to large buf */
bool multiRsp:1; /* multiple trans2 responses for one request */
bool multiEnd:1; /* both received */
@ -1893,10 +1895,6 @@ static inline bool is_replayable_error(int error)
#define MID_RESPONSE_READY 0x40 /* ready for other process handle the rsp */
#define MID_RC 0x80 /* mid_rc contains custom rc */
/* Flags */
#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */
#define MID_DELETED 2 /* Mid has been dequeued/deleted */
/* Types of response buffer returned from SendReceive2 */
#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
#define CIFS_SMALL_BUFFER 1
@ -2007,9 +2005,9 @@ require use of the stronger protocol */
* GlobalCurrentXid
* GlobalTotalActiveXid
* TCP_Server_Info->srv_lock (anything in struct not protected by another lock and can change)
* TCP_Server_Info->mid_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session
* ->CurrentMid
* (any changes in mid_q_entry fields)
* TCP_Server_Info->mid_queue_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session
* mid_q_entry->deleted_from_q
* TCP_Server_Info->mid_counter_lock TCP_Server_Info->current_mid cifs_get_tcp_session
* TCP_Server_Info->req_lock TCP_Server_Info->in_flight cifs_get_tcp_session
* ->credits
* ->echo_credits
@ -2377,4 +2375,9 @@ static inline bool cifs_netbios_name(const char *name, size_t namelen)
return ret;
}
#define CIFS_REPARSE_SUPPORT(tcon) \
((tcon)->posix_extensions || \
(le32_to_cpu((tcon)->fsAttrInfo.Attributes) & \
FILE_SUPPORTS_REPARSE_POINTS))
#endif /* _CIFS_GLOB_H */

View file

@ -116,16 +116,31 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
int * /* bytes returned */ , const int);
extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
char *in_buf, int flags);
int cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server);
extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *,
struct TCP_Server_Info *,
struct smb_rqst *);
extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
struct smb_rqst *);
int __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
struct smb_rqst *rqst);
extern int cifs_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error);
int wait_for_free_request(struct TCP_Server_Info *server, const int flags,
unsigned int *instance);
extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
size_t size, size_t *num,
struct cifs_credits *credits);
static inline int
send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
struct mid_q_entry *mid)
{
return server->ops->send_cancel ?
server->ops->send_cancel(server, rqst, mid) : 0;
}
int wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ);
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
struct kvec *, int /* nvec to send */,
int * /* type of buf returned */, const int flags,

View file

@ -2751,7 +2751,7 @@ int cifs_query_reparse_point(const unsigned int xid,
if (cap_unix(tcon->ses))
return -EOPNOTSUPP;
if (!(le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS))
if (!CIFS_REPARSE_SUPPORT(tcon))
return -EOPNOTSUPP;
oparms = (struct cifs_open_parms) {
@ -2879,7 +2879,7 @@ struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
* attempt to create reparse point. This will prevent creating unusable
* empty object on the server.
*/
if (!(le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS))
if (!CIFS_REPARSE_SUPPORT(tcon))
return ERR_PTR(-EOPNOTSUPP);
#ifndef CONFIG_CIFS_XATTR

View file

@ -0,0 +1,566 @@
// SPDX-License-Identifier: LGPL-2.1
/*
*
* Copyright (C) International Business Machines Corp., 2002,2008
* Author(s): Steve French (sfrench@us.ibm.com)
* Jeremy Allison (jra@samba.org) 2006.
*
*/
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/gfp.h>
#include <linux/wait.h>
#include <linux/net.h>
#include <linux/delay.h>
#include <linux/freezer.h>
#include <linux/tcp.h>
#include <linux/bvec.h>
#include <linux/highmem.h>
#include <linux/uaccess.h>
#include <linux/processor.h>
#include <linux/mempool.h>
#include <linux/sched/signal.h>
#include <linux/task_io_accounting_ops.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "smb2proto.h"
#include "smbdirect.h"
#include "compress.h"
/* Max number of iovectors we can use off the stack when sending requests. */
#define CIFS_MAX_IOV_SIZE 8
static struct mid_q_entry *
alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
{
struct mid_q_entry *temp;
if (server == NULL) {
cifs_dbg(VFS, "%s: null TCP session\n", __func__);
return NULL;
}
temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
memset(temp, 0, sizeof(struct mid_q_entry));
kref_init(&temp->refcount);
temp->mid = get_mid(smb_buffer);
temp->pid = current->pid;
temp->command = cpu_to_le16(smb_buffer->Command);
cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
/* easier to use jiffies */
/* when mid allocated can be before when sent */
temp->when_alloc = jiffies;
temp->server = server;
/*
* The default is for the mid to be synchronous, so the
* default callback just wakes up the current task.
*/
get_task_struct(current);
temp->creator = current;
temp->callback = cifs_wake_up_task;
temp->callback_data = current;
atomic_inc(&mid_count);
temp->mid_state = MID_REQUEST_ALLOCATED;
return temp;
}
int
smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
unsigned int smb_buf_length)
{
struct kvec iov[2];
struct smb_rqst rqst = { .rq_iov = iov,
.rq_nvec = 2 };
iov[0].iov_base = smb_buffer;
iov[0].iov_len = 4;
iov[1].iov_base = (char *)smb_buffer + 4;
iov[1].iov_len = smb_buf_length;
return __smb_send_rqst(server, 1, &rqst);
}
static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
struct mid_q_entry **ppmidQ)
{
spin_lock(&ses->ses_lock);
if (ses->ses_status == SES_NEW) {
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
(in_buf->Command != SMB_COM_NEGOTIATE)) {
spin_unlock(&ses->ses_lock);
return -EAGAIN;
}
/* else ok - we are setting up session */
}
if (ses->ses_status == SES_EXITING) {
/* check if SMB session is bad because we are setting it up */
if (in_buf->Command != SMB_COM_LOGOFF_ANDX) {
spin_unlock(&ses->ses_lock);
return -EAGAIN;
}
/* else ok - we are shutting down session */
}
spin_unlock(&ses->ses_lock);
*ppmidQ = alloc_mid(in_buf, ses->server);
if (*ppmidQ == NULL)
return -ENOMEM;
spin_lock(&ses->server->mid_queue_lock);
list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
spin_unlock(&ses->server->mid_queue_lock);
return 0;
}
struct mid_q_entry *
cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
{
int rc;
struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
if (rqst->rq_iov[0].iov_len != 4 ||
rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
return ERR_PTR(-EIO);
/* enable signing if server requires it */
if (server->sign)
hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
mid = alloc_mid(hdr, server);
if (mid == NULL)
return ERR_PTR(-ENOMEM);
rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
if (rc) {
release_mid(mid);
return ERR_PTR(rc);
}
return mid;
}
/*
*
* Send an SMB Request. No response info (other than return code)
* needs to be parsed.
*
* flags indicate the type of request buffer and how long to wait
* and whether to log NT STATUS code (error) before mapping it to POSIX error
*
*/
int
SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
char *in_buf, int flags)
{
int rc;
struct kvec iov[1];
struct kvec rsp_iov;
int resp_buf_type;
iov[0].iov_base = in_buf;
iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
flags |= CIFS_NO_RSP_BUF;
rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
return rc;
}
int
cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
bool log_error)
{
unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
dump_smb(mid->resp_buf, min_t(u32, 92, len));
/* convert the length into a more usable form */
if (server->sign) {
struct kvec iov[2];
int rc = 0;
struct smb_rqst rqst = { .rq_iov = iov,
.rq_nvec = 2 };
iov[0].iov_base = mid->resp_buf;
iov[0].iov_len = 4;
iov[1].iov_base = (char *)mid->resp_buf + 4;
iov[1].iov_len = len - 4;
/* FIXME: add code to kill session */
rc = cifs_verify_signature(&rqst, server,
mid->sequence_number);
if (rc)
cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
rc);
}
/* BB special case reconnect tid and uid here? */
return map_and_check_smb_error(mid, log_error);
}
struct mid_q_entry *
cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
struct smb_rqst *rqst)
{
int rc;
struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
if (rqst->rq_iov[0].iov_len != 4 ||
rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
return ERR_PTR(-EIO);
rc = allocate_mid(ses, hdr, &mid);
if (rc)
return ERR_PTR(rc);
rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
if (rc) {
delete_mid(mid);
return ERR_PTR(rc);
}
return mid;
}
int
SendReceive2(const unsigned int xid, struct cifs_ses *ses,
struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
const int flags, struct kvec *resp_iov)
{
struct smb_rqst rqst;
struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov;
int rc;
if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec),
GFP_KERNEL);
if (!new_iov) {
/* otherwise cifs_send_recv below sets resp_buf_type */
*resp_buf_type = CIFS_NO_BUFFER;
return -ENOMEM;
}
} else
new_iov = s_iov;
/* 1st iov is a RFC1001 length followed by the rest of the packet */
memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
new_iov[0].iov_base = new_iov[1].iov_base;
new_iov[0].iov_len = 4;
new_iov[1].iov_base += 4;
new_iov[1].iov_len -= 4;
memset(&rqst, 0, sizeof(struct smb_rqst));
rqst.rq_iov = new_iov;
rqst.rq_nvec = n_vec + 1;
rc = cifs_send_recv(xid, ses, ses->server,
&rqst, resp_buf_type, flags, resp_iov);
if (n_vec + 1 > CIFS_MAX_IOV_SIZE)
kfree(new_iov);
return rc;
}
int
SendReceive(const unsigned int xid, struct cifs_ses *ses,
struct smb_hdr *in_buf, struct smb_hdr *out_buf,
int *pbytes_returned, const int flags)
{
int rc = 0;
struct mid_q_entry *midQ;
unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
struct cifs_credits credits = { .value = 1, .instance = 0 };
struct TCP_Server_Info *server;
if (ses == NULL) {
cifs_dbg(VFS, "Null smb session\n");
return -EIO;
}
server = ses->server;
if (server == NULL) {
cifs_dbg(VFS, "Null tcp session\n");
return -EIO;
}
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&server->srv_lock);
return -ENOENT;
}
spin_unlock(&server->srv_lock);
/* Ensure that we do not send more than 50 overlapping requests
to the same server. We may make this configurable later or
use ses->maxReq */
if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
len);
return -EIO;
}
rc = wait_for_free_request(server, flags, &credits.instance);
if (rc)
return rc;
/* make sure that we sign in the same order that we send on this socket
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
cifs_server_lock(server);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
cifs_server_unlock(server);
/* Update # of requests on wire to server */
add_credits(server, &credits, 0);
return rc;
}
rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
if (rc) {
cifs_server_unlock(server);
goto out;
}
midQ->mid_state = MID_REQUEST_SUBMITTED;
rc = smb_send(server, in_buf, len);
cifs_save_when_sent(midQ);
if (rc < 0)
server->sequence_number -= 2;
cifs_server_unlock(server);
if (rc < 0)
goto out;
rc = wait_for_response(server, midQ);
if (rc != 0) {
send_cancel(server, &rqst, midQ);
spin_lock(&server->mid_queue_lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED) {
/* no longer considered to be "in-flight" */
midQ->callback = release_mid;
spin_unlock(&server->mid_queue_lock);
add_credits(server, &credits, 0);
return rc;
}
spin_unlock(&server->mid_queue_lock);
}
rc = cifs_sync_mid_result(midQ, server);
if (rc != 0) {
add_credits(server, &credits, 0);
return rc;
}
if (!midQ->resp_buf || !out_buf ||
midQ->mid_state != MID_RESPONSE_READY) {
rc = -EIO;
cifs_server_dbg(VFS, "Bad MID state?\n");
goto out;
}
*pbytes_returned = get_rfc1002_length(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, server, 0);
out:
delete_mid(midQ);
add_credits(server, &credits, 0);
return rc;
}
/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
blocking lock to return. */
static int
send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
struct smb_hdr *in_buf,
struct smb_hdr *out_buf)
{
int bytes_returned;
struct cifs_ses *ses = tcon->ses;
LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
/* We just modify the current in_buf to change
the type of lock from LOCKING_ANDX_SHARED_LOCK
or LOCKING_ANDX_EXCLUSIVE_LOCK to
LOCKING_ANDX_CANCEL_LOCK. */
pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
pSMB->Timeout = 0;
pSMB->hdr.Mid = get_next_mid(ses->server);
return SendReceive(xid, ses, in_buf, out_buf,
&bytes_returned, 0);
}
int
SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
struct smb_hdr *in_buf, struct smb_hdr *out_buf,
int *pbytes_returned)
{
int rc = 0;
int rstart = 0;
struct mid_q_entry *midQ;
struct cifs_ses *ses;
unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
unsigned int instance;
struct TCP_Server_Info *server;
if (tcon == NULL || tcon->ses == NULL) {
cifs_dbg(VFS, "Null smb session\n");
return -EIO;
}
ses = tcon->ses;
server = ses->server;
if (server == NULL) {
cifs_dbg(VFS, "Null tcp session\n");
return -EIO;
}
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&server->srv_lock);
return -ENOENT;
}
spin_unlock(&server->srv_lock);
/* Ensure that we do not send more than 50 overlapping requests
to the same server. We may make this configurable later or
use ses->maxReq */
if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
len);
return -EIO;
}
rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
if (rc)
return rc;
/* make sure that we sign in the same order that we send on this socket
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
cifs_server_lock(server);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
cifs_server_unlock(server);
return rc;
}
rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
if (rc) {
delete_mid(midQ);
cifs_server_unlock(server);
return rc;
}
midQ->mid_state = MID_REQUEST_SUBMITTED;
rc = smb_send(server, in_buf, len);
cifs_save_when_sent(midQ);
if (rc < 0)
server->sequence_number -= 2;
cifs_server_unlock(server);
if (rc < 0) {
delete_mid(midQ);
return rc;
}
/* Wait for a reply - allow signals to interrupt. */
rc = wait_event_interruptible(server->response_q,
(!(midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED)) ||
((server->tcpStatus != CifsGood) &&
(server->tcpStatus != CifsNew)));
/* Were we interrupted by a signal ? */
spin_lock(&server->srv_lock);
if ((rc == -ERESTARTSYS) &&
(midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED) &&
((server->tcpStatus == CifsGood) ||
(server->tcpStatus == CifsNew))) {
spin_unlock(&server->srv_lock);
if (in_buf->Command == SMB_COM_TRANSACTION2) {
/* POSIX lock. We send a NT_CANCEL SMB to cause the
blocking lock to return. */
rc = send_cancel(server, &rqst, midQ);
if (rc) {
delete_mid(midQ);
return rc;
}
} else {
/* Windows lock. We send a LOCKINGX_CANCEL_LOCK
to cause the blocking lock to return. */
rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
/* If we get -ENOLCK back the lock may have
already been removed. Don't exit in this case. */
if (rc && rc != -ENOLCK) {
delete_mid(midQ);
return rc;
}
}
rc = wait_for_response(server, midQ);
if (rc) {
send_cancel(server, &rqst, midQ);
spin_lock(&server->mid_queue_lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED) {
/* no longer considered to be "in-flight" */
midQ->callback = release_mid;
spin_unlock(&server->mid_queue_lock);
return rc;
}
spin_unlock(&server->mid_queue_lock);
}
/* We got the response - restart system call. */
rstart = 1;
spin_lock(&server->srv_lock);
}
spin_unlock(&server->srv_lock);
rc = cifs_sync_mid_result(midQ, server);
if (rc != 0)
return rc;
/* rcvd frame is ok */
if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_READY) {
rc = -EIO;
cifs_tcon_dbg(VFS, "Bad MID state?\n");
goto out;
}
*pbytes_returned = get_rfc1002_length(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, server, 0);
out:
delete_mid(midQ);
if (rstart && rc == -EACCES)
return -ERESTARTSYS;
return rc;
}

View file

@ -321,15 +321,15 @@ cifs_abort_connection(struct TCP_Server_Info *server)
/* mark submitted MIDs for retry and issue callback */
INIT_LIST_HEAD(&retry_list);
cifs_dbg(FYI, "%s: moving mids to private list\n", __func__);
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) {
kref_get(&mid->refcount);
if (mid->mid_state == MID_REQUEST_SUBMITTED)
mid->mid_state = MID_RETRY_NEEDED;
list_move(&mid->qhead, &retry_list);
mid->mid_flags |= MID_DELETED;
mid->deleted_from_q = true;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
cifs_server_unlock(server);
cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
@ -358,7 +358,7 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
}
cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
trace_smb3_reconnect(server->CurrentMid, server->conn_id,
trace_smb3_reconnect(server->current_mid, server->conn_id,
server->hostname);
server->tcpStatus = CifsNeedReconnect;
@ -884,13 +884,13 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
* server there should be exactly one pending mid
* corresponding to SMB1/SMB2 Negotiate packet.
*/
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) {
kref_get(&mid->refcount);
list_move(&mid->qhead, &dispose_list);
mid->mid_flags |= MID_DELETED;
mid->deleted_from_q = true;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
/* Now try to reconnect once with NetBIOS session. */
server->with_rfc1001 = true;
@ -957,7 +957,7 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
#ifdef CONFIG_CIFS_STATS2
mid->when_received = jiffies;
#endif
spin_lock(&mid->server->mid_lock);
spin_lock(&mid->server->mid_queue_lock);
if (!malformed)
mid->mid_state = MID_RESPONSE_RECEIVED;
else
@ -966,13 +966,13 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
* Trying to handle/dequeue a mid after the send_recv()
* function has finished processing it is a bug.
*/
if (mid->mid_flags & MID_DELETED) {
spin_unlock(&mid->server->mid_lock);
if (mid->deleted_from_q == true) {
spin_unlock(&mid->server->mid_queue_lock);
pr_warn_once("trying to dequeue a deleted mid\n");
} else {
list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
spin_unlock(&mid->server->mid_lock);
mid->deleted_from_q = true;
spin_unlock(&mid->server->mid_queue_lock);
}
}
@ -1101,16 +1101,16 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
struct list_head *tmp, *tmp2;
LIST_HEAD(dispose_list);
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
cifs_dbg(FYI, "Clearing mid %llu\n", mid_entry->mid);
kref_get(&mid_entry->refcount);
mid_entry->mid_state = MID_SHUTDOWN;
list_move(&mid_entry->qhead, &dispose_list);
mid_entry->mid_flags |= MID_DELETED;
mid_entry->deleted_from_q = true;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
/* now walk dispose list and issue callbacks */
list_for_each_safe(tmp, tmp2, &dispose_list) {
@ -1242,7 +1242,7 @@ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
trace_smb3_hdr_credits(server->CurrentMid,
trace_smb3_hdr_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
le16_to_cpu(shdr->CreditRequest), in_flight);
cifs_server_dbg(FYI, "%s: added %u credits total=%d\n",
@ -1822,7 +1822,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
tcp_ses->compression.requested = ctx->compress;
spin_lock_init(&tcp_ses->req_lock);
spin_lock_init(&tcp_ses->srv_lock);
spin_lock_init(&tcp_ses->mid_lock);
spin_lock_init(&tcp_ses->mid_queue_lock);
spin_lock_init(&tcp_ses->mid_counter_lock);
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);

View file

@ -1652,6 +1652,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
pr_warn_once("conflicting posix mount options specified\n");
ctx->linux_ext = 1;
ctx->no_linux_ext = 0;
ctx->nonativesocket = 1; /* POSIX mounts use NFS style reparse points */
}
break;
case Opt_nocase:
@ -1829,24 +1830,6 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
return -EINVAL;
}
enum cifs_symlink_type get_cifs_symlink_type(struct cifs_sb_info *cifs_sb)
{
if (cifs_sb->ctx->symlink_type == CIFS_SYMLINK_TYPE_DEFAULT) {
if (cifs_sb->ctx->mfsymlinks)
return CIFS_SYMLINK_TYPE_MFSYMLINKS;
else if (cifs_sb->ctx->sfu_emul)
return CIFS_SYMLINK_TYPE_SFU;
else if (cifs_sb->ctx->linux_ext && !cifs_sb->ctx->no_linux_ext)
return CIFS_SYMLINK_TYPE_UNIX;
else if (cifs_sb->ctx->reparse_type != CIFS_REPARSE_TYPE_NONE)
return CIFS_SYMLINK_TYPE_NATIVE;
else
return CIFS_SYMLINK_TYPE_NONE;
} else {
return cifs_sb->ctx->symlink_type;
}
}
int smb3_init_fs_context(struct fs_context *fc)
{
struct smb3_fs_context *ctx;

View file

@ -341,7 +341,23 @@ struct smb3_fs_context {
extern const struct fs_parameter_spec smb3_fs_parameters[];
extern enum cifs_symlink_type get_cifs_symlink_type(struct cifs_sb_info *cifs_sb);
static inline enum cifs_symlink_type cifs_symlink_type(struct cifs_sb_info *cifs_sb)
{
bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions;
if (cifs_sb->ctx->symlink_type != CIFS_SYMLINK_TYPE_DEFAULT)
return cifs_sb->ctx->symlink_type;
if (cifs_sb->ctx->mfsymlinks)
return CIFS_SYMLINK_TYPE_MFSYMLINKS;
else if (cifs_sb->ctx->sfu_emul)
return CIFS_SYMLINK_TYPE_SFU;
else if (cifs_sb->ctx->linux_ext && !cifs_sb->ctx->no_linux_ext)
return posix ? CIFS_SYMLINK_TYPE_NATIVE : CIFS_SYMLINK_TYPE_UNIX;
else if (cifs_sb->ctx->reparse_type != CIFS_REPARSE_TYPE_NONE)
return CIFS_SYMLINK_TYPE_NATIVE;
return CIFS_SYMLINK_TYPE_NONE;
}
extern int smb3_init_fs_context(struct fs_context *fc);
extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx);

View file

@ -605,14 +605,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
/* BB what if DFS and this volume is on different share? BB */
rc = -EOPNOTSUPP;
switch (get_cifs_symlink_type(cifs_sb)) {
case CIFS_SYMLINK_TYPE_DEFAULT:
/* should not happen, get_cifs_symlink_type() resolves the default */
break;
case CIFS_SYMLINK_TYPE_NONE:
break;
switch (cifs_symlink_type(cifs_sb)) {
case CIFS_SYMLINK_TYPE_UNIX:
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
if (pTcon->unix_ext) {
@ -642,12 +635,14 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
case CIFS_SYMLINK_TYPE_NATIVE:
case CIFS_SYMLINK_TYPE_NFS:
case CIFS_SYMLINK_TYPE_WSL:
if (le32_to_cpu(pTcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS) {
if (CIFS_REPARSE_SUPPORT(pTcon)) {
rc = create_reparse_symlink(xid, inode, direntry, pTcon,
full_path, symname);
goto symlink_exit;
}
break;
default:
break;
}
if (rc == 0) {

View file

@ -38,7 +38,7 @@ int create_reparse_symlink(const unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, const char *symname)
{
switch (get_cifs_symlink_type(CIFS_SB(inode->i_sb))) {
switch (cifs_symlink_type(CIFS_SB(inode->i_sb))) {
case CIFS_SYMLINK_TYPE_NATIVE:
return create_native_symlink(xid, inode, dentry, tcon, full_path, symname);
case CIFS_SYMLINK_TYPE_NFS:

View file

@ -95,17 +95,17 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
struct smb_hdr *buf = (struct smb_hdr *)buffer;
struct mid_q_entry *mid;
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if (compare_mid(mid->mid, buf) &&
mid->mid_state == MID_REQUEST_SUBMITTED &&
le16_to_cpu(mid->command) == buf->Command) {
kref_get(&mid->refcount);
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return mid;
}
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return NULL;
}
@ -169,10 +169,9 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
__u16 last_mid, cur_mid;
bool collision, reconnect = false;
spin_lock(&server->mid_lock);
spin_lock(&server->mid_counter_lock);
/* mid is 16 bit only for CIFS/SMB */
cur_mid = (__u16)((server->CurrentMid) & 0xffff);
cur_mid = (__u16)((server->current_mid) & 0xffff);
/* we do not want to loop forever */
last_mid = cur_mid;
cur_mid++;
@ -198,6 +197,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
cur_mid++;
num_mids = 0;
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
++num_mids;
if (mid_entry->mid == cur_mid &&
@ -207,6 +207,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
break;
}
}
spin_unlock(&server->mid_queue_lock);
/*
* if we have more than 32k mids in the list, then something
@ -223,12 +224,12 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
if (!collision) {
mid = (__u64)cur_mid;
server->CurrentMid = mid;
server->current_mid = mid;
break;
}
cur_mid++;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_counter_lock);
if (reconnect) {
cifs_signal_cifsd_for_reconnect(server, false);
@ -1272,7 +1273,7 @@ cifs_make_node(unsigned int xid, struct inode *inode,
*/
return cifs_sfu_make_node(xid, inode, dentry, tcon,
full_path, mode, dev);
} else if (le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS) {
} else if (CIFS_REPARSE_SUPPORT(tcon)) {
/*
* mknod via reparse points requires server support for
* storing reparse points, which is available since

View file

@ -1346,9 +1346,8 @@ struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
* attempt to create reparse point. This will prevent creating unusable
* empty object on the server.
*/
if (!(le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS))
if (!tcon->posix_extensions)
return ERR_PTR(-EOPNOTSUPP);
if (!CIFS_REPARSE_SUPPORT(tcon))
return ERR_PTR(-EOPNOTSUPP);
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
SYNCHRONIZE | DELETE |

View file

@ -91,7 +91,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
if (*val > 65000) {
*val = 65000; /* Don't get near 64K credits, avoid srv bugs */
pr_warn_once("server overflowed SMB3 credits\n");
trace_smb3_overflow_credits(server->CurrentMid,
trace_smb3_overflow_credits(server->current_mid,
server->conn_id, server->hostname, *val,
add, server->in_flight);
}
@ -136,7 +136,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
wake_up(&server->request_q);
if (reconnect_detected) {
trace_smb3_reconnect_detected(server->CurrentMid,
trace_smb3_reconnect_detected(server->current_mid,
server->conn_id, server->hostname, scredits, add, in_flight);
cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n",
@ -144,7 +144,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
}
if (reconnect_with_invalid_credits) {
trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
trace_smb3_reconnect_with_invalid_credits(server->current_mid,
server->conn_id, server->hostname, scredits, add, in_flight);
cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n",
optype, scredits, add);
@ -176,7 +176,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
break;
}
trace_smb3_add_credits(server->CurrentMid,
trace_smb3_add_credits(server->current_mid,
server->conn_id, server->hostname, scredits, add, in_flight);
cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits);
}
@ -203,7 +203,7 @@ smb2_set_credits(struct TCP_Server_Info *server, const int val)
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_set_credits(server->CurrentMid,
trace_smb3_set_credits(server->current_mid,
server->conn_id, server->hostname, scredits, val, in_flight);
cifs_dbg(FYI, "%s: set %u credits\n", __func__, val);
@ -288,7 +288,7 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, size_t size,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_wait_credits(server->CurrentMid,
trace_smb3_wait_credits(server->current_mid,
server->conn_id, server->hostname, scredits, -(credits->value), in_flight);
cifs_dbg(FYI, "%s: removed %u credits total=%d\n",
__func__, credits->value, scredits);
@ -316,7 +316,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
server->credits, server->in_flight,
new_val - credits->value,
cifs_trace_rw_credits_no_adjust_up);
trace_smb3_too_many_credits(server->CurrentMid,
trace_smb3_too_many_credits(server->current_mid,
server->conn_id, server->hostname, 0, credits->value - new_val, 0);
cifs_server_dbg(VFS, "R=%x[%x] request has less credits (%d) than required (%d)",
subreq->rreq->debug_id, subreq->subreq.debug_index,
@ -338,7 +338,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
server->credits, server->in_flight,
new_val - credits->value,
cifs_trace_rw_credits_old_session);
trace_smb3_reconnect_detected(server->CurrentMid,
trace_smb3_reconnect_detected(server->current_mid,
server->conn_id, server->hostname, scredits,
credits->value - new_val, in_flight);
cifs_server_dbg(VFS, "R=%x[%x] trying to return %d credits to old session\n",
@ -358,7 +358,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
trace_smb3_adj_credits(server->CurrentMid,
trace_smb3_adj_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
credits->value - new_val, in_flight);
cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n",
@ -374,19 +374,19 @@ smb2_get_next_mid(struct TCP_Server_Info *server)
{
__u64 mid;
/* for SMB2 we need the current value */
spin_lock(&server->mid_lock);
mid = server->CurrentMid++;
spin_unlock(&server->mid_lock);
spin_lock(&server->mid_counter_lock);
mid = server->current_mid++;
spin_unlock(&server->mid_counter_lock);
return mid;
}
static void
smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)
{
spin_lock(&server->mid_lock);
if (server->CurrentMid >= val)
server->CurrentMid -= val;
spin_unlock(&server->mid_lock);
spin_lock(&server->mid_counter_lock);
if (server->current_mid >= val)
server->current_mid -= val;
spin_unlock(&server->mid_counter_lock);
}
static struct mid_q_entry *
@ -401,7 +401,7 @@ __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
return NULL;
}
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if ((mid->mid == wire_mid) &&
(mid->mid_state == MID_REQUEST_SUBMITTED) &&
@ -409,13 +409,13 @@ __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
kref_get(&mid->refcount);
if (dequeue) {
list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
mid->deleted_from_q = true;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return mid;
}
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return NULL;
}
@ -460,9 +460,9 @@ smb2_negotiate(const unsigned int xid,
{
int rc;
spin_lock(&server->mid_lock);
server->CurrentMid = 0;
spin_unlock(&server->mid_lock);
spin_lock(&server->mid_counter_lock);
server->current_mid = 0;
spin_unlock(&server->mid_counter_lock);
rc = SMB2_negotiate(xid, ses, server);
return rc;
}
@ -2498,7 +2498,7 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
trace_smb3_pend_credits(server->CurrentMid,
trace_smb3_pend_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
le16_to_cpu(shdr->CreditRequest), in_flight);
cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n",
@ -4809,18 +4809,18 @@ static void smb2_decrypt_offload(struct work_struct *work)
} else {
spin_lock(&dw->server->srv_lock);
if (dw->server->tcpStatus == CifsNeedReconnect) {
spin_lock(&dw->server->mid_lock);
spin_lock(&dw->server->mid_queue_lock);
mid->mid_state = MID_RETRY_NEEDED;
spin_unlock(&dw->server->mid_lock);
spin_unlock(&dw->server->mid_queue_lock);
spin_unlock(&dw->server->srv_lock);
mid->callback(mid);
} else {
spin_lock(&dw->server->mid_lock);
spin_lock(&dw->server->mid_queue_lock);
mid->mid_state = MID_REQUEST_SUBMITTED;
mid->mid_flags &= ~(MID_DELETED);
mid->deleted_from_q = false;
list_add_tail(&mid->qhead,
&dw->server->pending_mid_q);
spin_unlock(&dw->server->mid_lock);
spin_unlock(&dw->server->mid_queue_lock);
spin_unlock(&dw->server->srv_lock);
}
}
@ -5260,10 +5260,9 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
rc = cifs_sfu_make_node(xid, inode, dentry, tcon,
full_path, mode, dev);
} else if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_REPARSE_POINTS)
|| (tcon->posix_extensions)) {
} else if (CIFS_REPARSE_SUPPORT(tcon)) {
rc = mknod_reparse(xid, inode, dentry, tcon,
full_path, mode, dev);
full_path, mode, dev);
}
return rc;
}

View file

@ -840,9 +840,9 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
*mid = smb2_mid_entry_alloc(shdr, server);
if (*mid == NULL)
return -ENOMEM;
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_add_tail(&(*mid)->qhead, &server->pending_mid_q);
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return 0;
}

View file

@ -13,27 +13,23 @@
#include "cifsproto.h"
#include "smb2proto.h"
static struct smbd_response *get_empty_queue_buffer(
struct smbd_connection *info);
static struct smbd_response *get_receive_buffer(
static struct smbdirect_recv_io *get_receive_buffer(
struct smbd_connection *info);
static void put_receive_buffer(
struct smbd_connection *info,
struct smbd_response *response);
struct smbdirect_recv_io *response);
static int allocate_receive_buffers(struct smbd_connection *info, int num_buf);
static void destroy_receive_buffers(struct smbd_connection *info);
static void put_empty_packet(
struct smbd_connection *info, struct smbd_response *response);
static void enqueue_reassembly(
struct smbd_connection *info,
struct smbd_response *response, int data_length);
static struct smbd_response *_get_first_reassembly(
struct smbdirect_recv_io *response, int data_length);
static struct smbdirect_recv_io *_get_first_reassembly(
struct smbd_connection *info);
static int smbd_post_recv(
struct smbd_connection *info,
struct smbd_response *response);
struct smbdirect_recv_io *response);
static int smbd_post_send_empty(struct smbd_connection *info);
@ -182,9 +178,10 @@ static int smbd_conn_upcall(
{
struct smbd_connection *info = id->context;
struct smbdirect_socket *sc = &info->socket;
const char *event_name = rdma_event_msg(event->event);
log_rdma_event(INFO, "event=%d status=%d\n",
event->event, event->status);
log_rdma_event(INFO, "event=%s status=%d\n",
event_name, event->status);
switch (event->event) {
case RDMA_CM_EVENT_ADDR_RESOLVED:
@ -194,45 +191,50 @@ static int smbd_conn_upcall(
break;
case RDMA_CM_EVENT_ADDR_ERROR:
log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
info->ri_rc = -EHOSTUNREACH;
complete(&info->ri_done);
break;
case RDMA_CM_EVENT_ROUTE_ERROR:
log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
info->ri_rc = -ENETUNREACH;
complete(&info->ri_done);
break;
case RDMA_CM_EVENT_ESTABLISHED:
log_rdma_event(INFO, "connected event=%d\n", event->event);
log_rdma_event(INFO, "connected event=%s\n", event_name);
sc->status = SMBDIRECT_SOCKET_CONNECTED;
wake_up_interruptible(&info->conn_wait);
wake_up_interruptible(&info->status_wait);
break;
case RDMA_CM_EVENT_CONNECT_ERROR:
case RDMA_CM_EVENT_UNREACHABLE:
case RDMA_CM_EVENT_REJECTED:
log_rdma_event(INFO, "connecting failed event=%d\n", event->event);
log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
wake_up_interruptible(&info->conn_wait);
wake_up_interruptible(&info->status_wait);
break;
case RDMA_CM_EVENT_DEVICE_REMOVAL:
case RDMA_CM_EVENT_DISCONNECTED:
/* This happens when we fail the negotiation */
if (sc->status == SMBDIRECT_SOCKET_NEGOTIATE_FAILED) {
log_rdma_event(ERR, "event=%s during negotiation\n", event_name);
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
wake_up(&info->conn_wait);
wake_up(&info->status_wait);
break;
}
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
wake_up_interruptible(&info->disconn_wait);
wake_up_interruptible(&info->wait_reassembly_queue);
wake_up_interruptible(&info->status_wait);
wake_up_interruptible(&sc->recv_io.reassembly.wait_queue);
wake_up_interruptible_all(&info->wait_send_queue);
break;
default:
log_rdma_event(ERR, "unexpected event=%s status=%d\n",
event_name, event->status);
break;
}
@ -259,12 +261,12 @@ smbd_qp_async_error_upcall(struct ib_event *event, void *context)
}
}
static inline void *smbd_request_payload(struct smbd_request *request)
static inline void *smbdirect_send_io_payload(struct smbdirect_send_io *request)
{
return (void *)request->packet;
}
static inline void *smbd_response_payload(struct smbd_response *response)
static inline void *smbdirect_recv_io_payload(struct smbdirect_recv_io *response)
{
return (void *)response->packet;
}
@ -273,32 +275,35 @@ static inline void *smbd_response_payload(struct smbd_response *response)
static void send_done(struct ib_cq *cq, struct ib_wc *wc)
{
int i;
struct smbd_request *request =
container_of(wc->wr_cqe, struct smbd_request, cqe);
struct smbd_connection *info = request->info;
struct smbdirect_socket *sc = &info->socket;
struct smbdirect_send_io *request =
container_of(wc->wr_cqe, struct smbdirect_send_io, cqe);
struct smbdirect_socket *sc = request->socket;
struct smbd_connection *info =
container_of(sc, struct smbd_connection, socket);
log_rdma_send(INFO, "smbd_request 0x%p completed wc->status=%d\n",
log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%d\n",
request, wc->status);
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
log_rdma_send(ERR, "wc->status=%d wc->opcode=%d\n",
wc->status, wc->opcode);
smbd_disconnect_rdma_connection(request->info);
}
for (i = 0; i < request->num_sge; i++)
ib_dma_unmap_single(sc->ib.dev,
request->sge[i].addr,
request->sge[i].length,
DMA_TO_DEVICE);
if (atomic_dec_and_test(&request->info->send_pending))
wake_up(&request->info->wait_send_pending);
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
log_rdma_send(ERR, "wc->status=%d wc->opcode=%d\n",
wc->status, wc->opcode);
mempool_free(request, sc->send_io.mem.pool);
smbd_disconnect_rdma_connection(info);
return;
}
wake_up(&request->info->wait_post_send);
if (atomic_dec_and_test(&info->send_pending))
wake_up(&info->wait_send_pending);
mempool_free(request, request->info->request_mempool);
wake_up(&info->wait_post_send);
mempool_free(request, sc->send_io.mem.pool);
}
static void dump_smbdirect_negotiate_resp(struct smbdirect_negotiate_resp *resp)
@ -317,12 +322,13 @@ static void dump_smbdirect_negotiate_resp(struct smbdirect_negotiate_resp *resp)
* return value: true if negotiation is a success, false if failed
*/
static bool process_negotiation_response(
struct smbd_response *response, int packet_length)
struct smbdirect_recv_io *response, int packet_length)
{
struct smbd_connection *info = response->info;
struct smbdirect_socket *sc = &info->socket;
struct smbdirect_socket *sc = response->socket;
struct smbd_connection *info =
container_of(sc, struct smbd_connection, socket);
struct smbdirect_socket_parameters *sp = &sc->parameters;
struct smbdirect_negotiate_resp *packet = smbd_response_payload(response);
struct smbdirect_negotiate_resp *packet = smbdirect_recv_io_payload(response);
if (packet_length < sizeof(struct smbdirect_negotiate_resp)) {
log_rdma_event(ERR,
@ -385,15 +391,15 @@ static bool process_negotiation_response(
info->max_frmr_depth * PAGE_SIZE);
info->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE;
sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
return true;
}
static void smbd_post_send_credits(struct work_struct *work)
{
int ret = 0;
int use_receive_queue = 1;
int rc;
struct smbd_response *response;
struct smbdirect_recv_io *response;
struct smbd_connection *info =
container_of(work, struct smbd_connection,
post_send_credits_work);
@ -407,20 +413,10 @@ static void smbd_post_send_credits(struct work_struct *work)
if (info->receive_credit_target >
atomic_read(&info->receive_credits)) {
while (true) {
if (use_receive_queue)
response = get_receive_buffer(info);
else
response = get_empty_queue_buffer(info);
if (!response) {
/* now switch to empty packet queue */
if (use_receive_queue) {
use_receive_queue = 0;
continue;
} else
break;
}
response = get_receive_buffer(info);
if (!response)
break;
response->type = SMBD_TRANSFER_DATA;
response->first_segment = false;
rc = smbd_post_recv(info, response);
if (rc) {
@ -454,19 +450,20 @@ static void smbd_post_send_credits(struct work_struct *work)
static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
{
struct smbdirect_data_transfer *data_transfer;
struct smbd_response *response =
container_of(wc->wr_cqe, struct smbd_response, cqe);
struct smbd_connection *info = response->info;
struct smbdirect_recv_io *response =
container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
struct smbdirect_socket *sc = response->socket;
struct smbd_connection *info =
container_of(sc, struct smbd_connection, socket);
int data_length = 0;
log_rdma_recv(INFO, "response=0x%p type=%d wc status=%d wc opcode %d byte_len=%d pkey_index=%u\n",
response, response->type, wc->status, wc->opcode,
response, sc->recv_io.expected, wc->status, wc->opcode,
wc->byte_len, wc->pkey_index);
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) {
log_rdma_recv(INFO, "wc->status=%d opcode=%d\n",
wc->status, wc->opcode);
smbd_disconnect_rdma_connection(info);
goto error;
}
@ -476,43 +473,31 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
response->sge.length,
DMA_FROM_DEVICE);
switch (response->type) {
switch (sc->recv_io.expected) {
/* SMBD negotiation response */
case SMBD_NEGOTIATE_RESP:
dump_smbdirect_negotiate_resp(smbd_response_payload(response));
info->full_packet_received = true;
case SMBDIRECT_EXPECT_NEGOTIATE_REP:
dump_smbdirect_negotiate_resp(smbdirect_recv_io_payload(response));
sc->recv_io.reassembly.full_packet_received = true;
info->negotiate_done =
process_negotiation_response(response, wc->byte_len);
put_receive_buffer(info, response);
complete(&info->negotiate_completion);
break;
return;
/* SMBD data transfer packet */
case SMBD_TRANSFER_DATA:
data_transfer = smbd_response_payload(response);
case SMBDIRECT_EXPECT_DATA_TRANSFER:
data_transfer = smbdirect_recv_io_payload(response);
data_length = le32_to_cpu(data_transfer->data_length);
/*
* If this is a packet with data playload place the data in
* reassembly queue and wake up the reading thread
*/
if (data_length) {
if (info->full_packet_received)
if (sc->recv_io.reassembly.full_packet_received)
response->first_segment = true;
if (le32_to_cpu(data_transfer->remaining_data_length))
info->full_packet_received = false;
sc->recv_io.reassembly.full_packet_received = false;
else
info->full_packet_received = true;
enqueue_reassembly(
info,
response,
data_length);
} else
put_empty_packet(info, response);
if (data_length)
wake_up_interruptible(&info->wait_reassembly_queue);
sc->recv_io.reassembly.full_packet_received = true;
}
atomic_dec(&info->receive_credits);
info->receive_credit_target =
@ -540,15 +525,31 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
info->keep_alive_requested = KEEP_ALIVE_PENDING;
}
/*
* If this is a packet with data playload place the data in
* reassembly queue and wake up the reading thread
*/
if (data_length) {
enqueue_reassembly(info, response, data_length);
wake_up_interruptible(&sc->recv_io.reassembly.wait_queue);
} else
put_receive_buffer(info, response);
return;
default:
log_rdma_recv(ERR,
"unexpected response type=%d\n", response->type);
case SMBDIRECT_EXPECT_NEGOTIATE_REQ:
/* Only server... */
break;
}
/*
* This is an internal error!
*/
log_rdma_recv(ERR, "unexpected response type=%d\n", sc->recv_io.expected);
WARN_ON_ONCE(sc->recv_io.expected != SMBDIRECT_EXPECT_DATA_TRANSFER);
error:
put_receive_buffer(info, response);
smbd_disconnect_rdma_connection(info);
}
static struct rdma_cm_id *smbd_create_id(
@ -694,16 +695,16 @@ static int smbd_post_send_negotiate_req(struct smbd_connection *info)
struct smbdirect_socket_parameters *sp = &sc->parameters;
struct ib_send_wr send_wr;
int rc = -ENOMEM;
struct smbd_request *request;
struct smbdirect_send_io *request;
struct smbdirect_negotiate_req *packet;
request = mempool_alloc(info->request_mempool, GFP_KERNEL);
request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
if (!request)
return rc;
request->info = info;
request->socket = sc;
packet = smbd_request_payload(request);
packet = smbdirect_send_io_payload(request);
packet->min_version = cpu_to_le16(SMBDIRECT_V1);
packet->max_version = cpu_to_le16(SMBDIRECT_V1);
packet->reserved = 0;
@ -756,7 +757,7 @@ static int smbd_post_send_negotiate_req(struct smbd_connection *info)
smbd_disconnect_rdma_connection(info);
dma_mapping_failed:
mempool_free(request, info->request_mempool);
mempool_free(request, sc->send_io.mem.pool);
return rc;
}
@ -800,7 +801,7 @@ static int manage_keep_alive_before_sending(struct smbd_connection *info)
/* Post the send request */
static int smbd_post_send(struct smbd_connection *info,
struct smbd_request *request)
struct smbdirect_send_io *request)
{
struct smbdirect_socket *sc = &info->socket;
struct smbdirect_socket_parameters *sp = &sc->parameters;
@ -849,7 +850,7 @@ static int smbd_post_send_iter(struct smbd_connection *info,
int i, rc;
int header_length;
int data_length;
struct smbd_request *request;
struct smbdirect_send_io *request;
struct smbdirect_data_transfer *packet;
int new_credits = 0;
@ -888,20 +889,20 @@ wait_send_queue:
goto wait_send_queue;
}
request = mempool_alloc(info->request_mempool, GFP_KERNEL);
request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
if (!request) {
rc = -ENOMEM;
goto err_alloc;
}
request->info = info;
request->socket = sc;
memset(request->sge, 0, sizeof(request->sge));
/* Fill in the data payload to find out how much data we can add */
if (iter) {
struct smb_extract_to_rdma extract = {
.nr_sge = 1,
.max_sge = SMBDIRECT_MAX_SEND_SGE,
.max_sge = SMBDIRECT_SEND_IO_MAX_SGE,
.sge = request->sge,
.device = sc->ib.dev,
.local_dma_lkey = sc->ib.pd->local_dma_lkey,
@ -923,7 +924,7 @@ wait_send_queue:
}
/* Fill in the packet header */
packet = smbd_request_payload(request);
packet = smbdirect_send_io_payload(request);
packet->credits_requested = cpu_to_le16(sp->send_credit_target);
new_credits = manage_credits_prior_sending(info);
@ -982,7 +983,7 @@ err_dma:
request->sge[i].addr,
request->sge[i].length,
DMA_TO_DEVICE);
mempool_free(request, info->request_mempool);
mempool_free(request, sc->send_io.mem.pool);
/* roll back receive credits and credits to be offered */
spin_lock(&info->lock_new_credits_offered);
@ -1042,7 +1043,7 @@ static int smbd_post_send_full_iter(struct smbd_connection *info,
* The interaction is controlled by send/receive credit system
*/
static int smbd_post_recv(
struct smbd_connection *info, struct smbd_response *response)
struct smbd_connection *info, struct smbdirect_recv_io *response)
{
struct smbdirect_socket *sc = &info->socket;
struct smbdirect_socket_parameters *sp = &sc->parameters;
@ -1069,6 +1070,7 @@ static int smbd_post_recv(
if (rc) {
ib_dma_unmap_single(sc->ib.dev, response->sge.addr,
response->sge.length, DMA_FROM_DEVICE);
response->sge.length = 0;
smbd_disconnect_rdma_connection(info);
log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc);
}
@ -1079,10 +1081,11 @@ static int smbd_post_recv(
/* Perform SMBD negotiate according to [MS-SMBD] 3.1.5.2 */
static int smbd_negotiate(struct smbd_connection *info)
{
struct smbdirect_socket *sc = &info->socket;
int rc;
struct smbd_response *response = get_receive_buffer(info);
struct smbdirect_recv_io *response = get_receive_buffer(info);
response->type = SMBD_NEGOTIATE_RESP;
sc->recv_io.expected = SMBDIRECT_EXPECT_NEGOTIATE_REP;
rc = smbd_post_recv(info, response);
log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=0x%llx iov.length=%u iov.lkey=0x%x\n",
rc, response->sge.addr,
@ -1113,17 +1116,6 @@ static int smbd_negotiate(struct smbd_connection *info)
return rc;
}
static void put_empty_packet(
struct smbd_connection *info, struct smbd_response *response)
{
spin_lock(&info->empty_packet_queue_lock);
list_add_tail(&response->list, &info->empty_packet_queue);
info->count_empty_packet_queue++;
spin_unlock(&info->empty_packet_queue_lock);
queue_work(info->workqueue, &info->post_send_credits_work);
}
/*
* Implement Connection.FragmentReassemblyBuffer defined in [MS-SMBD] 3.1.1.1
* This is a queue for reassembling upper layer payload and present to upper
@ -1136,12 +1128,14 @@ static void put_empty_packet(
*/
static void enqueue_reassembly(
struct smbd_connection *info,
struct smbd_response *response,
struct smbdirect_recv_io *response,
int data_length)
{
spin_lock(&info->reassembly_queue_lock);
list_add_tail(&response->list, &info->reassembly_queue);
info->reassembly_queue_length++;
struct smbdirect_socket *sc = &info->socket;
spin_lock(&sc->recv_io.reassembly.lock);
list_add_tail(&response->list, &sc->recv_io.reassembly.list);
sc->recv_io.reassembly.queue_length++;
/*
* Make sure reassembly_data_length is updated after list and
* reassembly_queue_length are updated. On the dequeue side
@ -1149,8 +1143,8 @@ static void enqueue_reassembly(
* if reassembly_queue_length and list is up to date
*/
virt_wmb();
info->reassembly_data_length += data_length;
spin_unlock(&info->reassembly_queue_lock);
sc->recv_io.reassembly.data_length += data_length;
spin_unlock(&sc->recv_io.reassembly.lock);
info->count_reassembly_queue++;
info->count_enqueue_reassembly_queue++;
}
@ -1160,58 +1154,41 @@ static void enqueue_reassembly(
* Caller is responsible for locking
* return value: the first entry if any, NULL if queue is empty
*/
static struct smbd_response *_get_first_reassembly(struct smbd_connection *info)
static struct smbdirect_recv_io *_get_first_reassembly(struct smbd_connection *info)
{
struct smbd_response *ret = NULL;
struct smbdirect_socket *sc = &info->socket;
struct smbdirect_recv_io *ret = NULL;
if (!list_empty(&info->reassembly_queue)) {
if (!list_empty(&sc->recv_io.reassembly.list)) {
ret = list_first_entry(
&info->reassembly_queue,
struct smbd_response, list);
&sc->recv_io.reassembly.list,
struct smbdirect_recv_io, list);
}
return ret;
}
static struct smbd_response *get_empty_queue_buffer(
struct smbd_connection *info)
{
struct smbd_response *ret = NULL;
unsigned long flags;
spin_lock_irqsave(&info->empty_packet_queue_lock, flags);
if (!list_empty(&info->empty_packet_queue)) {
ret = list_first_entry(
&info->empty_packet_queue,
struct smbd_response, list);
list_del(&ret->list);
info->count_empty_packet_queue--;
}
spin_unlock_irqrestore(&info->empty_packet_queue_lock, flags);
return ret;
}
/*
* Get a receive buffer
* For each remote send, we need to post a receive. The receive buffers are
* pre-allocated in advance.
* return value: the receive buffer, NULL if none is available
*/
static struct smbd_response *get_receive_buffer(struct smbd_connection *info)
static struct smbdirect_recv_io *get_receive_buffer(struct smbd_connection *info)
{
struct smbd_response *ret = NULL;
struct smbdirect_socket *sc = &info->socket;
struct smbdirect_recv_io *ret = NULL;
unsigned long flags;
spin_lock_irqsave(&info->receive_queue_lock, flags);
if (!list_empty(&info->receive_queue)) {
spin_lock_irqsave(&sc->recv_io.free.lock, flags);
if (!list_empty(&sc->recv_io.free.list)) {
ret = list_first_entry(
&info->receive_queue,
struct smbd_response, list);
&sc->recv_io.free.list,
struct smbdirect_recv_io, list);
list_del(&ret->list);
info->count_receive_queue--;
info->count_get_receive_buffer++;
}
spin_unlock_irqrestore(&info->receive_queue_lock, flags);
spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
return ret;
}
@ -1223,19 +1200,24 @@ static struct smbd_response *get_receive_buffer(struct smbd_connection *info)
* receive buffer is returned.
*/
static void put_receive_buffer(
struct smbd_connection *info, struct smbd_response *response)
struct smbd_connection *info, struct smbdirect_recv_io *response)
{
struct smbdirect_socket *sc = &info->socket;
unsigned long flags;
ib_dma_unmap_single(sc->ib.dev, response->sge.addr,
response->sge.length, DMA_FROM_DEVICE);
if (likely(response->sge.length != 0)) {
ib_dma_unmap_single(sc->ib.dev,
response->sge.addr,
response->sge.length,
DMA_FROM_DEVICE);
response->sge.length = 0;
}
spin_lock_irqsave(&info->receive_queue_lock, flags);
list_add_tail(&response->list, &info->receive_queue);
spin_lock_irqsave(&sc->recv_io.free.lock, flags);
list_add_tail(&response->list, &sc->recv_io.free.list);
info->count_receive_queue++;
info->count_put_receive_buffer++;
spin_unlock_irqrestore(&info->receive_queue_lock, flags);
spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
queue_work(info->workqueue, &info->post_send_credits_work);
}
@ -1243,58 +1225,54 @@ static void put_receive_buffer(
/* Preallocate all receive buffer on transport establishment */
static int allocate_receive_buffers(struct smbd_connection *info, int num_buf)
{
struct smbdirect_socket *sc = &info->socket;
struct smbdirect_recv_io *response;
int i;
struct smbd_response *response;
INIT_LIST_HEAD(&info->reassembly_queue);
spin_lock_init(&info->reassembly_queue_lock);
info->reassembly_data_length = 0;
info->reassembly_queue_length = 0;
INIT_LIST_HEAD(&sc->recv_io.reassembly.list);
spin_lock_init(&sc->recv_io.reassembly.lock);
sc->recv_io.reassembly.data_length = 0;
sc->recv_io.reassembly.queue_length = 0;
INIT_LIST_HEAD(&info->receive_queue);
spin_lock_init(&info->receive_queue_lock);
INIT_LIST_HEAD(&sc->recv_io.free.list);
spin_lock_init(&sc->recv_io.free.lock);
info->count_receive_queue = 0;
INIT_LIST_HEAD(&info->empty_packet_queue);
spin_lock_init(&info->empty_packet_queue_lock);
info->count_empty_packet_queue = 0;
init_waitqueue_head(&info->wait_receive_queues);
for (i = 0; i < num_buf; i++) {
response = mempool_alloc(info->response_mempool, GFP_KERNEL);
response = mempool_alloc(sc->recv_io.mem.pool, GFP_KERNEL);
if (!response)
goto allocate_failed;
response->info = info;
list_add_tail(&response->list, &info->receive_queue);
response->socket = sc;
response->sge.length = 0;
list_add_tail(&response->list, &sc->recv_io.free.list);
info->count_receive_queue++;
}
return 0;
allocate_failed:
while (!list_empty(&info->receive_queue)) {
while (!list_empty(&sc->recv_io.free.list)) {
response = list_first_entry(
&info->receive_queue,
struct smbd_response, list);
&sc->recv_io.free.list,
struct smbdirect_recv_io, list);
list_del(&response->list);
info->count_receive_queue--;
mempool_free(response, info->response_mempool);
mempool_free(response, sc->recv_io.mem.pool);
}
return -ENOMEM;
}
static void destroy_receive_buffers(struct smbd_connection *info)
{
struct smbd_response *response;
struct smbdirect_socket *sc = &info->socket;
struct smbdirect_recv_io *response;
while ((response = get_receive_buffer(info)))
mempool_free(response, info->response_mempool);
while ((response = get_empty_queue_buffer(info)))
mempool_free(response, info->response_mempool);
mempool_free(response, sc->recv_io.mem.pool);
}
/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */
@ -1332,7 +1310,7 @@ void smbd_destroy(struct TCP_Server_Info *server)
struct smbd_connection *info = server->smbd_conn;
struct smbdirect_socket *sc;
struct smbdirect_socket_parameters *sp;
struct smbd_response *response;
struct smbdirect_recv_io *response;
unsigned long flags;
if (!info) {
@ -1347,7 +1325,7 @@ void smbd_destroy(struct TCP_Server_Info *server)
rdma_disconnect(sc->rdma.cm_id);
log_rdma_event(INFO, "wait for transport being disconnected\n");
wait_event_interruptible(
info->disconn_wait,
info->status_wait,
sc->status == SMBDIRECT_SOCKET_DISCONNECTED);
}
@ -1366,23 +1344,22 @@ void smbd_destroy(struct TCP_Server_Info *server)
/* It's not possible for upper layer to get to reassembly */
log_rdma_event(INFO, "drain the reassembly queue\n");
do {
spin_lock_irqsave(&info->reassembly_queue_lock, flags);
spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
response = _get_first_reassembly(info);
if (response) {
list_del(&response->list);
spin_unlock_irqrestore(
&info->reassembly_queue_lock, flags);
&sc->recv_io.reassembly.lock, flags);
put_receive_buffer(info, response);
} else
spin_unlock_irqrestore(
&info->reassembly_queue_lock, flags);
&sc->recv_io.reassembly.lock, flags);
} while (response);
info->reassembly_data_length = 0;
sc->recv_io.reassembly.data_length = 0;
log_rdma_event(INFO, "free receive buffers\n");
wait_event(info->wait_receive_queues,
info->count_receive_queue + info->count_empty_packet_queue
== sp->recv_credit_max);
info->count_receive_queue == sp->recv_credit_max);
destroy_receive_buffers(info);
/*
@ -1407,11 +1384,11 @@ void smbd_destroy(struct TCP_Server_Info *server)
rdma_destroy_id(sc->rdma.cm_id);
/* free mempools */
mempool_destroy(info->request_mempool);
kmem_cache_destroy(info->request_cache);
mempool_destroy(sc->send_io.mem.pool);
kmem_cache_destroy(sc->send_io.mem.cache);
mempool_destroy(info->response_mempool);
kmem_cache_destroy(info->response_cache);
mempool_destroy(sc->recv_io.mem.pool);
kmem_cache_destroy(sc->recv_io.mem.cache);
sc->status = SMBDIRECT_SOCKET_DESTROYED;
@ -1459,12 +1436,14 @@ create_conn:
static void destroy_caches_and_workqueue(struct smbd_connection *info)
{
struct smbdirect_socket *sc = &info->socket;
destroy_receive_buffers(info);
destroy_workqueue(info->workqueue);
mempool_destroy(info->response_mempool);
kmem_cache_destroy(info->response_cache);
mempool_destroy(info->request_mempool);
kmem_cache_destroy(info->request_cache);
mempool_destroy(sc->recv_io.mem.pool);
kmem_cache_destroy(sc->recv_io.mem.cache);
mempool_destroy(sc->send_io.mem.pool);
kmem_cache_destroy(sc->send_io.mem.cache);
}
#define MAX_NAME_LEN 80
@ -1478,41 +1457,41 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
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 =
scnprintf(name, MAX_NAME_LEN, "smbdirect_send_io_%p", info);
sc->send_io.mem.cache =
kmem_cache_create(
name,
sizeof(struct smbd_request) +
sizeof(struct smbdirect_send_io) +
sizeof(struct smbdirect_data_transfer),
0, SLAB_HWCACHE_ALIGN, NULL);
if (!info->request_cache)
if (!sc->send_io.mem.cache)
return -ENOMEM;
info->request_mempool =
sc->send_io.mem.pool =
mempool_create(sp->send_credit_target, mempool_alloc_slab,
mempool_free_slab, info->request_cache);
if (!info->request_mempool)
mempool_free_slab, sc->send_io.mem.cache);
if (!sc->send_io.mem.pool)
goto out1;
scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info);
scnprintf(name, MAX_NAME_LEN, "smbdirect_recv_io_%p", info);
struct kmem_cache_args response_args = {
.align = __alignof__(struct smbd_response),
.useroffset = (offsetof(struct smbd_response, packet) +
.align = __alignof__(struct smbdirect_recv_io),
.useroffset = (offsetof(struct smbdirect_recv_io, packet) +
sizeof(struct smbdirect_data_transfer)),
.usersize = sp->max_recv_size - sizeof(struct smbdirect_data_transfer),
};
info->response_cache =
sc->recv_io.mem.cache =
kmem_cache_create(name,
sizeof(struct smbd_response) + sp->max_recv_size,
sizeof(struct smbdirect_recv_io) + sp->max_recv_size,
&response_args, SLAB_HWCACHE_ALIGN);
if (!info->response_cache)
if (!sc->recv_io.mem.cache)
goto out2;
info->response_mempool =
sc->recv_io.mem.pool =
mempool_create(sp->recv_credit_max, mempool_alloc_slab,
mempool_free_slab, info->response_cache);
if (!info->response_mempool)
mempool_free_slab, sc->recv_io.mem.cache);
if (!sc->recv_io.mem.pool)
goto out3;
scnprintf(name, MAX_NAME_LEN, "smbd_%p", info);
@ -1531,13 +1510,13 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
out5:
destroy_workqueue(info->workqueue);
out4:
mempool_destroy(info->response_mempool);
mempool_destroy(sc->recv_io.mem.pool);
out3:
kmem_cache_destroy(info->response_cache);
kmem_cache_destroy(sc->recv_io.mem.cache);
out2:
mempool_destroy(info->request_mempool);
mempool_destroy(sc->send_io.mem.pool);
out1:
kmem_cache_destroy(info->request_cache);
kmem_cache_destroy(sc->send_io.mem.cache);
return -ENOMEM;
}
@ -1593,8 +1572,8 @@ static struct smbd_connection *_smbd_get_connection(
sp->max_recv_size = smbd_max_receive_size;
sp->keepalive_interval_msec = smbd_keep_alive_interval * 1000;
if (sc->ib.dev->attrs.max_send_sge < SMBDIRECT_MAX_SEND_SGE ||
sc->ib.dev->attrs.max_recv_sge < SMBDIRECT_MAX_RECV_SGE) {
if (sc->ib.dev->attrs.max_send_sge < SMBDIRECT_SEND_IO_MAX_SGE ||
sc->ib.dev->attrs.max_recv_sge < SMBDIRECT_RECV_IO_MAX_SGE) {
log_rdma_event(ERR,
"device %.*s max_send_sge/max_recv_sge = %d/%d too small\n",
IB_DEVICE_NAME_MAX,
@ -1625,8 +1604,8 @@ static struct smbd_connection *_smbd_get_connection(
qp_attr.qp_context = info;
qp_attr.cap.max_send_wr = sp->send_credit_target;
qp_attr.cap.max_recv_wr = sp->recv_credit_max;
qp_attr.cap.max_send_sge = SMBDIRECT_MAX_SEND_SGE;
qp_attr.cap.max_recv_sge = SMBDIRECT_MAX_RECV_SGE;
qp_attr.cap.max_send_sge = SMBDIRECT_SEND_IO_MAX_SGE;
qp_attr.cap.max_recv_sge = SMBDIRECT_RECV_IO_MAX_SGE;
qp_attr.cap.max_inline_data = 0;
qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
qp_attr.qp_type = IB_QPT_RC;
@ -1671,17 +1650,18 @@ static struct smbd_connection *_smbd_get_connection(
log_rdma_event(INFO, "connecting to IP %pI4 port %d\n",
&addr_in->sin_addr, port);
init_waitqueue_head(&info->conn_wait);
init_waitqueue_head(&info->disconn_wait);
init_waitqueue_head(&info->wait_reassembly_queue);
init_waitqueue_head(&info->status_wait);
init_waitqueue_head(&sc->recv_io.reassembly.wait_queue);
rc = rdma_connect(sc->rdma.cm_id, &conn_param);
if (rc) {
log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc);
goto rdma_connect_failed;
}
wait_event_interruptible(
info->conn_wait, sc->status != SMBDIRECT_SOCKET_CONNECTING);
wait_event_interruptible_timeout(
info->status_wait,
sc->status != SMBDIRECT_SOCKET_CONNECTING,
msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT));
if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
log_rdma_event(ERR, "rdma_connect failed port=%d\n", port);
@ -1735,9 +1715,8 @@ negotiation_failed:
cancel_delayed_work_sync(&info->idle_timer_work);
destroy_caches_and_workqueue(info);
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
init_waitqueue_head(&info->conn_wait);
rdma_disconnect(sc->rdma.cm_id);
wait_event(info->conn_wait,
wait_event(info->status_wait,
sc->status == SMBDIRECT_SOCKET_DISCONNECTED);
allocate_cache_failed:
@ -1794,7 +1773,7 @@ try_again:
int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
{
struct smbdirect_socket *sc = &info->socket;
struct smbd_response *response;
struct smbdirect_recv_io *response;
struct smbdirect_data_transfer *data_transfer;
size_t size = iov_iter_count(&msg->msg_iter);
int to_copy, to_read, data_read, offset;
@ -1810,9 +1789,9 @@ again:
* 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=%zd info->reassembly_data_length=%d\n", size,
info->reassembly_data_length);
if (info->reassembly_data_length >= size) {
log_read(INFO, "size=%zd sc->recv_io.reassembly.data_length=%d\n", size,
sc->recv_io.reassembly.data_length);
if (sc->recv_io.reassembly.data_length >= size) {
int queue_length;
int queue_removed = 0;
@ -1824,13 +1803,13 @@ again:
* updated in SOFTIRQ as more data is received
*/
virt_rmb();
queue_length = info->reassembly_queue_length;
queue_length = sc->recv_io.reassembly.queue_length;
data_read = 0;
to_read = size;
offset = info->first_entry_offset;
offset = sc->recv_io.reassembly.first_entry_offset;
while (data_read < size) {
response = _get_first_reassembly(info);
data_transfer = smbd_response_payload(response);
data_transfer = smbdirect_recv_io_payload(response);
data_length = le32_to_cpu(data_transfer->data_length);
remaining_data_length =
le32_to_cpu(
@ -1875,10 +1854,10 @@ again:
list_del(&response->list);
else {
spin_lock_irq(
&info->reassembly_queue_lock);
&sc->recv_io.reassembly.lock);
list_del(&response->list);
spin_unlock_irq(
&info->reassembly_queue_lock);
&sc->recv_io.reassembly.lock);
}
queue_removed++;
info->count_reassembly_queue--;
@ -1897,23 +1876,23 @@ again:
to_read, data_read, offset);
}
spin_lock_irq(&info->reassembly_queue_lock);
info->reassembly_data_length -= data_read;
info->reassembly_queue_length -= queue_removed;
spin_unlock_irq(&info->reassembly_queue_lock);
spin_lock_irq(&sc->recv_io.reassembly.lock);
sc->recv_io.reassembly.data_length -= data_read;
sc->recv_io.reassembly.queue_length -= queue_removed;
spin_unlock_irq(&sc->recv_io.reassembly.lock);
info->first_entry_offset = offset;
sc->recv_io.reassembly.first_entry_offset = offset;
log_read(INFO, "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n",
data_read, info->reassembly_data_length,
info->first_entry_offset);
data_read, sc->recv_io.reassembly.data_length,
sc->recv_io.reassembly.first_entry_offset);
read_rfc1002_done:
return data_read;
}
log_read(INFO, "wait_event on more data\n");
rc = wait_event_interruptible(
info->wait_reassembly_queue,
info->reassembly_data_length >= size ||
sc->recv_io.reassembly.wait_queue,
sc->recv_io.reassembly.data_length >= size ||
sc->status != SMBDIRECT_SOCKET_CONNECTED);
/* Don't return any data if interrupted */
if (rc)

View file

@ -33,16 +33,6 @@ enum keep_alive_status {
KEEP_ALIVE_SENT,
};
enum smbd_connection_status {
SMBD_CREATED,
SMBD_CONNECTING,
SMBD_CONNECTED,
SMBD_NEGOTIATE_FAILED,
SMBD_DISCONNECTING,
SMBD_DISCONNECTED,
SMBD_DESTROYED
};
/*
* The context for the SMBDirect transport
* Everything related to the transport is here. It has several logical parts
@ -57,8 +47,7 @@ struct smbd_connection {
int ri_rc;
struct completion ri_done;
wait_queue_head_t conn_wait;
wait_queue_head_t disconn_wait;
wait_queue_head_t status_wait;
struct completion negotiate_completion;
bool negotiate_done;
@ -75,7 +64,6 @@ struct smbd_connection {
atomic_t send_credits;
atomic_t receive_credits;
int receive_credit_target;
int fragment_reassembly_remaining;
/* Memory registrations */
/* Maximum number of RDMA read/write outstanding on this connection */
@ -106,52 +94,16 @@ struct smbd_connection {
wait_queue_head_t wait_post_send;
/* Receive queue */
struct list_head receive_queue;
int count_receive_queue;
spinlock_t receive_queue_lock;
struct list_head empty_packet_queue;
int count_empty_packet_queue;
spinlock_t empty_packet_queue_lock;
wait_queue_head_t wait_receive_queues;
/* Reassembly queue */
struct list_head reassembly_queue;
spinlock_t reassembly_queue_lock;
wait_queue_head_t wait_reassembly_queue;
/* total data length of reassembly queue */
int reassembly_data_length;
int reassembly_queue_length;
/* the offset to first buffer in reassembly queue */
int first_entry_offset;
bool send_immediate;
wait_queue_head_t wait_send_queue;
/*
* Indicate if we have received a full packet on the connection
* This is used to identify the first SMBD packet of a assembled
* payload (SMB packet) in reassembly queue so we can return a
* RFC1002 length to upper layer to indicate the length of the SMB
* packet received
*/
bool full_packet_received;
struct workqueue_struct *workqueue;
struct delayed_work idle_timer_work;
/* Memory pool for preallocating buffers */
/* request pool for RDMA send */
struct kmem_cache *request_cache;
mempool_t *request_mempool;
/* response pool for RDMA receive */
struct kmem_cache *response_cache;
mempool_t *response_mempool;
/* for debug purposes */
unsigned int count_get_receive_buffer;
unsigned int count_put_receive_buffer;
@ -161,48 +113,6 @@ struct smbd_connection {
unsigned int count_send_empty;
};
enum smbd_message_type {
SMBD_NEGOTIATE_RESP,
SMBD_TRANSFER_DATA,
};
/* Maximum number of SGEs used by smbdirect.c in any send work request */
#define SMBDIRECT_MAX_SEND_SGE 6
/* The context for a SMBD request */
struct smbd_request {
struct smbd_connection *info;
struct ib_cqe cqe;
/* the SGE entries for this work request */
struct ib_sge sge[SMBDIRECT_MAX_SEND_SGE];
int num_sge;
/* SMBD packet header follows this structure */
u8 packet[];
};
/* Maximum number of SGEs used by smbdirect.c in any receive work request */
#define SMBDIRECT_MAX_RECV_SGE 1
/* The context for a SMBD response */
struct smbd_response {
struct smbd_connection *info;
struct ib_cqe cqe;
struct ib_sge sge;
enum smbd_message_type type;
/* Link to receive queue or reassembly queue */
struct list_head list;
/* Indicate if this is the 1st packet of a payload */
bool first_segment;
/* SMBD packet header and payload follows this structure */
u8 packet[];
};
/* Create a SMBDirect session */
struct smbd_connection *smbd_get_connection(
struct TCP_Server_Info *server, struct sockaddr *dstaddr);

View file

@ -30,9 +30,6 @@
#include "smbdirect.h"
#include "compress.h"
/* Max number of iovectors we can use off the stack when sending requests. */
#define CIFS_MAX_IOV_SIZE 8
void
cifs_wake_up_task(struct mid_q_entry *mid)
{
@ -41,42 +38,6 @@ cifs_wake_up_task(struct mid_q_entry *mid)
wake_up_process(mid->callback_data);
}
static struct mid_q_entry *
alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
{
struct mid_q_entry *temp;
if (server == NULL) {
cifs_dbg(VFS, "%s: null TCP session\n", __func__);
return NULL;
}
temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
memset(temp, 0, sizeof(struct mid_q_entry));
kref_init(&temp->refcount);
temp->mid = get_mid(smb_buffer);
temp->pid = current->pid;
temp->command = cpu_to_le16(smb_buffer->Command);
cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
/* easier to use jiffies */
/* when mid allocated can be before when sent */
temp->when_alloc = jiffies;
temp->server = server;
/*
* The default is for the mid to be synchronous, so the
* default callback just wakes up the current task.
*/
get_task_struct(current);
temp->creator = current;
temp->callback = cifs_wake_up_task;
temp->callback_data = current;
atomic_inc(&mid_count);
temp->mid_state = MID_REQUEST_ALLOCATED;
return temp;
}
void __release_mid(struct kref *refcount)
{
struct mid_q_entry *midEntry =
@ -89,7 +50,7 @@ void __release_mid(struct kref *refcount)
#endif
struct TCP_Server_Info *server = midEntry->server;
if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) &&
if (midEntry->resp_buf && (midEntry->wait_cancelled) &&
(midEntry->mid_state == MID_RESPONSE_RECEIVED ||
midEntry->mid_state == MID_RESPONSE_READY) &&
server->ops->handle_cancelled_mid)
@ -160,12 +121,12 @@ void __release_mid(struct kref *refcount)
void
delete_mid(struct mid_q_entry *mid)
{
spin_lock(&mid->server->mid_lock);
if (!(mid->mid_flags & MID_DELETED)) {
spin_lock(&mid->server->mid_queue_lock);
if (mid->deleted_from_q == false) {
list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
mid->deleted_from_q = true;
}
spin_unlock(&mid->server->mid_lock);
spin_unlock(&mid->server->mid_queue_lock);
release_mid(mid);
}
@ -269,9 +230,8 @@ smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst)
return buflen;
}
static int
__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
struct smb_rqst *rqst)
int __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
struct smb_rqst *rqst)
{
int rc;
struct kvec *iov;
@ -397,7 +357,7 @@ unmask:
* socket so the server throws away the partial SMB
*/
cifs_signal_cifsd_for_reconnect(server, false);
trace_smb3_partial_send_reconnect(server->CurrentMid,
trace_smb3_partial_send_reconnect(server->current_mid,
server->conn_id, server->hostname);
}
smbd_done:
@ -456,22 +416,6 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
return rc;
}
int
smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
unsigned int smb_buf_length)
{
struct kvec iov[2];
struct smb_rqst rqst = { .rq_iov = iov,
.rq_nvec = 2 };
iov[0].iov_base = smb_buffer;
iov[0].iov_len = 4;
iov[1].iov_base = (char *)smb_buffer + 4;
iov[1].iov_len = smb_buf_length;
return __smb_send_rqst(server, 1, &rqst);
}
static int
wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
const int timeout, const int flags,
@ -509,7 +453,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_nblk_credits(server->CurrentMid,
trace_smb3_nblk_credits(server->current_mid,
server->conn_id, server->hostname, scredits, -1, in_flight);
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
__func__, 1, scredits);
@ -542,7 +486,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_credit_timeout(server->CurrentMid,
trace_smb3_credit_timeout(server->current_mid,
server->conn_id, server->hostname, scredits,
num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
@ -585,7 +529,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
spin_unlock(&server->req_lock);
trace_smb3_credit_timeout(
server->CurrentMid,
server->current_mid,
server->conn_id, server->hostname,
scredits, num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
@ -615,7 +559,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_waitff_credits(server->CurrentMid,
trace_smb3_waitff_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
-(num_credits), in_flight);
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
@ -626,9 +570,8 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
return 0;
}
static int
wait_for_free_request(struct TCP_Server_Info *server, const int flags,
unsigned int *instance)
int wait_for_free_request(struct TCP_Server_Info *server, const int flags,
unsigned int *instance)
{
return wait_for_free_credits(server, 1, -1, flags,
instance);
@ -666,7 +609,7 @@ wait_for_compound_request(struct TCP_Server_Info *server, int num,
*/
if (server->in_flight == 0) {
spin_unlock(&server->req_lock);
trace_smb3_insufficient_credits(server->CurrentMid,
trace_smb3_insufficient_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
num, in_flight);
cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n",
@ -690,40 +633,7 @@ cifs_wait_mtu_credits(struct TCP_Server_Info *server, size_t size,
return 0;
}
static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
struct mid_q_entry **ppmidQ)
{
spin_lock(&ses->ses_lock);
if (ses->ses_status == SES_NEW) {
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
(in_buf->Command != SMB_COM_NEGOTIATE)) {
spin_unlock(&ses->ses_lock);
return -EAGAIN;
}
/* else ok - we are setting up session */
}
if (ses->ses_status == SES_EXITING) {
/* check if SMB session is bad because we are setting it up */
if (in_buf->Command != SMB_COM_LOGOFF_ANDX) {
spin_unlock(&ses->ses_lock);
return -EAGAIN;
}
/* else ok - we are shutting down session */
}
spin_unlock(&ses->ses_lock);
*ppmidQ = alloc_mid(in_buf, ses->server);
if (*ppmidQ == NULL)
return -ENOMEM;
spin_lock(&ses->server->mid_lock);
list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
spin_unlock(&ses->server->mid_lock);
return 0;
}
static int
wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
int wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
{
int error;
@ -737,34 +647,6 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
return 0;
}
struct mid_q_entry *
cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
{
int rc;
struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
if (rqst->rq_iov[0].iov_len != 4 ||
rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
return ERR_PTR(-EIO);
/* enable signing if server requires it */
if (server->sign)
hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
mid = alloc_mid(hdr, server);
if (mid == NULL)
return ERR_PTR(-ENOMEM);
rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
if (rc) {
release_mid(mid);
return ERR_PTR(rc);
}
return mid;
}
/*
* Send a SMB request and set the callback function in the mid to handle
* the result. Caller is responsible for dealing with timeouts.
@ -819,9 +701,9 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
mid->mid_state = MID_REQUEST_SUBMITTED;
/* put it on the pending_mid_q */
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_add_tail(&mid->qhead, &server->pending_mid_q);
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
/*
* Need to store the time in mid before calling I/O. For call_async,
@ -845,45 +727,17 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
return rc;
}
/*
*
* Send an SMB Request. No response info (other than return code)
* needs to be parsed.
*
* flags indicate the type of request buffer and how long to wait
* and whether to log NT STATUS code (error) before mapping it to POSIX error
*
*/
int
SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
char *in_buf, int flags)
{
int rc;
struct kvec iov[1];
struct kvec rsp_iov;
int resp_buf_type;
iov[0].iov_base = in_buf;
iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
flags |= CIFS_NO_RSP_BUF;
rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
return rc;
}
static int
cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
int cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
{
int rc = 0;
cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n",
__func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state);
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
switch (mid->mid_state) {
case MID_RESPONSE_READY:
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return rc;
case MID_RETRY_NEEDED:
rc = -EAGAIN;
@ -898,85 +752,23 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
rc = mid->mid_rc;
break;
default:
if (!(mid->mid_flags & MID_DELETED)) {
if (mid->deleted_from_q == false) {
list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
mid->deleted_from_q = true;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n",
__func__, mid->mid, mid->mid_state);
rc = -EIO;
goto sync_mid_done;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
sync_mid_done:
release_mid(mid);
return rc;
}
static inline int
send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
struct mid_q_entry *mid)
{
return server->ops->send_cancel ?
server->ops->send_cancel(server, rqst, mid) : 0;
}
int
cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
bool log_error)
{
unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
dump_smb(mid->resp_buf, min_t(u32, 92, len));
/* convert the length into a more usable form */
if (server->sign) {
struct kvec iov[2];
int rc = 0;
struct smb_rqst rqst = { .rq_iov = iov,
.rq_nvec = 2 };
iov[0].iov_base = mid->resp_buf;
iov[0].iov_len = 4;
iov[1].iov_base = (char *)mid->resp_buf + 4;
iov[1].iov_len = len - 4;
/* FIXME: add code to kill session */
rc = cifs_verify_signature(&rqst, server,
mid->sequence_number);
if (rc)
cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
rc);
}
/* BB special case reconnect tid and uid here? */
return map_and_check_smb_error(mid, log_error);
}
struct mid_q_entry *
cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
struct smb_rqst *rqst)
{
int rc;
struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
if (rqst->rq_iov[0].iov_len != 4 ||
rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
return ERR_PTR(-EIO);
rc = allocate_mid(ses, hdr, &mid);
if (rc)
return ERR_PTR(rc);
rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
if (rc) {
delete_mid(mid);
return ERR_PTR(rc);
}
return mid;
}
static void
cifs_compound_callback(struct mid_q_entry *mid)
{
@ -1213,15 +1005,15 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n",
midQ[i]->mid, le16_to_cpu(midQ[i]->command));
send_cancel(server, &rqst[i], midQ[i]);
spin_lock(&server->mid_lock);
midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
spin_lock(&server->mid_queue_lock);
midQ[i]->wait_cancelled = true;
if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED ||
midQ[i]->mid_state == MID_RESPONSE_RECEIVED) {
midQ[i]->callback = cifs_cancelled_callback;
cancelled_mid[i] = true;
credits[i].value = 0;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
}
}
@ -1304,344 +1096,6 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
rqst, resp_buf_type, resp_iov);
}
int
SendReceive2(const unsigned int xid, struct cifs_ses *ses,
struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
const int flags, struct kvec *resp_iov)
{
struct smb_rqst rqst;
struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov;
int rc;
if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec),
GFP_KERNEL);
if (!new_iov) {
/* otherwise cifs_send_recv below sets resp_buf_type */
*resp_buf_type = CIFS_NO_BUFFER;
return -ENOMEM;
}
} else
new_iov = s_iov;
/* 1st iov is a RFC1001 length followed by the rest of the packet */
memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
new_iov[0].iov_base = new_iov[1].iov_base;
new_iov[0].iov_len = 4;
new_iov[1].iov_base += 4;
new_iov[1].iov_len -= 4;
memset(&rqst, 0, sizeof(struct smb_rqst));
rqst.rq_iov = new_iov;
rqst.rq_nvec = n_vec + 1;
rc = cifs_send_recv(xid, ses, ses->server,
&rqst, resp_buf_type, flags, resp_iov);
if (n_vec + 1 > CIFS_MAX_IOV_SIZE)
kfree(new_iov);
return rc;
}
int
SendReceive(const unsigned int xid, struct cifs_ses *ses,
struct smb_hdr *in_buf, struct smb_hdr *out_buf,
int *pbytes_returned, const int flags)
{
int rc = 0;
struct mid_q_entry *midQ;
unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
struct cifs_credits credits = { .value = 1, .instance = 0 };
struct TCP_Server_Info *server;
if (ses == NULL) {
cifs_dbg(VFS, "Null smb session\n");
return -EIO;
}
server = ses->server;
if (server == NULL) {
cifs_dbg(VFS, "Null tcp session\n");
return -EIO;
}
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&server->srv_lock);
return -ENOENT;
}
spin_unlock(&server->srv_lock);
/* Ensure that we do not send more than 50 overlapping requests
to the same server. We may make this configurable later or
use ses->maxReq */
if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
len);
return -EIO;
}
rc = wait_for_free_request(server, flags, &credits.instance);
if (rc)
return rc;
/* make sure that we sign in the same order that we send on this socket
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
cifs_server_lock(server);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
cifs_server_unlock(server);
/* Update # of requests on wire to server */
add_credits(server, &credits, 0);
return rc;
}
rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
if (rc) {
cifs_server_unlock(server);
goto out;
}
midQ->mid_state = MID_REQUEST_SUBMITTED;
rc = smb_send(server, in_buf, len);
cifs_save_when_sent(midQ);
if (rc < 0)
server->sequence_number -= 2;
cifs_server_unlock(server);
if (rc < 0)
goto out;
rc = wait_for_response(server, midQ);
if (rc != 0) {
send_cancel(server, &rqst, midQ);
spin_lock(&server->mid_lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED) {
/* no longer considered to be "in-flight" */
midQ->callback = release_mid;
spin_unlock(&server->mid_lock);
add_credits(server, &credits, 0);
return rc;
}
spin_unlock(&server->mid_lock);
}
rc = cifs_sync_mid_result(midQ, server);
if (rc != 0) {
add_credits(server, &credits, 0);
return rc;
}
if (!midQ->resp_buf || !out_buf ||
midQ->mid_state != MID_RESPONSE_READY) {
rc = -EIO;
cifs_server_dbg(VFS, "Bad MID state?\n");
goto out;
}
*pbytes_returned = get_rfc1002_length(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, server, 0);
out:
delete_mid(midQ);
add_credits(server, &credits, 0);
return rc;
}
/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
blocking lock to return. */
static int
send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
struct smb_hdr *in_buf,
struct smb_hdr *out_buf)
{
int bytes_returned;
struct cifs_ses *ses = tcon->ses;
LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
/* We just modify the current in_buf to change
the type of lock from LOCKING_ANDX_SHARED_LOCK
or LOCKING_ANDX_EXCLUSIVE_LOCK to
LOCKING_ANDX_CANCEL_LOCK. */
pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
pSMB->Timeout = 0;
pSMB->hdr.Mid = get_next_mid(ses->server);
return SendReceive(xid, ses, in_buf, out_buf,
&bytes_returned, 0);
}
int
SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
struct smb_hdr *in_buf, struct smb_hdr *out_buf,
int *pbytes_returned)
{
int rc = 0;
int rstart = 0;
struct mid_q_entry *midQ;
struct cifs_ses *ses;
unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
unsigned int instance;
struct TCP_Server_Info *server;
if (tcon == NULL || tcon->ses == NULL) {
cifs_dbg(VFS, "Null smb session\n");
return -EIO;
}
ses = tcon->ses;
server = ses->server;
if (server == NULL) {
cifs_dbg(VFS, "Null tcp session\n");
return -EIO;
}
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&server->srv_lock);
return -ENOENT;
}
spin_unlock(&server->srv_lock);
/* Ensure that we do not send more than 50 overlapping requests
to the same server. We may make this configurable later or
use ses->maxReq */
if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
len);
return -EIO;
}
rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
if (rc)
return rc;
/* make sure that we sign in the same order that we send on this socket
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
cifs_server_lock(server);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
cifs_server_unlock(server);
return rc;
}
rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
if (rc) {
delete_mid(midQ);
cifs_server_unlock(server);
return rc;
}
midQ->mid_state = MID_REQUEST_SUBMITTED;
rc = smb_send(server, in_buf, len);
cifs_save_when_sent(midQ);
if (rc < 0)
server->sequence_number -= 2;
cifs_server_unlock(server);
if (rc < 0) {
delete_mid(midQ);
return rc;
}
/* Wait for a reply - allow signals to interrupt. */
rc = wait_event_interruptible(server->response_q,
(!(midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED)) ||
((server->tcpStatus != CifsGood) &&
(server->tcpStatus != CifsNew)));
/* Were we interrupted by a signal ? */
spin_lock(&server->srv_lock);
if ((rc == -ERESTARTSYS) &&
(midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED) &&
((server->tcpStatus == CifsGood) ||
(server->tcpStatus == CifsNew))) {
spin_unlock(&server->srv_lock);
if (in_buf->Command == SMB_COM_TRANSACTION2) {
/* POSIX lock. We send a NT_CANCEL SMB to cause the
blocking lock to return. */
rc = send_cancel(server, &rqst, midQ);
if (rc) {
delete_mid(midQ);
return rc;
}
} else {
/* Windows lock. We send a LOCKINGX_CANCEL_LOCK
to cause the blocking lock to return. */
rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
/* If we get -ENOLCK back the lock may have
already been removed. Don't exit in this case. */
if (rc && rc != -ENOLCK) {
delete_mid(midQ);
return rc;
}
}
rc = wait_for_response(server, midQ);
if (rc) {
send_cancel(server, &rqst, midQ);
spin_lock(&server->mid_lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED) {
/* no longer considered to be "in-flight" */
midQ->callback = release_mid;
spin_unlock(&server->mid_lock);
return rc;
}
spin_unlock(&server->mid_lock);
}
/* We got the response - restart system call. */
rstart = 1;
spin_lock(&server->srv_lock);
}
spin_unlock(&server->srv_lock);
rc = cifs_sync_mid_result(midQ, server);
if (rc != 0)
return rc;
/* rcvd frame is ok */
if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_READY) {
rc = -EIO;
cifs_tcon_dbg(VFS, "Bad MID state?\n");
goto out;
}
*pbytes_returned = get_rfc1002_length(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, server, 0);
out:
delete_mid(midQ);
if (rstart && rc == -EACCES)
return -ERESTARTSYS;
return rc;
}
/*
* Discard any remaining data in the current SMB. To do this, we borrow the

View file

@ -38,6 +38,124 @@ struct smbdirect_socket {
} ib;
struct smbdirect_socket_parameters parameters;
/*
* The state for posted send buffers
*/
struct {
/*
* Memory pools for preallocating
* smbdirect_send_io buffers
*/
struct {
struct kmem_cache *cache;
mempool_t *pool;
} mem;
} send_io;
/*
* The state for posted receive buffers
*/
struct {
/*
* The type of PDU we are expecting
*/
enum {
SMBDIRECT_EXPECT_NEGOTIATE_REQ = 1,
SMBDIRECT_EXPECT_NEGOTIATE_REP = 2,
SMBDIRECT_EXPECT_DATA_TRANSFER = 3,
} expected;
/*
* Memory pools for preallocating
* smbdirect_recv_io buffers
*/
struct {
struct kmem_cache *cache;
mempool_t *pool;
} mem;
/*
* The list of free smbdirect_recv_io
* structures
*/
struct {
struct list_head list;
spinlock_t lock;
} free;
/*
* The list of arrived non-empty smbdirect_recv_io
* structures
*
* This represents the reassembly queue.
*/
struct {
struct list_head list;
spinlock_t lock;
wait_queue_head_t wait_queue;
/* total data length of reassembly queue */
int data_length;
int queue_length;
/* the offset to first buffer in reassembly queue */
int first_entry_offset;
/*
* Indicate if we have received a full packet on the
* connection This is used to identify the first SMBD
* packet of a assembled payload (SMB packet) in
* reassembly queue so we can return a RFC1002 length to
* upper layer to indicate the length of the SMB packet
* received
*/
bool full_packet_received;
} reassembly;
} recv_io;
};
struct smbdirect_send_io {
struct smbdirect_socket *socket;
struct ib_cqe cqe;
/*
* The SGE entries for this work request
*
* The first points to the packet header
*/
#define SMBDIRECT_SEND_IO_MAX_SGE 6
size_t num_sge;
struct ib_sge sge[SMBDIRECT_SEND_IO_MAX_SGE];
/*
* Link to the list of sibling smbdirect_send_io
* messages.
*/
struct list_head sibling_list;
struct ib_send_wr wr;
/* SMBD packet header follows this structure */
u8 packet[];
};
struct smbdirect_recv_io {
struct smbdirect_socket *socket;
struct ib_cqe cqe;
/*
* For now we only use a single SGE
* as we have just one large buffer
* per posted recv.
*/
#define SMBDIRECT_RECV_IO_MAX_SGE 1
struct ib_sge sge;
/* Link to free or reassembly list */
struct list_head list;
/* Indicate if this is the 1st packet of a payload */
bool first_segment;
/* SMBD packet header and payload follows this structure */
u8 packet[];
};
#endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_SOCKET_H__ */