mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
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:
commit
d53f4d93f3
25 changed files with 1485 additions and 203 deletions
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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
134
fs/smb/server/proc.c
Normal 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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
73
fs/smb/server/stats.h
Normal 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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue