32 ksmbd and smbdirect fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmmMlx0ACgkQiiy9cAdy
 T1H39Qv+IYc4hUuM6eOMftlzGrVkSaSzjQe5bsXX/1UYyiwbvOyUqYJVFIm+HxAd
 3EvtlBGfpZ5Xm1Un3CS8pWZMl2qbDRr2l1T9gPpe4qH7ILWKMzJBmEzOm5nro+fy
 Qu+URZ01F0JVbAj/zTU5Wuqq/BQmiHNRPycNnmXofP8rWaPTSMB92dnThC8++K3Q
 v5J0jxzrfsBtAZ1Tt9lisX5BfGkYEyYBKoD0xB7h8o9heXrsRu/rJbSWSsQoYXiD
 8LhICT6On3nbq/5H7tkuv5AieBOdwaIumwpweK5JTJv2AA51+BU4qy4pyeDFVndc
 kxdyUxJUloVn5xKvCNoOrQWtJJyW8sULebWjudJuvuktJcGiDzZV/bnXI2CfU0MF
 R2ZdBjUmJhxr+xCXGIxdjnhAavnHh81yF4mAA6r9TnLgRtdkMFcc7+9FfwjxQYf4
 wHbXCZWpEz4CVFGiy2GABQ0HdVrt5vVuGW7rBO6pUlUjUeR/wvQieYNBhTjwlN0X
 Dgr4qlhN
 =VTb9
 -----END PGP SIGNATURE-----

Merge tag 'v7.0-rc-part1-ksmbd-and-smbdirect-fixes' of git://git.samba.org/ksmbd

Pull smb server and smbdirect updates from Steve French:

 - Fix tcp connection leak

 - Fix potential use after free when freeing multichannel

 - Fix locking problem in showing channel list

 - Locking improvement for tree connection

 - Fix infinite loop when signing errors

 - Add /proc interface for monitoring server state

 - Fixes to avoid mixing iWarp and InfiniBand/RoCEv1/RoCEv2
   port ranges used for smbdirect

 - Fixes for smbdirect credit handling problems, these make
   the connections more reliable

* tag 'v7.0-rc-part1-ksmbd-and-smbdirect-fixes' of git://git.samba.org/ksmbd: (32 commits)
  ksmbd: fix non-IPv6 build
  ksmbd: convert tree_conns_lock to rw_semaphore
  ksmbd: fix missing chann_lock while iterating session channel list
  ksmbd: add chann_lock to protect ksmbd_chann_list xarray
  smb: server: correct value for smb_direct_max_fragmented_recv_size
  smb: client: correct value for smbd_max_fragmented_recv_size
  smb: server: fix leak of active_num_conn in ksmbd_tcp_new_connection()
  ksmbd: add procfs interface for runtime monitoring and statistics
  ksmbd: fix infinite loop caused by next_smb2_rcv_hdr_off reset in error paths
  smb: server: make use of rdma_restrict_node_type()
  smb: client: make use of rdma_restrict_node_type()
  RDMA/core: introduce rdma_restrict_node_type()
  smb: client: let send_done handle a completion without IB_SEND_SIGNALED
  smb: client: let smbd_post_send_negotiate_req() use smbd_post_send()
  smb: client: fix last send credit problem causing disconnects
  smb: client: make use of smbdirect_socket.send_io.bcredits
  smb: client: use smbdirect_send_batch processing
  smb: client: introduce and use smbd_{alloc, free}_send_io()
  smb: client: split out smbd_ib_post_send()
  smb: client: port and use the wait_for_credits logic used by server
  ...
This commit is contained in:
Linus Torvalds 2026-02-12 08:31:12 -08:00
commit d53f4d93f3
25 changed files with 1485 additions and 203 deletions

View file

@ -793,6 +793,9 @@ static int cma_acquire_dev_by_src_ip(struct rdma_id_private *id_priv)
mutex_lock(&lock);
list_for_each_entry(cma_dev, &dev_list, list) {
if (id_priv->restricted_node_type != RDMA_NODE_UNSPECIFIED &&
id_priv->restricted_node_type != cma_dev->device->node_type)
continue;
rdma_for_each_port (cma_dev->device, port) {
gidp = rdma_protocol_roce(cma_dev->device, port) ?
&iboe_gid : &gid;
@ -1015,6 +1018,7 @@ __rdma_create_id(struct net *net, rdma_cm_event_handler event_handler,
return ERR_PTR(-ENOMEM);
id_priv->state = RDMA_CM_IDLE;
id_priv->restricted_node_type = RDMA_NODE_UNSPECIFIED;
id_priv->id.context = context;
id_priv->id.event_handler = event_handler;
id_priv->id.ps = ps;
@ -4177,6 +4181,32 @@ err:
}
EXPORT_SYMBOL(rdma_resolve_addr);
int rdma_restrict_node_type(struct rdma_cm_id *id, u8 node_type)
{
struct rdma_id_private *id_priv =
container_of(id, struct rdma_id_private, id);
int ret = 0;
switch (node_type) {
case RDMA_NODE_UNSPECIFIED:
case RDMA_NODE_IB_CA:
case RDMA_NODE_RNIC:
break;
default:
return -EINVAL;
}
mutex_lock(&lock);
if (id_priv->cma_dev)
ret = -EALREADY;
else
id_priv->restricted_node_type = node_type;
mutex_unlock(&lock);
return ret;
}
EXPORT_SYMBOL(rdma_restrict_node_type);
int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
{
struct rdma_id_private *id_priv =

View file

@ -72,6 +72,7 @@ struct rdma_id_private {
int internal_id;
enum rdma_cm_state state;
u8 restricted_node_type;
spinlock_t lock;
struct mutex qp_mutex;

View file

@ -35,6 +35,10 @@ static void enqueue_reassembly(
static struct smbdirect_recv_io *_get_first_reassembly(
struct smbdirect_socket *sc);
static int smbd_post_send(struct smbdirect_socket *sc,
struct smbdirect_send_batch *batch,
struct smbdirect_send_io *request);
static int smbd_post_recv(
struct smbdirect_socket *sc,
struct smbdirect_recv_io *response);
@ -97,8 +101,23 @@ int smbd_send_credit_target = 255;
/* The maximum single message size can be sent to remote peer */
int smbd_max_send_size = 1364;
/* The maximum fragmented upper-layer payload receive size supported */
int smbd_max_fragmented_recv_size = 1024 * 1024;
/*
* The maximum fragmented upper-layer payload receive size supported
*
* Assume max_payload_per_credit is
* smbd_max_receive_size - 24 = 1340
*
* The maximum number would be
* smbd_receive_credit_max * max_payload_per_credit
*
* 1340 * 255 = 341700 (0x536C4)
*
* The minimum value from the spec is 131072 (0x20000)
*
* For now we use the logic we used in ksmbd before:
* (1364 * 255) / 2 = 173910 (0x2A756)
*/
int smbd_max_fragmented_recv_size = (1364 * 255) / 2;
/* The maximum single-message size which can be received */
int smbd_max_receive_size = 1364;
@ -493,27 +512,103 @@ static inline void *smbdirect_recv_io_payload(struct smbdirect_recv_io *response
return (void *)response->packet;
}
static struct smbdirect_send_io *smbd_alloc_send_io(struct smbdirect_socket *sc)
{
struct smbdirect_send_io *msg;
msg = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
if (!msg)
return ERR_PTR(-ENOMEM);
msg->socket = sc;
INIT_LIST_HEAD(&msg->sibling_list);
msg->num_sge = 0;
return msg;
}
static void smbd_free_send_io(struct smbdirect_send_io *msg)
{
struct smbdirect_socket *sc = msg->socket;
size_t i;
/*
* The list needs to be empty!
* The caller should take care of it.
*/
WARN_ON_ONCE(!list_empty(&msg->sibling_list));
/*
* Note we call ib_dma_unmap_page(), even if some sges are mapped using
* ib_dma_map_single().
*
* The difference between _single() and _page() only matters for the
* ib_dma_map_*() case.
*
* For the ib_dma_unmap_*() case it does not matter as both take the
* dma_addr_t and dma_unmap_single_attrs() is just an alias to
* dma_unmap_page_attrs().
*/
for (i = 0; i < msg->num_sge; i++)
ib_dma_unmap_page(sc->ib.dev,
msg->sge[i].addr,
msg->sge[i].length,
DMA_TO_DEVICE);
mempool_free(msg, sc->send_io.mem.pool);
}
/* Called when a RDMA send is done */
static void send_done(struct ib_cq *cq, struct ib_wc *wc)
{
int i;
struct smbdirect_send_io *request =
container_of(wc->wr_cqe, struct smbdirect_send_io, cqe);
struct smbdirect_socket *sc = request->socket;
struct smbdirect_send_io *sibling, *next;
int lcredits = 0;
log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n",
request, ib_wc_status_msg(wc->status));
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);
mempool_free(request, sc->send_io.mem.pool);
if (unlikely(!(request->wr.send_flags & IB_SEND_SIGNALED))) {
/*
* This happens when smbdirect_send_io is a sibling
* before the final message, it is signaled on
* error anyway, so we need to skip
* smbdirect_connection_free_send_io here,
* otherwise is will destroy the memory
* of the siblings too, which will cause
* use after free problems for the others
* triggered from ib_drain_qp().
*/
if (wc->status != IB_WC_SUCCESS)
goto skip_free;
/*
* This should not happen!
* But we better just close the
* connection...
*/
log_rdma_send(ERR,
"unexpected send completion wc->status=%s (%d) wc->opcode=%d\n",
ib_wc_status_msg(wc->status), wc->status, wc->opcode);
smbd_disconnect_rdma_connection(sc);
return;
}
/*
* Free possible siblings and then the main send_io
*/
list_for_each_entry_safe(sibling, next, &request->sibling_list, sibling_list) {
list_del_init(&sibling->sibling_list);
smbd_free_send_io(sibling);
lcredits += 1;
}
/* Note this frees wc->wr_cqe, but not wc */
smbd_free_send_io(request);
lcredits += 1;
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
skip_free:
if (wc->status != IB_WC_WR_FLUSH_ERR)
log_rdma_send(ERR, "wc->status=%s wc->opcode=%d\n",
ib_wc_status_msg(wc->status), wc->opcode);
@ -608,6 +703,7 @@ static bool process_negotiation_response(
sp->max_frmr_depth * PAGE_SIZE);
sp->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE;
atomic_set(&sc->send_io.bcredits.count, 1);
sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
return true;
}
@ -618,6 +714,7 @@ static void smbd_post_send_credits(struct work_struct *work)
struct smbdirect_recv_io *response;
struct smbdirect_socket *sc =
container_of(work, struct smbdirect_socket, recv_io.posted.refill_work);
int posted = 0;
if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
return;
@ -640,9 +737,21 @@ static void smbd_post_send_credits(struct work_struct *work)
}
atomic_inc(&sc->recv_io.posted.count);
posted += 1;
}
}
atomic_add(posted, &sc->recv_io.credits.available);
/*
* If the last send credit is waiting for credits
* it can grant we need to wake it up
*/
if (posted &&
atomic_read(&sc->send_io.bcredits.count) == 0 &&
atomic_read(&sc->send_io.credits.count) == 0)
wake_up(&sc->send_io.credits.wait_queue);
/* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */
if (atomic_read(&sc->recv_io.credits.count) <
sc->recv_io.credits.target - 1) {
@ -659,6 +768,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
struct smbdirect_socket *sc = response->socket;
struct smbdirect_socket_parameters *sp = &sc->parameters;
int current_recv_credits;
u16 old_recv_credit_target;
u32 data_offset = 0;
u32 data_length = 0;
@ -743,7 +853,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
}
atomic_dec(&sc->recv_io.posted.count);
atomic_dec(&sc->recv_io.credits.count);
current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count);
old_recv_credit_target = sc->recv_io.credits.target;
sc->recv_io.credits.target =
le16_to_cpu(data_transfer->credits_requested);
@ -779,7 +890,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
* reassembly queue and wake up the reading thread
*/
if (data_length) {
if (sc->recv_io.credits.target > old_recv_credit_target)
if (current_recv_credits <= (sc->recv_io.credits.target / 4) ||
sc->recv_io.credits.target > old_recv_credit_target)
queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
enqueue_reassembly(sc, response, data_length);
@ -810,6 +922,7 @@ static struct rdma_cm_id *smbd_create_id(
{
struct smbdirect_socket_parameters *sp = &sc->parameters;
struct rdma_cm_id *id;
u8 node_type = RDMA_NODE_UNSPECIFIED;
int rc;
__be16 *sport;
@ -821,6 +934,31 @@ static struct rdma_cm_id *smbd_create_id(
return id;
}
switch (port) {
case SMBD_PORT:
/*
* only allow iWarp devices
* for port 5445.
*/
node_type = RDMA_NODE_RNIC;
break;
case SMB_PORT:
/*
* only allow InfiniBand, RoCEv1 or RoCEv2
* devices for port 445.
*
* (Basically don't allow iWarp devices)
*/
node_type = RDMA_NODE_IB_CA;
break;
}
rc = rdma_restrict_node_type(id, node_type);
if (rc) {
log_rdma_event(ERR, "rdma_restrict_node_type(%u) failed %i\n",
node_type, rc);
goto out;
}
if (dstaddr->sa_family == AF_INET6)
sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port;
else
@ -955,16 +1093,13 @@ out1:
static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
{
struct smbdirect_socket_parameters *sp = &sc->parameters;
struct ib_send_wr send_wr;
int rc = -ENOMEM;
int rc;
struct smbdirect_send_io *request;
struct smbdirect_negotiate_req *packet;
request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
if (!request)
return rc;
request->socket = sc;
request = smbd_alloc_send_io(sc);
if (IS_ERR(request))
return PTR_ERR(request);
packet = smbdirect_send_io_payload(request);
packet->min_version = cpu_to_le16(SMBDIRECT_V1);
@ -976,7 +1111,6 @@ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
packet->max_fragmented_size =
cpu_to_le32(sp->max_fragmented_recv_size);
request->num_sge = 1;
request->sge[0].addr = ib_dma_map_single(
sc->ib.dev, (void *)packet,
sizeof(*packet), DMA_TO_DEVICE);
@ -984,42 +1118,20 @@ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
rc = -EIO;
goto dma_mapping_failed;
}
request->num_sge = 1;
request->sge[0].length = sizeof(*packet);
request->sge[0].lkey = sc->ib.pd->local_dma_lkey;
ib_dma_sync_single_for_device(
sc->ib.dev, request->sge[0].addr,
request->sge[0].length, DMA_TO_DEVICE);
request->cqe.done = send_done;
send_wr.next = NULL;
send_wr.wr_cqe = &request->cqe;
send_wr.sg_list = request->sge;
send_wr.num_sge = request->num_sge;
send_wr.opcode = IB_WR_SEND;
send_wr.send_flags = IB_SEND_SIGNALED;
log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n",
request->sge[0].addr,
request->sge[0].length, request->sge[0].lkey);
atomic_inc(&sc->send_io.pending.count);
rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
rc = smbd_post_send(sc, NULL, request);
if (!rc)
return 0;
/* if we reach here, post send failed */
log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
atomic_dec(&sc->send_io.pending.count);
ib_dma_unmap_single(sc->ib.dev, request->sge[0].addr,
request->sge[0].length, DMA_TO_DEVICE);
smbd_disconnect_rdma_connection(sc);
if (rc == -EAGAIN)
rc = -EIO;
dma_mapping_failed:
mempool_free(request, sc->send_io.mem.pool);
smbd_free_send_io(request);
return rc;
}
@ -1033,19 +1145,38 @@ dma_mapping_failed:
*/
static int manage_credits_prior_sending(struct smbdirect_socket *sc)
{
int missing;
int available;
int new_credits;
if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target)
return 0;
new_credits = atomic_read(&sc->recv_io.posted.count);
if (new_credits == 0)
missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count);
available = atomic_xchg(&sc->recv_io.credits.available, 0);
new_credits = (u16)min3(U16_MAX, missing, available);
if (new_credits <= 0) {
/*
* If credits are available, but not granted
* we need to re-add them again.
*/
if (available)
atomic_add(available, &sc->recv_io.credits.available);
return 0;
}
new_credits -= atomic_read(&sc->recv_io.credits.count);
if (new_credits <= 0)
return 0;
if (new_credits < available) {
/*
* Readd the remaining available again.
*/
available -= new_credits;
atomic_add(available, &sc->recv_io.credits.available);
}
/*
* Remember we granted the credits
*/
atomic_add(new_credits, &sc->recv_io.credits.count);
return new_credits;
}
@ -1075,12 +1206,27 @@ static int manage_keep_alive_before_sending(struct smbdirect_socket *sc)
return 0;
}
static int smbd_ib_post_send(struct smbdirect_socket *sc,
struct ib_send_wr *wr)
{
int ret;
atomic_inc(&sc->send_io.pending.count);
ret = ib_post_send(sc->ib.qp, wr, NULL);
if (ret) {
pr_err("failed to post send: %d\n", ret);
smbd_disconnect_rdma_connection(sc);
ret = -EAGAIN;
}
return ret;
}
/* Post the send request */
static int smbd_post_send(struct smbdirect_socket *sc,
struct smbdirect_send_io *request)
struct smbdirect_send_batch *batch,
struct smbdirect_send_io *request)
{
struct ib_send_wr send_wr;
int rc, i;
int i;
for (i = 0; i < request->num_sge; i++) {
log_rdma_send(INFO,
@ -1094,79 +1240,245 @@ static int smbd_post_send(struct smbdirect_socket *sc,
}
request->cqe.done = send_done;
request->wr.next = NULL;
request->wr.sg_list = request->sge;
request->wr.num_sge = request->num_sge;
request->wr.opcode = IB_WR_SEND;
send_wr.next = NULL;
send_wr.wr_cqe = &request->cqe;
send_wr.sg_list = request->sge;
send_wr.num_sge = request->num_sge;
send_wr.opcode = IB_WR_SEND;
send_wr.send_flags = IB_SEND_SIGNALED;
if (batch) {
request->wr.wr_cqe = NULL;
request->wr.send_flags = 0;
if (!list_empty(&batch->msg_list)) {
struct smbdirect_send_io *last;
rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
if (rc) {
log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
smbd_disconnect_rdma_connection(sc);
rc = -EAGAIN;
last = list_last_entry(&batch->msg_list,
struct smbdirect_send_io,
sibling_list);
last->wr.next = &request->wr;
}
list_add_tail(&request->sibling_list, &batch->msg_list);
batch->wr_cnt++;
return 0;
}
return rc;
request->wr.wr_cqe = &request->cqe;
request->wr.send_flags = IB_SEND_SIGNALED;
return smbd_ib_post_send(sc, &request->wr);
}
static void smbd_send_batch_init(struct smbdirect_send_batch *batch,
bool need_invalidate_rkey,
unsigned int remote_key)
{
INIT_LIST_HEAD(&batch->msg_list);
batch->wr_cnt = 0;
batch->need_invalidate_rkey = need_invalidate_rkey;
batch->remote_key = remote_key;
batch->credit = 0;
}
static int smbd_send_batch_flush(struct smbdirect_socket *sc,
struct smbdirect_send_batch *batch,
bool is_last)
{
struct smbdirect_send_io *first, *last;
int ret = 0;
if (list_empty(&batch->msg_list))
goto release_credit;
first = list_first_entry(&batch->msg_list,
struct smbdirect_send_io,
sibling_list);
last = list_last_entry(&batch->msg_list,
struct smbdirect_send_io,
sibling_list);
if (batch->need_invalidate_rkey) {
first->wr.opcode = IB_WR_SEND_WITH_INV;
first->wr.ex.invalidate_rkey = batch->remote_key;
batch->need_invalidate_rkey = false;
batch->remote_key = 0;
}
last->wr.send_flags = IB_SEND_SIGNALED;
last->wr.wr_cqe = &last->cqe;
/*
* Remove last from batch->msg_list
* and splice the rest of batch->msg_list
* to last->sibling_list.
*
* batch->msg_list is a valid empty list
* at the end.
*/
list_del_init(&last->sibling_list);
list_splice_tail_init(&batch->msg_list, &last->sibling_list);
batch->wr_cnt = 0;
ret = smbd_ib_post_send(sc, &first->wr);
if (ret) {
struct smbdirect_send_io *sibling, *next;
list_for_each_entry_safe(sibling, next, &last->sibling_list, sibling_list) {
list_del_init(&sibling->sibling_list);
smbd_free_send_io(sibling);
}
smbd_free_send_io(last);
}
release_credit:
if (is_last && !ret && batch->credit) {
atomic_add(batch->credit, &sc->send_io.bcredits.count);
batch->credit = 0;
wake_up(&sc->send_io.bcredits.wait_queue);
}
return ret;
}
static int wait_for_credits(struct smbdirect_socket *sc,
wait_queue_head_t *waitq, atomic_t *total_credits,
int needed)
{
int ret;
do {
if (atomic_sub_return(needed, total_credits) >= 0)
return 0;
atomic_add(needed, total_credits);
ret = wait_event_interruptible(*waitq,
atomic_read(total_credits) >= needed ||
sc->status != SMBDIRECT_SOCKET_CONNECTED);
if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
return -ENOTCONN;
else if (ret < 0)
return ret;
} while (true);
}
static int wait_for_send_bcredit(struct smbdirect_socket *sc,
struct smbdirect_send_batch *batch)
{
int ret;
if (batch->credit)
return 0;
ret = wait_for_credits(sc,
&sc->send_io.bcredits.wait_queue,
&sc->send_io.bcredits.count,
1);
if (ret)
return ret;
batch->credit = 1;
return 0;
}
static int wait_for_send_lcredit(struct smbdirect_socket *sc,
struct smbdirect_send_batch *batch)
{
if (batch && (atomic_read(&sc->send_io.lcredits.count) <= 1)) {
int ret;
ret = smbd_send_batch_flush(sc, batch, false);
if (ret)
return ret;
}
return wait_for_credits(sc,
&sc->send_io.lcredits.wait_queue,
&sc->send_io.lcredits.count,
1);
}
static int wait_for_send_credits(struct smbdirect_socket *sc,
struct smbdirect_send_batch *batch)
{
if (batch &&
(batch->wr_cnt >= 16 || atomic_read(&sc->send_io.credits.count) <= 1)) {
int ret;
ret = smbd_send_batch_flush(sc, batch, false);
if (ret)
return ret;
}
return wait_for_credits(sc,
&sc->send_io.credits.wait_queue,
&sc->send_io.credits.count,
1);
}
static int smbd_post_send_iter(struct smbdirect_socket *sc,
struct smbdirect_send_batch *batch,
struct iov_iter *iter,
int *_remaining_data_length)
{
struct smbdirect_socket_parameters *sp = &sc->parameters;
int i, rc;
int rc;
int header_length;
int data_length;
struct smbdirect_send_io *request;
struct smbdirect_data_transfer *packet;
int new_credits = 0;
struct smbdirect_send_batch _batch;
wait_lcredit:
/* Wait for local send credits */
rc = wait_event_interruptible(sc->send_io.lcredits.wait_queue,
atomic_read(&sc->send_io.lcredits.count) > 0 ||
sc->status != SMBDIRECT_SOCKET_CONNECTED);
if (rc)
goto err_wait_lcredit;
if (!batch) {
smbd_send_batch_init(&_batch, false, 0);
batch = &_batch;
}
if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
log_outgoing(ERR, "disconnected not sending on wait_credit\n");
rc = wait_for_send_bcredit(sc, batch);
if (rc) {
log_outgoing(ERR, "disconnected not sending on wait_bcredit\n");
rc = -EAGAIN;
goto err_wait_bcredit;
}
rc = wait_for_send_lcredit(sc, batch);
if (rc) {
log_outgoing(ERR, "disconnected not sending on wait_lcredit\n");
rc = -EAGAIN;
goto err_wait_lcredit;
}
if (unlikely(atomic_dec_return(&sc->send_io.lcredits.count) < 0)) {
atomic_inc(&sc->send_io.lcredits.count);
goto wait_lcredit;
}
wait_credit:
/* Wait for send credits. A SMBD packet needs one credit */
rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
atomic_read(&sc->send_io.credits.count) > 0 ||
sc->status != SMBDIRECT_SOCKET_CONNECTED);
if (rc)
goto err_wait_credit;
if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
rc = wait_for_send_credits(sc, batch);
if (rc) {
log_outgoing(ERR, "disconnected not sending on wait_credit\n");
rc = -EAGAIN;
goto err_wait_credit;
}
if (unlikely(atomic_dec_return(&sc->send_io.credits.count) < 0)) {
atomic_inc(&sc->send_io.credits.count);
goto wait_credit;
new_credits = manage_credits_prior_sending(sc);
if (new_credits == 0 &&
atomic_read(&sc->send_io.credits.count) == 0 &&
atomic_read(&sc->recv_io.credits.count) == 0) {
queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
atomic_read(&sc->send_io.credits.count) >= 1 ||
atomic_read(&sc->recv_io.credits.available) >= 1 ||
sc->status != SMBDIRECT_SOCKET_CONNECTED);
if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
rc = -ENOTCONN;
if (rc < 0) {
log_outgoing(ERR, "disconnected not sending on last credit\n");
rc = -EAGAIN;
goto err_wait_credit;
}
new_credits = manage_credits_prior_sending(sc);
}
request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
if (!request) {
rc = -ENOMEM;
request = smbd_alloc_send_io(sc);
if (IS_ERR(request)) {
rc = PTR_ERR(request);
goto err_alloc;
}
request->socket = sc;
memset(request->sge, 0, sizeof(request->sge));
/* Map the packet to DMA */
@ -1215,9 +1527,6 @@ wait_credit:
/* Fill in the packet header */
packet->credits_requested = cpu_to_le16(sp->send_credit_target);
new_credits = manage_credits_prior_sending(sc);
atomic_add(new_credits, &sc->recv_io.credits.count);
packet->credits_granted = cpu_to_le16(new_credits);
packet->flags = 0;
@ -1240,32 +1549,18 @@ wait_credit:
le32_to_cpu(packet->data_length),
le32_to_cpu(packet->remaining_data_length));
/*
* Now that we got a local and a remote credit
* we add us as pending
*/
atomic_inc(&sc->send_io.pending.count);
rc = smbd_post_send(sc, batch, request);
if (!rc) {
if (batch != &_batch)
return 0;
rc = smbd_post_send(sc, request);
if (!rc)
return 0;
if (atomic_dec_and_test(&sc->send_io.pending.count))
wake_up(&sc->send_io.pending.zero_wait_queue);
wake_up(&sc->send_io.pending.dec_wait_queue);
rc = smbd_send_batch_flush(sc, batch, true);
if (!rc)
return 0;
}
err_dma:
for (i = 0; i < request->num_sge; i++)
if (request->sge[i].addr)
ib_dma_unmap_single(sc->ib.dev,
request->sge[i].addr,
request->sge[i].length,
DMA_TO_DEVICE);
mempool_free(request, sc->send_io.mem.pool);
/* roll back the granted receive credits */
atomic_sub(new_credits, &sc->recv_io.credits.count);
smbd_free_send_io(request);
err_alloc:
atomic_inc(&sc->send_io.credits.count);
@ -1276,6 +1571,11 @@ err_wait_credit:
wake_up(&sc->send_io.lcredits.wait_queue);
err_wait_lcredit:
atomic_add(batch->credit, &sc->send_io.bcredits.count);
batch->credit = 0;
wake_up(&sc->send_io.bcredits.wait_queue);
err_wait_bcredit:
return rc;
}
@ -1289,10 +1589,11 @@ static int smbd_post_send_empty(struct smbdirect_socket *sc)
int remaining_data_length = 0;
sc->statistics.send_empty++;
return smbd_post_send_iter(sc, NULL, &remaining_data_length);
return smbd_post_send_iter(sc, NULL, NULL, &remaining_data_length);
}
static int smbd_post_send_full_iter(struct smbdirect_socket *sc,
struct smbdirect_send_batch *batch,
struct iov_iter *iter,
int *_remaining_data_length)
{
@ -1305,7 +1606,7 @@ static int smbd_post_send_full_iter(struct smbdirect_socket *sc,
*/
while (iov_iter_count(iter) > 0) {
rc = smbd_post_send_iter(sc, iter, _remaining_data_length);
rc = smbd_post_send_iter(sc, batch, iter, _remaining_data_length);
if (rc < 0)
break;
}
@ -2227,8 +2528,10 @@ int smbd_send(struct TCP_Server_Info *server,
struct smbdirect_socket_parameters *sp = &sc->parameters;
struct smb_rqst *rqst;
struct iov_iter iter;
struct smbdirect_send_batch batch;
unsigned int remaining_data_length, klen;
int rc, i, rqst_idx;
int error = 0;
if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
return -EAGAIN;
@ -2253,6 +2556,7 @@ int smbd_send(struct TCP_Server_Info *server,
num_rqst, remaining_data_length);
rqst_idx = 0;
smbd_send_batch_init(&batch, false, 0);
do {
rqst = &rqst_array[rqst_idx];
@ -2271,20 +2575,28 @@ int smbd_send(struct TCP_Server_Info *server,
klen += rqst->rq_iov[i].iov_len;
iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, klen);
rc = smbd_post_send_full_iter(sc, &iter, &remaining_data_length);
if (rc < 0)
rc = smbd_post_send_full_iter(sc, &batch, &iter, &remaining_data_length);
if (rc < 0) {
error = rc;
break;
}
if (iov_iter_count(&rqst->rq_iter) > 0) {
/* And then the data pages if there are any */
rc = smbd_post_send_full_iter(sc, &rqst->rq_iter,
rc = smbd_post_send_full_iter(sc, &batch, &rqst->rq_iter,
&remaining_data_length);
if (rc < 0)
if (rc < 0) {
error = rc;
break;
}
}
} while (++rqst_idx < num_rqst);
rc = smbd_send_batch_flush(sc, &batch, true);
if (unlikely(!rc && error))
rc = error;
/*
* As an optimization, we don't wait for individual I/O to finish
* before sending the next one.

View file

@ -162,6 +162,17 @@ struct smbdirect_socket {
mempool_t *pool;
} mem;
/*
* This is a coordination for smbdirect_send_batch.
*
* There's only one possible credit, which means
* only one instance is running at a time.
*/
struct {
atomic_t count;
wait_queue_head_t wait_queue;
} bcredits;
/*
* The local credit state for ib_post_send()
*/
@ -239,6 +250,7 @@ struct smbdirect_socket {
*/
struct {
u16 target;
atomic_t available;
atomic_t count;
} credits;
@ -370,6 +382,9 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)
INIT_DELAYED_WORK(&sc->idle.timer_work, __smbdirect_socket_disabled_work);
disable_delayed_work_sync(&sc->idle.timer_work);
atomic_set(&sc->send_io.bcredits.count, 0);
init_waitqueue_head(&sc->send_io.bcredits.wait_queue);
atomic_set(&sc->send_io.lcredits.count, 0);
init_waitqueue_head(&sc->send_io.lcredits.wait_queue);
@ -387,6 +402,7 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)
INIT_WORK(&sc->recv_io.posted.refill_work, __smbdirect_socket_disabled_work);
disable_work_sync(&sc->recv_io.posted.refill_work);
atomic_set(&sc->recv_io.credits.available, 0);
atomic_set(&sc->recv_io.credits.count, 0);
INIT_LIST_HEAD(&sc->recv_io.reassembly.list);
@ -483,6 +499,8 @@ struct smbdirect_send_batch {
*/
bool need_invalidate_rkey;
u32 remote_key;
int credit;
};
struct smbdirect_recv_io {

View file

@ -18,3 +18,4 @@ $(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c
$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h
ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o
ksmbd-$(CONFIG_PROC_FS) += proc.o

View file

@ -14,6 +14,7 @@
#include "connection.h"
#include "transport_tcp.h"
#include "transport_rdma.h"
#include "misc.h"
static DEFINE_MUTEX(init_lock);
@ -22,6 +23,62 @@ static struct ksmbd_conn_ops default_conn_ops;
DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS);
DECLARE_RWSEM(conn_list_lock);
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_clients;
static int proc_show_clients(struct seq_file *m, void *v)
{
struct ksmbd_conn *conn;
struct timespec64 now, t;
int i;
seq_printf(m, "#%-20s %-10s %-10s %-10s %-10s %-10s\n",
"<name>", "<dialect>", "<credits>", "<open files>",
"<requests>", "<last active>");
down_read(&conn_list_lock);
hash_for_each(conn_list, i, conn, hlist) {
jiffies_to_timespec64(jiffies - conn->last_active, &t);
ktime_get_real_ts64(&now);
t = timespec64_sub(now, t);
#if IS_ENABLED(CONFIG_IPV6)
if (!conn->inet_addr)
seq_printf(m, "%-20pI6c", &conn->inet6_addr);
else
#endif
seq_printf(m, "%-20pI4", &conn->inet_addr);
seq_printf(m, " 0x%-10x %-10u %-12d %-10d %ptT\n",
conn->dialect,
conn->total_credits,
atomic_read(&conn->stats.open_files_count),
atomic_read(&conn->req_running),
&t);
}
up_read(&conn_list_lock);
return 0;
}
static int create_proc_clients(void)
{
proc_clients = ksmbd_proc_create("clients",
proc_show_clients, NULL);
if (!proc_clients)
return -ENOMEM;
return 0;
}
static void delete_proc_clients(void)
{
if (proc_clients) {
proc_remove(proc_clients);
proc_clients = NULL;
}
}
#else
static int create_proc_clients(void) { return 0; }
static void delete_proc_clients(void) {}
#endif
/**
* ksmbd_conn_free() - free resources of the connection instance
*
@ -472,6 +529,7 @@ int ksmbd_conn_transport_init(void)
}
out:
mutex_unlock(&init_lock);
create_proc_clients();
return ret;
}
@ -502,6 +560,7 @@ again:
void ksmbd_conn_transport_destroy(void)
{
delete_proc_clients();
mutex_lock(&init_lock);
ksmbd_tcp_destroy();
ksmbd_rdma_stop_listening();

View file

@ -7,6 +7,7 @@
#define __KSMBD_CONNECTION_H__
#include <linux/list.h>
#include <linux/inet.h>
#include <linux/ip.h>
#include <net/sock.h>
#include <net/tcp.h>
@ -33,7 +34,7 @@ enum {
KSMBD_SESS_RELEASING
};
struct ksmbd_stats {
struct ksmbd_conn_stats {
atomic_t open_files_count;
atomic64_t request_served;
};
@ -78,7 +79,7 @@ struct ksmbd_conn {
struct list_head requests;
struct list_head async_requests;
int connection_type;
struct ksmbd_stats stats;
struct ksmbd_conn_stats stats;
char ClientGUID[SMB2_CLIENT_GUID_SIZE];
struct ntlmssp_auth ntlmssp;

View file

@ -9,6 +9,7 @@
#include "../transport_ipc.h"
#include "../connection.h"
#include "../stats.h"
#include "tree_connect.h"
#include "user_config.h"
@ -79,12 +80,15 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)
status.tree_conn = tree_conn;
atomic_set(&tree_conn->refcount, 1);
down_write(&sess->tree_conns_lock);
ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
KSMBD_DEFAULT_GFP));
up_write(&sess->tree_conns_lock);
if (ret) {
status.ret = -ENOMEM;
goto out_error;
}
ksmbd_counter_inc(KSMBD_COUNTER_TREE_CONNS);
kvfree(resp);
return status;
@ -103,29 +107,36 @@ void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
kfree(tcon);
}
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn)
static int __ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn)
{
int ret;
write_lock(&sess->tree_conns_lock);
xa_erase(&sess->tree_conns, tree_conn->id);
write_unlock(&sess->tree_conns_lock);
ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
ksmbd_release_tree_conn_id(sess, tree_conn->id);
ksmbd_share_config_put(tree_conn->share_conf);
ksmbd_counter_dec(KSMBD_COUNTER_TREE_CONNS);
if (atomic_dec_and_test(&tree_conn->refcount))
kfree(tree_conn);
return ret;
}
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn)
{
down_write(&sess->tree_conns_lock);
xa_erase(&sess->tree_conns, tree_conn->id);
up_write(&sess->tree_conns_lock);
return __ksmbd_tree_conn_disconnect(sess, tree_conn);
}
struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
unsigned int id)
{
struct ksmbd_tree_connect *tcon;
read_lock(&sess->tree_conns_lock);
down_read(&sess->tree_conns_lock);
tcon = xa_load(&sess->tree_conns, id);
if (tcon) {
if (tcon->t_state != TREE_CONNECTED)
@ -133,7 +144,7 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
else if (!atomic_inc_not_zero(&tcon->refcount))
tcon = NULL;
}
read_unlock(&sess->tree_conns_lock);
up_read(&sess->tree_conns_lock);
return tcon;
}
@ -147,18 +158,19 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
if (!sess)
return -EINVAL;
down_write(&sess->tree_conns_lock);
xa_for_each(&sess->tree_conns, id, tc) {
write_lock(&sess->tree_conns_lock);
if (tc->t_state == TREE_DISCONNECTED) {
write_unlock(&sess->tree_conns_lock);
ret = -ENOENT;
continue;
}
tc->t_state = TREE_DISCONNECTED;
write_unlock(&sess->tree_conns_lock);
ret |= ksmbd_tree_conn_disconnect(sess, tc);
xa_erase(&sess->tree_conns, tc->id);
ret |= __ksmbd_tree_conn_disconnect(sess, tc);
}
xa_destroy(&sess->tree_conns);
up_write(&sess->tree_conns_lock);
return ret;
}

View file

@ -90,11 +90,9 @@ void ksmbd_free_user(struct ksmbd_user *user)
kfree(user);
}
int ksmbd_anonymous_user(struct ksmbd_user *user)
bool ksmbd_anonymous_user(struct ksmbd_user *user)
{
if (user->name[0] == '\0')
return 1;
return 0;
return user->name[0] == '\0';
}
bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2)

View file

@ -65,6 +65,6 @@ struct ksmbd_user *ksmbd_login_user(const char *account);
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
struct ksmbd_login_response_ext *resp_ext);
void ksmbd_free_user(struct ksmbd_user *user);
int ksmbd_anonymous_user(struct ksmbd_user *user);
bool ksmbd_anonymous_user(struct ksmbd_user *user);
bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2);
#endif /* __USER_CONFIG_MANAGEMENT_H__ */

View file

@ -12,9 +12,12 @@
#include "user_session.h"
#include "user_config.h"
#include "tree_connect.h"
#include "share_config.h"
#include "../transport_ipc.h"
#include "../connection.h"
#include "../vfs_cache.h"
#include "../misc.h"
#include "../stats.h"
static DEFINE_IDA(session_ida);
@ -27,17 +30,236 @@ struct ksmbd_session_rpc {
unsigned int method;
};
#ifdef CONFIG_PROC_FS
static const struct ksmbd_const_name ksmbd_sess_cap_const_names[] = {
{SMB2_GLOBAL_CAP_DFS, "dfs"},
{SMB2_GLOBAL_CAP_LEASING, "lease"},
{SMB2_GLOBAL_CAP_LARGE_MTU, "large-mtu"},
{SMB2_GLOBAL_CAP_MULTI_CHANNEL, "multi-channel"},
{SMB2_GLOBAL_CAP_PERSISTENT_HANDLES, "persistent-handles"},
{SMB2_GLOBAL_CAP_DIRECTORY_LEASING, "dir-lease"},
{SMB2_GLOBAL_CAP_ENCRYPTION, "encryption"}
};
static const struct ksmbd_const_name ksmbd_cipher_const_names[] = {
{le16_to_cpu(SMB2_ENCRYPTION_AES128_CCM), "aes128-ccm"},
{le16_to_cpu(SMB2_ENCRYPTION_AES128_GCM), "aes128-gcm"},
{le16_to_cpu(SMB2_ENCRYPTION_AES256_CCM), "aes256-ccm"},
{le16_to_cpu(SMB2_ENCRYPTION_AES256_GCM), "aes256-gcm"},
};
static const struct ksmbd_const_name ksmbd_signing_const_names[] = {
{SIGNING_ALG_HMAC_SHA256, "hmac-sha256"},
{SIGNING_ALG_AES_CMAC, "aes-cmac"},
{SIGNING_ALG_AES_GMAC, "aes-gmac"},
};
static const char *session_state_string(struct ksmbd_session *session)
{
switch (session->state) {
case SMB2_SESSION_VALID:
return "valid";
case SMB2_SESSION_IN_PROGRESS:
return "progress";
case SMB2_SESSION_EXPIRED:
return "expired";
default:
return "";
}
}
static const char *session_user_name(struct ksmbd_session *session)
{
if (user_guest(session->user))
return "(Guest)";
else if (ksmbd_anonymous_user(session->user))
return "(Anonymous)";
return session->user->name;
}
static int show_proc_session(struct seq_file *m, void *v)
{
struct ksmbd_session *sess;
struct ksmbd_tree_connect *tree_conn;
struct ksmbd_share_config *share_conf;
struct channel *chan;
unsigned long id;
int i = 0;
sess = (struct ksmbd_session *)m->private;
ksmbd_user_session_get(sess);
i = 0;
down_read(&sess->chann_lock);
xa_for_each(&sess->ksmbd_chann_list, id, chan) {
#if IS_ENABLED(CONFIG_IPV6)
if (chan->conn->inet_addr)
seq_printf(m, "%-20s\t%pI4\n", "client",
&chan->conn->inet_addr);
else
seq_printf(m, "%-20s\t%pI6c\n", "client",
&chan->conn->inet6_addr);
#else
seq_printf(m, "%-20s\t%pI4\n", "client",
&chan->conn->inet_addr);
#endif
seq_printf(m, "%-20s\t%s\n", "user", session_user_name(sess));
seq_printf(m, "%-20s\t%llu\n", "id", sess->id);
seq_printf(m, "%-20s\t%s\n", "state",
session_state_string(sess));
seq_printf(m, "%-20s\t", "capabilities");
ksmbd_proc_show_flag_names(m,
ksmbd_sess_cap_const_names,
ARRAY_SIZE(ksmbd_sess_cap_const_names),
chan->conn->vals->req_capabilities);
if (sess->sign) {
seq_printf(m, "%-20s\t", "signing");
ksmbd_proc_show_const_name(m, "%s\t",
ksmbd_signing_const_names,
ARRAY_SIZE(ksmbd_signing_const_names),
le16_to_cpu(chan->conn->signing_algorithm));
} else if (sess->enc) {
seq_printf(m, "%-20s\t", "encryption");
ksmbd_proc_show_const_name(m, "%s\t",
ksmbd_cipher_const_names,
ARRAY_SIZE(ksmbd_cipher_const_names),
le16_to_cpu(chan->conn->cipher_type));
}
i++;
}
up_read(&sess->chann_lock);
seq_printf(m, "%-20s\t%d\n", "channels", i);
i = 0;
down_read(&sess->tree_conns_lock);
xa_for_each(&sess->tree_conns, id, tree_conn) {
share_conf = tree_conn->share_conf;
seq_printf(m, "%-20s\t%s\t%8d", "share",
share_conf->name, tree_conn->id);
if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_PIPE))
seq_printf(m, " %s ", "pipe");
else
seq_printf(m, " %s ", "disk");
seq_putc(m, '\n');
}
up_read(&sess->tree_conns_lock);
ksmbd_user_session_put(sess);
return 0;
}
void ksmbd_proc_show_flag_names(struct seq_file *m,
const struct ksmbd_const_name *table,
int count,
unsigned int flags)
{
int i;
for (i = 0; i < count; i++) {
if (table[i].const_value & flags)
seq_printf(m, "0x%08x\t", table[i].const_value);
}
seq_putc(m, '\n');
}
void ksmbd_proc_show_const_name(struct seq_file *m,
const char *format,
const struct ksmbd_const_name *table,
int count,
unsigned int const_value)
{
int i;
for (i = 0; i < count; i++) {
if (table[i].const_value & const_value)
seq_printf(m, format, table[i].name);
}
seq_putc(m, '\n');
}
static int create_proc_session(struct ksmbd_session *sess)
{
char name[30];
snprintf(name, sizeof(name), "sessions/%llu", sess->id);
sess->proc_entry = ksmbd_proc_create(name,
show_proc_session, sess);
return 0;
}
static void delete_proc_session(struct ksmbd_session *sess)
{
if (sess->proc_entry)
proc_remove(sess->proc_entry);
}
static int show_proc_sessions(struct seq_file *m, void *v)
{
struct ksmbd_session *session;
struct channel *chan;
int i;
unsigned long id;
seq_printf(m, "#%-40s %-15s %-10s %-10s\n",
"<client>", "<user>", "<sess_id>", "<state>");
down_read(&sessions_table_lock);
hash_for_each(sessions_table, i, session, hlist) {
down_read(&session->chann_lock);
xa_for_each(&session->ksmbd_chann_list, id, chan) {
down_read(&chan->conn->session_lock);
ksmbd_user_session_get(session);
#if IS_ENABLED(CONFIG_IPV6)
if (!chan->conn->inet_addr)
seq_printf(m, " %-40pI6c", &chan->conn->inet6_addr);
else
#endif
seq_printf(m, " %-40pI4", &chan->conn->inet_addr);
seq_printf(m, " %-15s %-10llu %-10s\n",
session_user_name(session),
session->id,
session_state_string(session));
ksmbd_user_session_put(session);
up_read(&chan->conn->session_lock);
}
up_read(&session->chann_lock);
}
up_read(&sessions_table_lock);
return 0;
}
int create_proc_sessions(void)
{
if (!ksmbd_proc_create("sessions/sessions",
show_proc_sessions, NULL))
return -ENOMEM;
return 0;
}
#else
int create_proc_sessions(void) { return 0; }
static int create_proc_session(struct ksmbd_session *sess) { return 0; }
static void delete_proc_session(struct ksmbd_session *sess) {}
#endif
static void free_channel_list(struct ksmbd_session *sess)
{
struct channel *chann;
unsigned long index;
down_write(&sess->chann_lock);
xa_for_each(&sess->ksmbd_chann_list, index, chann) {
xa_erase(&sess->ksmbd_chann_list, index);
kfree(chann);
}
xa_destroy(&sess->ksmbd_chann_list);
up_write(&sess->chann_lock);
}
static void __session_rpc_close(struct ksmbd_session *sess,
@ -159,6 +381,8 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
if (!sess)
return;
delete_proc_session(sess);
if (sess->user)
ksmbd_free_user(sess->user);
@ -220,7 +444,9 @@ static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
{
struct channel *chann;
down_write(&sess->chann_lock);
chann = xa_erase(&sess->ksmbd_chann_list, (long)conn);
up_write(&sess->chann_lock);
if (!chann)
return -ENOENT;
@ -451,9 +677,10 @@ static struct ksmbd_session *__session_create(int protocol)
xa_init(&sess->ksmbd_chann_list);
xa_init(&sess->rpc_handle_list);
sess->sequence_number = 1;
rwlock_init(&sess->tree_conns_lock);
atomic_set(&sess->refcnt, 2);
init_rwsem(&sess->tree_conns_lock);
init_rwsem(&sess->rpc_lock);
init_rwsem(&sess->chann_lock);
ret = __init_smb2_session(sess);
if (ret)
@ -465,6 +692,8 @@ static struct ksmbd_session *__session_create(int protocol)
hash_add(sessions_table, &sess->hlist, sess->id);
up_write(&sessions_table_lock);
create_proc_session(sess);
ksmbd_counter_inc(KSMBD_COUNTER_SESSIONS);
return sess;
error:

View file

@ -41,7 +41,6 @@ struct ksmbd_session {
bool sign;
bool enc;
bool is_anonymous;
int state;
__u8 *Preauth_HashValue;
@ -49,6 +48,7 @@ struct ksmbd_session {
char sess_key[CIFS_KEY_SIZE];
struct hlist_node hlist;
struct rw_semaphore chann_lock;
struct xarray ksmbd_chann_list;
struct xarray tree_conns;
struct ida tree_conn_ida;
@ -60,8 +60,11 @@ struct ksmbd_session {
struct ksmbd_file_table file_table;
unsigned long last_active;
rwlock_t tree_conns_lock;
struct rw_semaphore tree_conns_lock;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_entry;
#endif
atomic_t refcnt;
struct rw_semaphore rpc_lock;
};
@ -111,4 +114,5 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id);
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id);
void ksmbd_user_session_get(struct ksmbd_session *sess);
void ksmbd_user_session_put(struct ksmbd_session *sess);
int create_proc_sessions(void);
#endif /* __USER_SESSION_MANAGEMENT_H__ */

View file

@ -6,6 +6,9 @@
#ifndef __KSMBD_MISC_H__
#define __KSMBD_MISC_H__
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#endif
struct ksmbd_share_config;
struct nls_table;
struct kstat;
@ -34,4 +37,31 @@ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc);
u64 ksmbd_UnixTimeToNT(struct timespec64 t);
long long ksmbd_systime(void);
#ifdef CONFIG_PROC_FS
struct ksmbd_const_name {
unsigned int const_value;
const char *name;
};
void ksmbd_proc_init(void);
void ksmbd_proc_cleanup(void);
void ksmbd_proc_reset(void);
struct proc_dir_entry *ksmbd_proc_create(const char *name,
int (*show)(struct seq_file *m, void *v),
void *v);
void ksmbd_proc_show_flag_names(struct seq_file *m,
const struct ksmbd_const_name *table,
int count,
unsigned int flags);
void ksmbd_proc_show_const_name(struct seq_file *m,
const char *format,
const struct ksmbd_const_name *table,
int count,
unsigned int const_value);
#else
static inline void ksmbd_proc_init(void) {}
static inline void ksmbd_proc_cleanup(void) {}
static inline void ksmbd_proc_reset(void) {}
#endif
#endif /* __KSMBD_MISC_H__ */

134
fs/smb/server/proc.c Normal file
View file

@ -0,0 +1,134 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025, LG Electronics.
* Author(s): Hyunchul Lee <hyc.lee@gmail.com>
* Copyright (C) 2025, Samsung Electronics.
* Author(s): Vedansh Bhardwaj <v.bhardwaj@samsung.com>
*/
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include "misc.h"
#include "server.h"
#include "stats.h"
#include "smb_common.h"
#include "smb2pdu.h"
static struct proc_dir_entry *ksmbd_proc_fs;
struct ksmbd_counters ksmbd_counters;
struct proc_dir_entry *ksmbd_proc_create(const char *name,
int (*show)(struct seq_file *m, void *v),
void *v)
{
return proc_create_single_data(name, 0400, ksmbd_proc_fs,
show, v);
}
struct ksmbd_const_smb2_process_req {
unsigned int const_value;
const char *name;
};
static const struct ksmbd_const_smb2_process_req smb2_process_req[KSMBD_COUNTER_MAX_REQS] = {
{le16_to_cpu(SMB2_NEGOTIATE), "SMB2_NEGOTIATE"},
{le16_to_cpu(SMB2_SESSION_SETUP), "SMB2_SESSION_SETUP"},
{le16_to_cpu(SMB2_LOGOFF), "SMB2_LOGOFF"},
{le16_to_cpu(SMB2_TREE_CONNECT), "SMB2_TREE_CONNECT"},
{le16_to_cpu(SMB2_TREE_DISCONNECT), "SMB2_TREE_DISCONNECT"},
{le16_to_cpu(SMB2_CREATE), "SMB2_CREATE"},
{le16_to_cpu(SMB2_CLOSE), "SMB2_CLOSE"},
{le16_to_cpu(SMB2_FLUSH), "SMB2_FLUSH"},
{le16_to_cpu(SMB2_READ), "SMB2_READ"},
{le16_to_cpu(SMB2_WRITE), "SMB2_WRITE"},
{le16_to_cpu(SMB2_LOCK), "SMB2_LOCK"},
{le16_to_cpu(SMB2_IOCTL), "SMB2_IOCTL"},
{le16_to_cpu(SMB2_CANCEL), "SMB2_CANCEL"},
{le16_to_cpu(SMB2_ECHO), "SMB2_ECHO"},
{le16_to_cpu(SMB2_QUERY_DIRECTORY), "SMB2_QUERY_DIRECTORY"},
{le16_to_cpu(SMB2_CHANGE_NOTIFY), "SMB2_CHANGE_NOTIFY"},
{le16_to_cpu(SMB2_QUERY_INFO), "SMB2_QUERY_INFO"},
{le16_to_cpu(SMB2_SET_INFO), "SMB2_SET_INFO"},
{le16_to_cpu(SMB2_OPLOCK_BREAK), "SMB2_OPLOCK_BREAK"},
};
static int proc_show_ksmbd_stats(struct seq_file *m, void *v)
{
int i;
seq_puts(m, "Server\n");
seq_printf(m, "name: %s\n", ksmbd_server_string());
seq_printf(m, "netbios: %s\n", ksmbd_netbios_name());
seq_printf(m, "work group: %s\n", ksmbd_work_group());
seq_printf(m, "min protocol: %s\n", ksmbd_get_protocol_string(server_conf.min_protocol));
seq_printf(m, "max protocol: %s\n", ksmbd_get_protocol_string(server_conf.max_protocol));
seq_printf(m, "flags: 0x%08x\n", server_conf.flags);
seq_printf(m, "share_fake_fscaps: 0x%08x\n",
server_conf.share_fake_fscaps);
seq_printf(m, "sessions: %lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS));
seq_printf(m, "tree connects: %lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS));
seq_printf(m, "read bytes: %lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES));
seq_printf(m, "written bytes: %lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_WRITE_BYTES));
seq_puts(m, "\nSMB2\n");
for (i = 0; i < KSMBD_COUNTER_MAX_REQS; i++)
seq_printf(m, "%-20s:\t%lld\n", smb2_process_req[i].name,
ksmbd_counter_sum(KSMBD_COUNTER_FIRST_REQ + i));
return 0;
}
void ksmbd_proc_cleanup(void)
{
int i;
if (!ksmbd_proc_fs)
return;
proc_remove(ksmbd_proc_fs);
for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++)
percpu_counter_destroy(&ksmbd_counters.counters[i]);
ksmbd_proc_fs = NULL;
}
void ksmbd_proc_reset(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++)
percpu_counter_set(&ksmbd_counters.counters[i], 0);
}
void ksmbd_proc_init(void)
{
int i;
int retval;
ksmbd_proc_fs = proc_mkdir("fs/ksmbd", NULL);
if (!ksmbd_proc_fs)
return;
if (!proc_mkdir_mode("sessions", 0400, ksmbd_proc_fs))
goto err_out;
for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) {
retval = percpu_counter_init(&ksmbd_counters.counters[i], 0, GFP_KERNEL);
if (retval)
goto err_out;
}
if (!ksmbd_proc_create("server", proc_show_ksmbd_stats, NULL))
goto err_out;
ksmbd_proc_reset();
return;
err_out:
ksmbd_proc_cleanup();
}

View file

@ -21,6 +21,8 @@
#include "mgmt/user_session.h"
#include "crypto_ctx.h"
#include "auth.h"
#include "misc.h"
#include "stats.h"
int ksmbd_debug_types;
@ -126,25 +128,27 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
andx_again:
if (command >= conn->max_cmds) {
conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
return SERVER_HANDLER_CONTINUE;
return SERVER_HANDLER_ABORT;
}
cmds = &conn->cmds[command];
if (!cmds->proc) {
ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command);
conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED);
return SERVER_HANDLER_CONTINUE;
return SERVER_HANDLER_ABORT;
}
if (work->sess && conn->ops->is_sign_req(work, command)) {
ret = conn->ops->check_sign_req(work);
if (!ret) {
conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED);
return SERVER_HANDLER_CONTINUE;
return SERVER_HANDLER_ABORT;
}
}
ret = cmds->proc(work);
if (conn->ops->inc_reqs)
conn->ops->inc_reqs(command);
if (ret < 0)
ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret);
@ -359,6 +363,7 @@ static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl)
{
int ret;
ksmbd_proc_reset();
ret = ksmbd_conn_transport_init();
if (ret) {
server_queue_ctrl_reset_work();
@ -531,6 +536,7 @@ static int ksmbd_server_shutdown(void)
{
WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN);
ksmbd_proc_cleanup();
class_unregister(&ksmbd_control_class);
ksmbd_workqueue_destroy();
ksmbd_ipc_release();
@ -554,6 +560,9 @@ static int __init ksmbd_server_init(void)
return ret;
}
ksmbd_proc_init();
create_proc_sessions();
ksmbd_server_tcp_callbacks_init();
ret = server_conf_init();

View file

@ -11,6 +11,7 @@
#include "connection.h"
#include "smb_common.h"
#include "server.h"
#include "stats.h"
static struct smb_version_values smb21_server_values = {
.version_string = SMB21_VERSION_STRING,
@ -121,6 +122,7 @@ static struct smb_version_values smb311_server_values = {
static struct smb_version_ops smb2_0_server_ops = {
.get_cmd_val = get_smb2_cmd_val,
.inc_reqs = ksmbd_counter_inc_reqs,
.init_rsp_hdr = init_smb2_rsp_hdr,
.set_rsp_status = set_smb2_rsp_status,
.allocate_rsp_buf = smb2_allocate_rsp_buf,
@ -134,6 +136,7 @@ static struct smb_version_ops smb2_0_server_ops = {
static struct smb_version_ops smb3_0_server_ops = {
.get_cmd_val = get_smb2_cmd_val,
.inc_reqs = ksmbd_counter_inc_reqs,
.init_rsp_hdr = init_smb2_rsp_hdr,
.set_rsp_status = set_smb2_rsp_status,
.allocate_rsp_buf = smb2_allocate_rsp_buf,
@ -152,6 +155,7 @@ static struct smb_version_ops smb3_0_server_ops = {
static struct smb_version_ops smb3_11_server_ops = {
.get_cmd_val = get_smb2_cmd_val,
.inc_reqs = ksmbd_counter_inc_reqs,
.init_rsp_hdr = init_smb2_rsp_hdr,
.set_rsp_status = set_smb2_rsp_status,
.allocate_rsp_buf = smb2_allocate_rsp_buf,

View file

@ -39,6 +39,7 @@
#include "mgmt/user_session.h"
#include "mgmt/ksmbd_ida.h"
#include "ndr.h"
#include "stats.h"
#include "transport_tcp.h"
static void __wbuf(struct ksmbd_work *work, void **req, void **rsp)
@ -79,7 +80,13 @@ static inline bool check_session_id(struct ksmbd_conn *conn, u64 id)
struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn)
{
return xa_load(&sess->ksmbd_chann_list, (long)conn);
struct channel *chann;
down_read(&sess->chann_lock);
chann = xa_load(&sess->ksmbd_chann_list, (long)conn);
up_read(&sess->chann_lock);
return chann;
}
/**
@ -1558,8 +1565,10 @@ binding_session:
return -ENOMEM;
chann->conn = conn;
down_write(&sess->chann_lock);
old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann,
KSMBD_DEFAULT_GFP);
up_write(&sess->chann_lock);
if (xa_is_err(old)) {
kfree(chann);
return xa_err(old);
@ -1651,8 +1660,10 @@ binding_session:
return -ENOMEM;
chann->conn = conn;
down_write(&sess->chann_lock);
old = xa_store(&sess->ksmbd_chann_list, (long)conn,
chann, KSMBD_DEFAULT_GFP);
up_write(&sess->chann_lock);
if (xa_is_err(old)) {
kfree(chann);
return xa_err(old);
@ -2026,9 +2037,9 @@ int smb2_tree_connect(struct ksmbd_work *work)
if (conn->posix_ext_supported)
status.tree_conn->posix_extensions = true;
write_lock(&sess->tree_conns_lock);
down_write(&sess->tree_conns_lock);
status.tree_conn->t_state = TREE_CONNECTED;
write_unlock(&sess->tree_conns_lock);
up_write(&sess->tree_conns_lock);
rsp->StructureSize = cpu_to_le16(16);
out_err1:
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && share &&
@ -2182,16 +2193,16 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
ksmbd_close_tree_conn_fds(work);
write_lock(&sess->tree_conns_lock);
down_write(&sess->tree_conns_lock);
if (tcon->t_state == TREE_DISCONNECTED) {
write_unlock(&sess->tree_conns_lock);
up_write(&sess->tree_conns_lock);
rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
err = -ENOENT;
goto err_out;
}
tcon->t_state = TREE_DISCONNECTED;
write_unlock(&sess->tree_conns_lock);
up_write(&sess->tree_conns_lock);
err = ksmbd_tree_conn_disconnect(sess, tcon);
if (err) {

View file

@ -98,6 +98,30 @@ inline int ksmbd_max_protocol(void)
return SMB311_PROT;
}
static const struct {
int version;
const char *string;
} version_strings[] = {
#ifdef CONFIG_SMB_INSECURE_SERVER
{SMB1_PROT, SMB1_VERSION_STRING},
#endif
{SMB2_PROT, SMB20_VERSION_STRING},
{SMB21_PROT, SMB21_VERSION_STRING},
{SMB30_PROT, SMB30_VERSION_STRING},
{SMB302_PROT, SMB302_VERSION_STRING},
{SMB311_PROT, SMB311_VERSION_STRING},
};
const char *ksmbd_get_protocol_string(int version)
{
int i;
for (i = 0; i < ARRAY_SIZE(version_strings); i++) {
if (version_strings[i].version == version)
return version_strings[i].string;
}
return "";
}
int ksmbd_lookup_protocol_idx(char *str)
{
int offt = ARRAY_SIZE(smb1_protos) - 1;

View file

@ -143,6 +143,7 @@ struct file_id_both_directory_info {
struct smb_version_ops {
u16 (*get_cmd_val)(struct ksmbd_work *swork);
void (*inc_reqs)(unsigned int cmd);
int (*init_rsp_hdr)(struct ksmbd_work *swork);
void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err);
int (*allocate_rsp_buf)(struct ksmbd_work *work);
@ -165,6 +166,7 @@ struct smb_version_cmds {
int ksmbd_min_protocol(void);
int ksmbd_max_protocol(void);
const char *ksmbd_get_protocol_string(int version);
int ksmbd_lookup_protocol_idx(char *str);

73
fs/smb/server/stats.h Normal file
View file

@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025, LG Electronics.
* Author(s): Hyunchul Lee <hyc.lee@gmail.com>
* Copyright (C) 2025, Samsung Electronics.
* Author(s): Vedansh Bhardwaj <v.bhardwaj@samsung.com>
*/
#ifndef __KSMBD_STATS_H__
#define __KSMBD_STATS_H__
#define KSMBD_COUNTER_MAX_REQS 19
enum {
KSMBD_COUNTER_SESSIONS = 0,
KSMBD_COUNTER_TREE_CONNS,
KSMBD_COUNTER_REQUESTS,
KSMBD_COUNTER_READ_BYTES,
KSMBD_COUNTER_WRITE_BYTES,
KSMBD_COUNTER_FIRST_REQ,
KSMBD_COUNTER_LAST_REQ = KSMBD_COUNTER_FIRST_REQ +
KSMBD_COUNTER_MAX_REQS - 1,
KSMBD_COUNTER_MAX,
};
#ifdef CONFIG_PROC_FS
extern struct ksmbd_counters ksmbd_counters;
struct ksmbd_counters {
struct percpu_counter counters[KSMBD_COUNTER_MAX];
};
static inline void ksmbd_counter_inc(int type)
{
percpu_counter_inc(&ksmbd_counters.counters[type]);
}
static inline void ksmbd_counter_dec(int type)
{
percpu_counter_dec(&ksmbd_counters.counters[type]);
}
static inline void ksmbd_counter_add(int type, s64 value)
{
percpu_counter_add(&ksmbd_counters.counters[type], value);
}
static inline void ksmbd_counter_sub(int type, s64 value)
{
percpu_counter_sub(&ksmbd_counters.counters[type], value);
}
static inline void ksmbd_counter_inc_reqs(unsigned int cmd)
{
if (cmd < KSMBD_COUNTER_MAX_REQS)
percpu_counter_inc(&ksmbd_counters.counters[KSMBD_COUNTER_FIRST_REQ + cmd]);
}
static inline s64 ksmbd_counter_sum(int type)
{
return percpu_counter_sum_positive(&ksmbd_counters.counters[type]);
}
#else
static inline void ksmbd_counter_inc(int type) {}
static inline void ksmbd_counter_dec(int type) {}
static inline void ksmbd_counter_add(int type, s64 value) {}
static inline void ksmbd_counter_sub(int type, s64 value) {}
static inline void ksmbd_counter_inc_reqs(unsigned int cmd) {}
static inline s64 ksmbd_counter_sum(int type) { return 0; }
#endif
#endif

View file

@ -61,9 +61,6 @@
* Those may change after a SMB_DIRECT negotiation
*/
/* Set 445 port to SMB Direct port by default */
static int smb_direct_port = SMB_DIRECT_PORT_INFINIBAND;
/* The local peer's maximum number of credits to grant to the peer */
static int smb_direct_receive_credit_max = 255;
@ -73,8 +70,23 @@ static int smb_direct_send_credit_target = 255;
/* The maximum single message size can be sent to remote peer */
static int smb_direct_max_send_size = 1364;
/* The maximum fragmented upper-layer payload receive size supported */
static int smb_direct_max_fragmented_recv_size = 1024 * 1024;
/*
* The maximum fragmented upper-layer payload receive size supported
*
* Assume max_payload_per_credit is
* smb_direct_receive_credit_max - 24 = 1340
*
* The maximum number would be
* smb_direct_receive_credit_max * max_payload_per_credit
*
* 1340 * 255 = 341700 (0x536C4)
*
* The minimum value from the spec is 131072 (0x20000)
*
* For now we use the logic we used before:
* (1364 * 255) / 2 = 173910 (0x2A756)
*/
static int smb_direct_max_fragmented_recv_size = (1364 * 255) / 2;
/* The maximum single-message size which can be received */
static int smb_direct_max_receive_size = 1364;
@ -90,8 +102,9 @@ struct smb_direct_device {
};
static struct smb_direct_listener {
int port;
struct rdma_cm_id *cm_id;
} smb_direct_listener;
} smb_direct_ib_listener, smb_direct_iw_listener;
static struct workqueue_struct *smb_direct_wq;
@ -221,6 +234,7 @@ static void smb_direct_disconnect_wake_up_all(struct smbdirect_socket *sc)
* in order to notice the broken connection.
*/
wake_up_all(&sc->status_wait);
wake_up_all(&sc->send_io.bcredits.wait_queue);
wake_up_all(&sc->send_io.lcredits.wait_queue);
wake_up_all(&sc->send_io.credits.wait_queue);
wake_up_all(&sc->send_io.pending.zero_wait_queue);
@ -644,6 +658,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
struct smbdirect_data_transfer *data_transfer =
(struct smbdirect_data_transfer *)recvmsg->packet;
u32 remaining_data_length, data_offset, data_length;
int current_recv_credits;
u16 old_recv_credit_target;
if (wc->byte_len <
@ -682,7 +697,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
}
atomic_dec(&sc->recv_io.posted.count);
atomic_dec(&sc->recv_io.credits.count);
current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count);
old_recv_credit_target = sc->recv_io.credits.target;
sc->recv_io.credits.target =
@ -702,7 +717,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
wake_up(&sc->send_io.credits.wait_queue);
if (data_length) {
if (sc->recv_io.credits.target > old_recv_credit_target)
if (current_recv_credits <= (sc->recv_io.credits.target / 4) ||
sc->recv_io.credits.target > old_recv_credit_target)
queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
enqueue_reassembly(sc, recvmsg, (int)data_length);
@ -1028,6 +1044,17 @@ static void smb_direct_post_recv_credits(struct work_struct *work)
}
}
atomic_add(credits, &sc->recv_io.credits.available);
/*
* If the last send credit is waiting for credits
* it can grant we need to wake it up
*/
if (credits &&
atomic_read(&sc->send_io.bcredits.count) == 0 &&
atomic_read(&sc->send_io.credits.count) == 0)
wake_up(&sc->send_io.credits.wait_queue);
if (credits)
queue_work(sc->workqueue, &sc->idle.immediate_work);
}
@ -1045,6 +1072,31 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc)
ib_wc_status_msg(wc->status), wc->status,
wc->opcode);
if (unlikely(!(sendmsg->wr.send_flags & IB_SEND_SIGNALED))) {
/*
* This happens when smbdirect_send_io is a sibling
* before the final message, it is signaled on
* error anyway, so we need to skip
* smbdirect_connection_free_send_io here,
* otherwise is will destroy the memory
* of the siblings too, which will cause
* use after free problems for the others
* triggered from ib_drain_qp().
*/
if (wc->status != IB_WC_SUCCESS)
goto skip_free;
/*
* This should not happen!
* But we better just close the
* connection...
*/
pr_err("unexpected send completion wc->status=%s (%d) wc->opcode=%d\n",
ib_wc_status_msg(wc->status), wc->status, wc->opcode);
smb_direct_disconnect_rdma_connection(sc);
return;
}
/*
* Free possible siblings and then the main send_io
*/
@ -1058,6 +1110,7 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc)
lcredits += 1;
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
skip_free:
pr_err("Send error. status='%s (%d)', opcode=%d\n",
ib_wc_status_msg(wc->status), wc->status,
wc->opcode);
@ -1074,19 +1127,37 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc)
static int manage_credits_prior_sending(struct smbdirect_socket *sc)
{
int missing;
int available;
int new_credits;
if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target)
return 0;
new_credits = atomic_read(&sc->recv_io.posted.count);
if (new_credits == 0)
missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count);
available = atomic_xchg(&sc->recv_io.credits.available, 0);
new_credits = (u16)min3(U16_MAX, missing, available);
if (new_credits <= 0) {
/*
* If credits are available, but not granted
* we need to re-add them again.
*/
if (available)
atomic_add(available, &sc->recv_io.credits.available);
return 0;
}
new_credits -= atomic_read(&sc->recv_io.credits.count);
if (new_credits <= 0)
return 0;
if (new_credits < available) {
/*
* Readd the remaining available again.
*/
available -= new_credits;
atomic_add(available, &sc->recv_io.credits.available);
}
/*
* Remember we granted the credits
*/
atomic_add(new_credits, &sc->recv_io.credits.count);
return new_credits;
}
@ -1130,6 +1201,7 @@ static void smb_direct_send_ctx_init(struct smbdirect_send_batch *send_ctx,
send_ctx->wr_cnt = 0;
send_ctx->need_invalidate_rkey = need_invalidate_rkey;
send_ctx->remote_key = remote_key;
send_ctx->credit = 0;
}
static int smb_direct_flush_send_list(struct smbdirect_socket *sc,
@ -1137,10 +1209,10 @@ static int smb_direct_flush_send_list(struct smbdirect_socket *sc,
bool is_last)
{
struct smbdirect_send_io *first, *last;
int ret;
int ret = 0;
if (list_empty(&send_ctx->msg_list))
return 0;
goto release_credit;
first = list_first_entry(&send_ctx->msg_list,
struct smbdirect_send_io,
@ -1182,6 +1254,13 @@ static int smb_direct_flush_send_list(struct smbdirect_socket *sc,
smb_direct_free_sendmsg(sc, last);
}
release_credit:
if (is_last && !ret && send_ctx->credit) {
atomic_add(send_ctx->credit, &sc->send_io.bcredits.count);
send_ctx->credit = 0;
wake_up(&sc->send_io.bcredits.wait_queue);
}
return ret;
}
@ -1207,6 +1286,25 @@ static int wait_for_credits(struct smbdirect_socket *sc,
} while (true);
}
static int wait_for_send_bcredit(struct smbdirect_socket *sc,
struct smbdirect_send_batch *send_ctx)
{
int ret;
if (send_ctx->credit)
return 0;
ret = wait_for_credits(sc,
&sc->send_io.bcredits.wait_queue,
&sc->send_io.bcredits.count,
1);
if (ret)
return ret;
send_ctx->credit = 1;
return 0;
}
static int wait_for_send_lcredit(struct smbdirect_socket *sc,
struct smbdirect_send_batch *send_ctx)
{
@ -1256,6 +1354,7 @@ static int calc_rw_credits(struct smbdirect_socket *sc,
static int smb_direct_create_header(struct smbdirect_socket *sc,
int size, int remaining_data_length,
int new_credits,
struct smbdirect_send_io **sendmsg_out)
{
struct smbdirect_socket_parameters *sp = &sc->parameters;
@ -1271,7 +1370,7 @@ static int smb_direct_create_header(struct smbdirect_socket *sc,
/* Fill in the packet header */
packet = (struct smbdirect_data_transfer *)sendmsg->packet;
packet->credits_requested = cpu_to_le16(sp->send_credit_target);
packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(sc));
packet->credits_granted = cpu_to_le16(new_credits);
packet->flags = 0;
if (manage_keep_alive_before_sending(sc))
@ -1408,6 +1507,17 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc,
struct smbdirect_send_io *msg;
int data_length;
struct scatterlist sg[SMBDIRECT_SEND_IO_MAX_SGE - 1];
struct smbdirect_send_batch _send_ctx;
int new_credits;
if (!send_ctx) {
smb_direct_send_ctx_init(&_send_ctx, false, 0);
send_ctx = &_send_ctx;
}
ret = wait_for_send_bcredit(sc, send_ctx);
if (ret)
goto bcredit_failed;
ret = wait_for_send_lcredit(sc, send_ctx);
if (ret)
@ -1417,12 +1527,29 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc,
if (ret)
goto credit_failed;
new_credits = manage_credits_prior_sending(sc);
if (new_credits == 0 &&
atomic_read(&sc->send_io.credits.count) == 0 &&
atomic_read(&sc->recv_io.credits.count) == 0) {
queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
ret = wait_event_interruptible(sc->send_io.credits.wait_queue,
atomic_read(&sc->send_io.credits.count) >= 1 ||
atomic_read(&sc->recv_io.credits.available) >= 1 ||
sc->status != SMBDIRECT_SOCKET_CONNECTED);
if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
ret = -ENOTCONN;
if (ret < 0)
goto credit_failed;
new_credits = manage_credits_prior_sending(sc);
}
data_length = 0;
for (i = 0; i < niov; i++)
data_length += iov[i].iov_len;
ret = smb_direct_create_header(sc, data_length, remaining_data_length,
&msg);
new_credits, &msg);
if (ret)
goto header_failed;
@ -1460,6 +1587,13 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc,
ret = post_sendmsg(sc, send_ctx, msg);
if (ret)
goto err;
if (send_ctx == &_send_ctx) {
ret = smb_direct_flush_send_list(sc, send_ctx, true);
if (ret)
goto err;
}
return 0;
err:
smb_direct_free_sendmsg(sc, msg);
@ -1468,6 +1602,9 @@ header_failed:
credit_failed:
atomic_inc(&sc->send_io.lcredits.count);
lcredit_failed:
atomic_add(send_ctx->credit, &sc->send_io.bcredits.count);
send_ctx->credit = 0;
bcredit_failed:
return ret;
}
@ -1939,6 +2076,7 @@ static int smb_direct_send_negotiate_response(struct smbdirect_socket *sc,
resp->max_fragmented_size =
cpu_to_le32(sp->max_fragmented_recv_size);
atomic_set(&sc->send_io.bcredits.count, 1);
sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
sc->status = SMBDIRECT_SOCKET_CONNECTED;
}
@ -2408,6 +2546,29 @@ static int smb_direct_prepare(struct ksmbd_transport *t)
le32_to_cpu(req->max_receive_size));
sp->max_fragmented_send_size =
le32_to_cpu(req->max_fragmented_size);
/*
* The maximum fragmented upper-layer payload receive size supported
*
* Assume max_payload_per_credit is
* smb_direct_receive_credit_max - 24 = 1340
*
* The maximum number would be
* smb_direct_receive_credit_max * max_payload_per_credit
*
* 1340 * 255 = 341700 (0x536C4)
*
* The minimum value from the spec is 131072 (0x20000)
*
* For now we use the logic we used before:
* (1364 * 255) / 2 = 173910 (0x2A756)
*
* We need to adjust this here in case the peer
* lowered sp->max_recv_size.
*
* TODO: instead of adjusting max_fragmented_recv_size
* we should adjust the number of available buffers,
* but for now we keep the current logic.
*/
sp->max_fragmented_recv_size =
(sp->recv_credit_max * sp->max_recv_size) / 2;
sc->recv_io.credits.target = le16_to_cpu(req->credits_requested);
@ -2495,6 +2656,7 @@ static bool rdma_frwr_is_supported(struct ib_device_attr *attrs)
static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id,
struct rdma_cm_event *event)
{
struct smb_direct_listener *listener = new_cm_id->context;
struct smb_direct_transport *t;
struct smbdirect_socket *sc;
struct smbdirect_socket_parameters *sp;
@ -2583,7 +2745,7 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id,
handler = kthread_run(ksmbd_conn_handler_loop,
KSMBD_TRANS(t)->conn, "ksmbd:r%u",
smb_direct_port);
listener->port);
if (IS_ERR(handler)) {
ret = PTR_ERR(handler);
pr_err("Can't start thread\n");
@ -2620,39 +2782,73 @@ static int smb_direct_listen_handler(struct rdma_cm_id *cm_id,
return 0;
}
static int smb_direct_listen(int port)
static int smb_direct_listen(struct smb_direct_listener *listener,
int port)
{
int ret;
struct rdma_cm_id *cm_id;
u8 node_type = RDMA_NODE_UNSPECIFIED;
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_ANY),
.sin_port = htons(port),
};
switch (port) {
case SMB_DIRECT_PORT_IWARP:
/*
* only allow iWarp devices
* for port 5445.
*/
node_type = RDMA_NODE_RNIC;
break;
case SMB_DIRECT_PORT_INFINIBAND:
/*
* only allow InfiniBand, RoCEv1 or RoCEv2
* devices for port 445.
*
* (Basically don't allow iWarp devices)
*/
node_type = RDMA_NODE_IB_CA;
break;
default:
pr_err("unsupported smbdirect port=%d!\n", port);
return -ENODEV;
}
cm_id = rdma_create_id(&init_net, smb_direct_listen_handler,
&smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC);
listener, RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(cm_id)) {
pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id));
return PTR_ERR(cm_id);
}
ret = rdma_restrict_node_type(cm_id, node_type);
if (ret) {
pr_err("rdma_restrict_node_type(%u) failed %d\n",
node_type, ret);
goto err;
}
ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin);
if (ret) {
pr_err("Can't bind: %d\n", ret);
goto err;
}
smb_direct_listener.cm_id = cm_id;
ret = rdma_listen(cm_id, 10);
if (ret) {
pr_err("Can't listen: %d\n", ret);
goto err;
}
listener->port = port;
listener->cm_id = cm_id;
return 0;
err:
smb_direct_listener.cm_id = NULL;
listener->port = 0;
listener->cm_id = NULL;
rdma_destroy_id(cm_id);
return ret;
}
@ -2661,10 +2857,6 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev)
{
struct smb_direct_device *smb_dev;
/* Set 5445 port if device type is iWARP(No IB) */
if (ib_dev->node_type != RDMA_NODE_IB_CA)
smb_direct_port = SMB_DIRECT_PORT_IWARP;
if (!rdma_frwr_is_supported(&ib_dev->attrs))
return 0;
@ -2707,8 +2899,9 @@ int ksmbd_rdma_init(void)
{
int ret;
smb_direct_port = SMB_DIRECT_PORT_INFINIBAND;
smb_direct_listener.cm_id = NULL;
smb_direct_ib_listener = smb_direct_iw_listener = (struct smb_direct_listener) {
.cm_id = NULL,
};
ret = ib_register_client(&smb_direct_ib_client);
if (ret) {
@ -2724,31 +2917,53 @@ int ksmbd_rdma_init(void)
smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq",
WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_PERCPU,
0);
if (!smb_direct_wq)
return -ENOMEM;
ret = smb_direct_listen(smb_direct_port);
if (ret) {
destroy_workqueue(smb_direct_wq);
smb_direct_wq = NULL;
pr_err("Can't listen: %d\n", ret);
return ret;
if (!smb_direct_wq) {
ret = -ENOMEM;
goto err;
}
ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n",
smb_direct_listener.cm_id);
ret = smb_direct_listen(&smb_direct_ib_listener,
SMB_DIRECT_PORT_INFINIBAND);
if (ret) {
pr_err("Can't listen on InfiniBand/RoCEv1/RoCEv2: %d\n", ret);
goto err;
}
ksmbd_debug(RDMA, "InfiniBand/RoCEv1/RoCEv2 RDMA listener. cm_id=%p\n",
smb_direct_ib_listener.cm_id);
ret = smb_direct_listen(&smb_direct_iw_listener,
SMB_DIRECT_PORT_IWARP);
if (ret) {
pr_err("Can't listen on iWarp: %d\n", ret);
goto err;
}
ksmbd_debug(RDMA, "iWarp RDMA listener. cm_id=%p\n",
smb_direct_iw_listener.cm_id);
return 0;
err:
ksmbd_rdma_stop_listening();
ksmbd_rdma_destroy();
return ret;
}
void ksmbd_rdma_stop_listening(void)
{
if (!smb_direct_listener.cm_id)
if (!smb_direct_ib_listener.cm_id && !smb_direct_iw_listener.cm_id)
return;
ib_unregister_client(&smb_direct_ib_client);
rdma_destroy_id(smb_direct_listener.cm_id);
smb_direct_listener.cm_id = NULL;
if (smb_direct_ib_listener.cm_id)
rdma_destroy_id(smb_direct_ib_listener.cm_id);
if (smb_direct_iw_listener.cm_id)
rdma_destroy_id(smb_direct_iw_listener.cm_id);
smb_direct_ib_listener = smb_direct_iw_listener = (struct smb_direct_listener) {
.cm_id = NULL,
};
}
void ksmbd_rdma_destroy(void)

View file

@ -40,6 +40,7 @@ static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
static void tcp_stop_kthread(struct task_struct *kthread);
static struct interface *alloc_iface(char *ifname);
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t);
#define KSMBD_TRANS(t) (&(t)->transport)
#define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \
@ -202,7 +203,7 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk)
if (IS_ERR(handler)) {
pr_err("cannot start conn thread\n");
rc = PTR_ERR(handler);
free_transport(t);
ksmbd_tcp_disconnect(KSMBD_TRANS(t));
}
return rc;
}

View file

@ -31,6 +31,7 @@
#include "ndr.h"
#include "auth.h"
#include "misc.h"
#include "stats.h"
#include "smb_common.h"
#include "mgmt/share_config.h"
@ -380,6 +381,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count,
}
filp->f_pos = *pos;
ksmbd_counter_add(KSMBD_COUNTER_READ_BYTES, (s64)nbytes);
return nbytes;
}
@ -517,6 +519,7 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
pr_err("fsync failed for filename = %pD, err = %d\n",
fp->filp, err);
}
ksmbd_counter_add(KSMBD_COUNTER_WRITE_BYTES, (s64)*written);
out:
return err;

View file

@ -16,10 +16,12 @@
#include "oplock.h"
#include "vfs.h"
#include "connection.h"
#include "misc.h"
#include "mgmt/tree_connect.h"
#include "mgmt/user_session.h"
#include "smb_common.h"
#include "server.h"
#include "smb2pdu.h"
#define S_DEL_PENDING 1
#define S_DEL_ON_CLS 2
@ -34,6 +36,97 @@ static struct ksmbd_file_table global_ft;
static atomic_long_t fd_limit;
static struct kmem_cache *filp_cache;
#define OPLOCK_NONE 0
#define OPLOCK_EXCLUSIVE 1
#define OPLOCK_BATCH 2
#define OPLOCK_READ 3 /* level 2 oplock */
#ifdef CONFIG_PROC_FS
static const struct ksmbd_const_name ksmbd_lease_const_names[] = {
{le32_to_cpu(SMB2_LEASE_NONE_LE), "LEASE_NONE"},
{le32_to_cpu(SMB2_LEASE_READ_CACHING_LE), "LEASE_R"},
{le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_H"},
{le32_to_cpu(SMB2_LEASE_WRITE_CACHING_LE), "LEASE_W"},
{le32_to_cpu(SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_RH"},
{le32_to_cpu(SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RW"},
{le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE |
SMB2_LEASE_WRITE_CACHING_LE), "LEASE_WH"},
{le32_to_cpu(SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE |
SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RWH"},
};
static const struct ksmbd_const_name ksmbd_oplock_const_names[] = {
{SMB2_OPLOCK_LEVEL_NONE, "OPLOCK_NONE"},
{SMB2_OPLOCK_LEVEL_II, "OPLOCK_II"},
{SMB2_OPLOCK_LEVEL_EXCLUSIVE, "OPLOCK_EXECL"},
{SMB2_OPLOCK_LEVEL_BATCH, "OPLOCK_BATCH"},
};
static int proc_show_files(struct seq_file *m, void *v)
{
struct ksmbd_file *fp = NULL;
unsigned int id;
struct oplock_info *opinfo;
seq_printf(m, "#%-10s %-10s %-10s %-10s %-15s %-10s %-10s %s\n",
"<tree id>", "<pid>", "<vid>", "<refcnt>",
"<oplock>", "<daccess>", "<saccess>",
"<name>");
read_lock(&global_ft.lock);
idr_for_each_entry(global_ft.idr, fp, id) {
seq_printf(m, "%#-10x %#-10llx %#-10llx %#-10x",
fp->tcon->id,
fp->persistent_id,
fp->volatile_id,
atomic_read(&fp->refcount));
rcu_read_lock();
opinfo = rcu_dereference(fp->f_opinfo);
rcu_read_unlock();
if (!opinfo) {
seq_printf(m, " %-15s", " ");
} else {
const struct ksmbd_const_name *const_names;
int count;
unsigned int level;
if (opinfo->is_lease) {
const_names = ksmbd_lease_const_names;
count = ARRAY_SIZE(ksmbd_lease_const_names);
level = le32_to_cpu(opinfo->o_lease->state);
} else {
const_names = ksmbd_oplock_const_names;
count = ARRAY_SIZE(ksmbd_oplock_const_names);
level = opinfo->level;
}
ksmbd_proc_show_const_name(m, " %-15s",
const_names, count, level);
}
seq_printf(m, " %#010x %#010x %s\n",
le32_to_cpu(fp->daccess),
le32_to_cpu(fp->saccess),
fp->filp->f_path.dentry->d_name.name);
}
read_unlock(&global_ft.lock);
return 0;
}
static int create_proc_files(void)
{
ksmbd_proc_create("files", proc_show_files, NULL);
return 0;
}
#else
static int create_proc_files(void) { return 0; }
#endif
static bool durable_scavenger_running;
static DEFINE_MUTEX(durable_scavenger_lock);
static wait_queue_head_t dh_wq;
@ -949,6 +1042,7 @@ void ksmbd_close_session_fds(struct ksmbd_work *work)
int ksmbd_init_global_file_table(void)
{
create_proc_files();
return ksmbd_init_file_table(&global_ft);
}

View file

@ -168,6 +168,23 @@ struct rdma_cm_id *rdma_create_user_id(rdma_cm_event_handler event_handler,
*/
void rdma_destroy_id(struct rdma_cm_id *id);
/**
* rdma_restrict_node_type - Restrict an RDMA identifier to specific
* RDMA device node type.
*
* @id: RDMA identifier.
* @node_type: The device node type. Only RDMA_NODE_UNSPECIFIED (default),
* RDMA_NODE_RNIC and RDMA_NODE_IB_CA are allowed
*
* This allows the caller to restrict the possible devices
* used to iWarp (RDMA_NODE_RNIC) or InfiniBand/RoCEv1/RoCEv2 (RDMA_NODE_IB_CA).
*
* It needs to be called before the RDMA identifier is bound
* to an device, which mean it should be called before
* rdma_bind_addr(), rdma_bind_addr() and rdma_listen().
*/
int rdma_restrict_node_type(struct rdma_cm_id *id, u8 node_type);
/**
* rdma_bind_addr - Bind an RDMA identifier to a source address and
* associated RDMA device, if needed.