| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| /* |
| * Copyright (c) 2025 Stefan Metzmacher |
| */ |
| |
| #ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_SOCKET_H__ |
| #define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_SOCKET_H__ |
| |
| #include <linux/wait.h> |
| #include <linux/workqueue.h> |
| #include <linux/kref.h> |
| #include <linux/mempool.h> |
| #include <linux/spinlock.h> |
| #include <linux/mutex.h> |
| #include <linux/completion.h> |
| #include <rdma/rw.h> |
| |
| enum smbdirect_socket_status { |
| SMBDIRECT_SOCKET_CREATED, |
| SMBDIRECT_SOCKET_LISTENING, |
| SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED, |
| SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING, |
| SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED, |
| SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED, |
| SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING, |
| SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED, |
| SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED, |
| SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING, |
| SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED, |
| SMBDIRECT_SOCKET_NEGOTIATE_NEEDED, |
| SMBDIRECT_SOCKET_NEGOTIATE_RUNNING, |
| SMBDIRECT_SOCKET_NEGOTIATE_FAILED, |
| SMBDIRECT_SOCKET_CONNECTED, |
| SMBDIRECT_SOCKET_ERROR, |
| SMBDIRECT_SOCKET_DISCONNECTING, |
| SMBDIRECT_SOCKET_DISCONNECTED, |
| SMBDIRECT_SOCKET_DESTROYED |
| }; |
| |
| static __always_inline |
| const char *smbdirect_socket_status_string(enum smbdirect_socket_status status) |
| { |
| switch (status) { |
| case SMBDIRECT_SOCKET_CREATED: |
| return "CREATED"; |
| case SMBDIRECT_SOCKET_LISTENING: |
| return "LISTENING"; |
| case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED: |
| return "RESOLVE_ADDR_NEEDED"; |
| case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING: |
| return "RESOLVE_ADDR_RUNNING"; |
| case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED: |
| return "RESOLVE_ADDR_FAILED"; |
| case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED: |
| return "RESOLVE_ROUTE_NEEDED"; |
| case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING: |
| return "RESOLVE_ROUTE_RUNNING"; |
| case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED: |
| return "RESOLVE_ROUTE_FAILED"; |
| case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED: |
| return "RDMA_CONNECT_NEEDED"; |
| case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING: |
| return "RDMA_CONNECT_RUNNING"; |
| case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED: |
| return "RDMA_CONNECT_FAILED"; |
| case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED: |
| return "NEGOTIATE_NEEDED"; |
| case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING: |
| return "NEGOTIATE_RUNNING"; |
| case SMBDIRECT_SOCKET_NEGOTIATE_FAILED: |
| return "NEGOTIATE_FAILED"; |
| case SMBDIRECT_SOCKET_CONNECTED: |
| return "CONNECTED"; |
| case SMBDIRECT_SOCKET_ERROR: |
| return "ERROR"; |
| case SMBDIRECT_SOCKET_DISCONNECTING: |
| return "DISCONNECTING"; |
| case SMBDIRECT_SOCKET_DISCONNECTED: |
| return "DISCONNECTED"; |
| case SMBDIRECT_SOCKET_DESTROYED: |
| return "DESTROYED"; |
| } |
| |
| return "<unknown>"; |
| } |
| |
| /* |
| * This can be used with %1pe to print errors as strings or '0' |
| * And it avoids warnings like: warn: passing zero to 'ERR_PTR' |
| * from smatch -p=kernel --pedantic |
| */ |
| static __always_inline |
| const void * __must_check SMBDIRECT_DEBUG_ERR_PTR(long error) |
| { |
| if (error == 0) |
| return NULL; |
| return ERR_PTR(error); |
| } |
| |
| enum smbdirect_keepalive_status { |
| SMBDIRECT_KEEPALIVE_NONE, |
| SMBDIRECT_KEEPALIVE_PENDING, |
| SMBDIRECT_KEEPALIVE_SENT |
| }; |
| |
| struct smbdirect_socket { |
| enum smbdirect_socket_status status; |
| wait_queue_head_t status_wait; |
| int first_error; |
| |
| /* |
| * This points to the workqueues to |
| * be used for this socket. |
| */ |
| struct { |
| struct workqueue_struct *accept; |
| struct workqueue_struct *connect; |
| struct workqueue_struct *idle; |
| struct workqueue_struct *refill; |
| struct workqueue_struct *immediate; |
| struct workqueue_struct *cleanup; |
| } workqueues; |
| |
| struct work_struct disconnect_work; |
| |
| /* |
| * The reference counts. |
| */ |
| struct { |
| /* |
| * This holds the references by the |
| * frontend, typically the smb layer. |
| * |
| * It is typically 1 and a disconnect |
| * will happen if it reaches 0. |
| */ |
| struct kref disconnect; |
| |
| /* |
| * This holds the reference by the |
| * backend, the code that manages |
| * the lifetime of the whole |
| * struct smbdirect_socket, |
| * if this reaches 0 it can will |
| * be freed. |
| * |
| * Can be REFCOUNT_MAX is part |
| * of another structure. |
| * |
| * This is equal or higher than |
| * the disconnect refcount. |
| */ |
| struct kref destroy; |
| } refs; |
| |
| /* RDMA related */ |
| struct { |
| struct rdma_cm_id *cm_id; |
| /* |
| * The expected event in our current |
| * cm_id->event_handler, all other events |
| * are treated as an error. |
| */ |
| enum rdma_cm_event_type expected_event; |
| /* |
| * This is for iWarp MPA v1 |
| */ |
| bool legacy_iwarp; |
| } rdma; |
| |
| /* IB verbs related */ |
| struct { |
| struct ib_pd *pd; |
| enum ib_poll_context poll_ctx; |
| struct ib_cq *send_cq; |
| struct ib_cq *recv_cq; |
| |
| /* |
| * shortcuts for rdma.cm_id->{qp,device}; |
| */ |
| struct ib_qp *qp; |
| struct ib_device *dev; |
| } ib; |
| |
| struct smbdirect_socket_parameters parameters; |
| |
| /* |
| * The state for connect/negotiation |
| */ |
| struct { |
| spinlock_t lock; |
| struct work_struct work; |
| } connect; |
| |
| /* |
| * The state for keepalive and timeout handling |
| */ |
| struct { |
| enum smbdirect_keepalive_status keepalive; |
| struct work_struct immediate_work; |
| struct delayed_work timer_work; |
| } idle; |
| |
| /* |
| * The state for listen sockets |
| */ |
| struct { |
| spinlock_t lock; |
| struct list_head pending; |
| struct list_head ready; |
| wait_queue_head_t wait_queue; |
| /* |
| * This starts as -1 and a value != -1 |
| * means this socket was in LISTENING state |
| * before. Note the valid backlog can |
| * only be > 0. |
| */ |
| int backlog; |
| } listen; |
| |
| /* |
| * The state for sockets waiting |
| * for accept, either still waiting |
| * for the negotiation to finish |
| * or already ready with a usable |
| * connection. |
| */ |
| struct { |
| struct smbdirect_socket *listener; |
| struct list_head list; |
| } accept; |
| |
| /* |
| * The state for posted send buffers |
| */ |
| struct { |
| /* |
| * Memory pools for preallocating |
| * smbdirect_send_io buffers |
| */ |
| struct { |
| struct kmem_cache *cache; |
| mempool_t *pool; |
| gfp_t gfp_mask; |
| } 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() |
| */ |
| struct { |
| atomic_t count; |
| wait_queue_head_t wait_queue; |
| } lcredits; |
| |
| /* |
| * The remote credit state for the send side |
| */ |
| struct { |
| atomic_t count; |
| wait_queue_head_t wait_queue; |
| } credits; |
| |
| /* |
| * The state about posted/pending sends |
| */ |
| struct { |
| atomic_t count; |
| /* |
| * woken when count reached zero |
| */ |
| wait_queue_head_t zero_wait_queue; |
| } pending; |
| } send_io; |
| |
| /* |
| * The state for posted receive buffers |
| */ |
| struct { |
| /* |
| * The type of PDU we are expecting |
| */ |
| enum { |
| SMBDIRECT_EXPECT_NEGOTIATE_REQ = 1, |
| SMBDIRECT_EXPECT_NEGOTIATE_REP = 2, |
| SMBDIRECT_EXPECT_DATA_TRANSFER = 3, |
| } expected; |
| |
| /* |
| * Memory pools for preallocating |
| * smbdirect_recv_io buffers |
| */ |
| struct { |
| struct kmem_cache *cache; |
| mempool_t *pool; |
| gfp_t gfp_mask; |
| } mem; |
| |
| /* |
| * The list of free smbdirect_recv_io |
| * structures |
| */ |
| struct { |
| struct list_head list; |
| spinlock_t lock; |
| } free; |
| |
| /* |
| * The state for posted recv_io messages |
| * and the refill work struct. |
| */ |
| struct { |
| atomic_t count; |
| struct work_struct refill_work; |
| } posted; |
| |
| /* |
| * The credit state for the recv side |
| */ |
| struct { |
| u16 target; |
| atomic_t available; |
| atomic_t count; |
| } credits; |
| |
| /* |
| * The list of arrived non-empty smbdirect_recv_io |
| * structures |
| * |
| * This represents the reassembly queue. |
| */ |
| struct { |
| struct list_head list; |
| spinlock_t lock; |
| wait_queue_head_t wait_queue; |
| /* total data length of reassembly queue */ |
| int data_length; |
| int queue_length; |
| /* the offset to first buffer in reassembly queue */ |
| int first_entry_offset; |
| /* |
| * Indicate if we have received a full packet on the |
| * connection This is used to identify the first SMBD |
| * packet of a assembled payload (SMB packet) in |
| * reassembly queue so we can return a RFC1002 length to |
| * upper layer to indicate the length of the SMB packet |
| * received |
| */ |
| bool full_packet_received; |
| } reassembly; |
| } recv_io; |
| |
| /* |
| * The state for Memory registrations on the client |
| */ |
| struct { |
| enum ib_mr_type type; |
| |
| /* |
| * The list of free smbdirect_mr_io |
| * structures |
| */ |
| struct { |
| struct list_head list; |
| spinlock_t lock; |
| } all; |
| |
| /* |
| * The number of available MRs ready for memory registration |
| */ |
| struct { |
| atomic_t count; |
| wait_queue_head_t wait_queue; |
| } ready; |
| |
| /* |
| * The number of used MRs |
| */ |
| struct { |
| atomic_t count; |
| } used; |
| } mr_io; |
| |
| /* |
| * The state for RDMA read/write requests on the server |
| */ |
| struct { |
| /* |
| * Memory hints for |
| * smbdirect_rw_io structs |
| */ |
| struct { |
| gfp_t gfp_mask; |
| } mem; |
| |
| /* |
| * The credit state for the send side |
| */ |
| struct { |
| /* |
| * The maximum number of rw credits |
| */ |
| size_t max; |
| /* |
| * The number of pages per credit |
| */ |
| size_t num_pages; |
| atomic_t count; |
| wait_queue_head_t wait_queue; |
| } credits; |
| } rw_io; |
| |
| /* |
| * For debug purposes |
| */ |
| struct { |
| u64 get_receive_buffer; |
| u64 put_receive_buffer; |
| u64 enqueue_reassembly_queue; |
| u64 dequeue_reassembly_queue; |
| u64 send_empty; |
| } statistics; |
| |
| struct { |
| void *private_ptr; |
| bool (*needed)(struct smbdirect_socket *sc, |
| void *private_ptr, |
| unsigned int lvl, |
| unsigned int cls); |
| void (*vaprintf)(struct smbdirect_socket *sc, |
| const char *func, |
| unsigned int line, |
| void *private_ptr, |
| unsigned int lvl, |
| unsigned int cls, |
| struct va_format *vaf); |
| } logging; |
| }; |
| |
| static void __smbdirect_socket_disabled_work(struct work_struct *work) |
| { |
| /* |
| * Should never be called as disable_[delayed_]work_sync() was used. |
| */ |
| WARN_ON_ONCE(1); |
| } |
| |
| static bool __smbdirect_log_needed(struct smbdirect_socket *sc, |
| void *private_ptr, |
| unsigned int lvl, |
| unsigned int cls) |
| { |
| /* |
| * Should never be called, the caller should |
| * set it's own functions. |
| */ |
| WARN_ON_ONCE(1); |
| return false; |
| } |
| |
| static void __smbdirect_log_vaprintf(struct smbdirect_socket *sc, |
| const char *func, |
| unsigned int line, |
| void *private_ptr, |
| unsigned int lvl, |
| unsigned int cls, |
| struct va_format *vaf) |
| { |
| /* |
| * Should never be called, the caller should |
| * set it's own functions. |
| */ |
| WARN_ON_ONCE(1); |
| } |
| |
| __printf(6, 7) |
| static void __smbdirect_log_printf(struct smbdirect_socket *sc, |
| const char *func, |
| unsigned int line, |
| unsigned int lvl, |
| unsigned int cls, |
| const char *fmt, |
| ...); |
| __maybe_unused |
| static void __smbdirect_log_printf(struct smbdirect_socket *sc, |
| const char *func, |
| unsigned int line, |
| unsigned int lvl, |
| unsigned int cls, |
| const char *fmt, |
| ...) |
| { |
| struct va_format vaf; |
| va_list args; |
| |
| va_start(args, fmt); |
| |
| vaf.fmt = fmt; |
| vaf.va = &args; |
| |
| sc->logging.vaprintf(sc, |
| func, |
| line, |
| sc->logging.private_ptr, |
| lvl, |
| cls, |
| &vaf); |
| va_end(args); |
| } |
| |
| #define ___smbdirect_log_generic(sc, func, line, lvl, cls, fmt, args...) do { \ |
| if (sc->logging.needed(sc, sc->logging.private_ptr, lvl, cls)) { \ |
| __smbdirect_log_printf(sc, func, line, lvl, cls, fmt, ##args); \ |
| } \ |
| } while (0) |
| #define __smbdirect_log_generic(sc, lvl, cls, fmt, args...) \ |
| ___smbdirect_log_generic(sc, __func__, __LINE__, lvl, cls, fmt, ##args) |
| |
| #define smbdirect_log_outgoing(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_OUTGOING, fmt, ##args) |
| #define smbdirect_log_incoming(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_INCOMING, fmt, ##args) |
| #define smbdirect_log_read(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_READ, fmt, ##args) |
| #define smbdirect_log_write(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_WRITE, fmt, ##args) |
| #define smbdirect_log_rdma_send(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_SEND, fmt, ##args) |
| #define smbdirect_log_rdma_recv(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_RECV, fmt, ##args) |
| #define smbdirect_log_keep_alive(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_KEEP_ALIVE, fmt, ##args) |
| #define smbdirect_log_rdma_event(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_EVENT, fmt, ##args) |
| #define smbdirect_log_rdma_mr(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_MR, fmt, ##args) |
| #define smbdirect_log_rdma_rw(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_RW, fmt, ##args) |
| #define smbdirect_log_negotiate(sc, lvl, fmt, args...) \ |
| __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_NEGOTIATE, fmt, ##args) |
| |
| static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc) |
| { |
| /* |
| * This also sets status = SMBDIRECT_SOCKET_CREATED |
| */ |
| BUILD_BUG_ON(SMBDIRECT_SOCKET_CREATED != 0); |
| memset(sc, 0, sizeof(*sc)); |
| |
| init_waitqueue_head(&sc->status_wait); |
| |
| sc->workqueues.accept = smbdirect_globals.workqueues.accept; |
| sc->workqueues.connect = smbdirect_globals.workqueues.connect; |
| sc->workqueues.idle = smbdirect_globals.workqueues.idle; |
| sc->workqueues.refill = smbdirect_globals.workqueues.refill; |
| sc->workqueues.immediate = smbdirect_globals.workqueues.immediate; |
| sc->workqueues.cleanup = smbdirect_globals.workqueues.cleanup; |
| |
| INIT_WORK(&sc->disconnect_work, __smbdirect_socket_disabled_work); |
| disable_work_sync(&sc->disconnect_work); |
| |
| kref_init(&sc->refs.disconnect); |
| sc->refs.destroy = (struct kref) KREF_INIT(REFCOUNT_MAX); |
| |
| sc->rdma.expected_event = RDMA_CM_EVENT_INTERNAL; |
| |
| sc->ib.poll_ctx = IB_POLL_UNBOUND_WORKQUEUE; |
| |
| spin_lock_init(&sc->connect.lock); |
| INIT_WORK(&sc->connect.work, __smbdirect_socket_disabled_work); |
| disable_work_sync(&sc->connect.work); |
| |
| INIT_WORK(&sc->idle.immediate_work, __smbdirect_socket_disabled_work); |
| disable_work_sync(&sc->idle.immediate_work); |
| INIT_DELAYED_WORK(&sc->idle.timer_work, __smbdirect_socket_disabled_work); |
| disable_delayed_work_sync(&sc->idle.timer_work); |
| |
| spin_lock_init(&sc->listen.lock); |
| INIT_LIST_HEAD(&sc->listen.pending); |
| INIT_LIST_HEAD(&sc->listen.ready); |
| sc->listen.backlog = -1; /* not a listener */ |
| init_waitqueue_head(&sc->listen.wait_queue); |
| |
| INIT_LIST_HEAD(&sc->accept.list); |
| |
| sc->send_io.mem.gfp_mask = GFP_KERNEL; |
| |
| 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); |
| |
| atomic_set(&sc->send_io.credits.count, 0); |
| init_waitqueue_head(&sc->send_io.credits.wait_queue); |
| |
| atomic_set(&sc->send_io.pending.count, 0); |
| init_waitqueue_head(&sc->send_io.pending.zero_wait_queue); |
| |
| sc->recv_io.mem.gfp_mask = GFP_KERNEL; |
| |
| INIT_LIST_HEAD(&sc->recv_io.free.list); |
| spin_lock_init(&sc->recv_io.free.lock); |
| |
| atomic_set(&sc->recv_io.posted.count, 0); |
| 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); |
| spin_lock_init(&sc->recv_io.reassembly.lock); |
| init_waitqueue_head(&sc->recv_io.reassembly.wait_queue); |
| |
| sc->rw_io.mem.gfp_mask = GFP_KERNEL; |
| atomic_set(&sc->rw_io.credits.count, 0); |
| init_waitqueue_head(&sc->rw_io.credits.wait_queue); |
| |
| spin_lock_init(&sc->mr_io.all.lock); |
| INIT_LIST_HEAD(&sc->mr_io.all.list); |
| atomic_set(&sc->mr_io.ready.count, 0); |
| init_waitqueue_head(&sc->mr_io.ready.wait_queue); |
| atomic_set(&sc->mr_io.used.count, 0); |
| |
| sc->logging.private_ptr = NULL; |
| sc->logging.needed = __smbdirect_log_needed; |
| sc->logging.vaprintf = __smbdirect_log_vaprintf; |
| } |
| |
| #define __SMBDIRECT_CHECK_STATUS_FAILED(__sc, __expected_status, __error_cmd, __unexpected_cmd) ({ \ |
| bool __failed = false; \ |
| if (unlikely((__sc)->first_error)) { \ |
| __failed = true; \ |
| __error_cmd \ |
| } else if (unlikely((__sc)->status != (__expected_status))) { \ |
| __failed = true; \ |
| __unexpected_cmd \ |
| } \ |
| __failed; \ |
| }) |
| |
| #define __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, __unexpected_cmd) \ |
| __SMBDIRECT_CHECK_STATUS_FAILED(__sc, __expected_status, \ |
| { \ |
| const struct sockaddr_storage *__src = NULL; \ |
| const struct sockaddr_storage *__dst = NULL; \ |
| if ((__sc)->rdma.cm_id) { \ |
| __src = &(__sc)->rdma.cm_id->route.addr.src_addr; \ |
| __dst = &(__sc)->rdma.cm_id->route.addr.dst_addr; \ |
| } \ |
| smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO, \ |
| "expected[%s] != %s first_error=%1pe local=%pISpsfc remote=%pISpsfc\n", \ |
| smbdirect_socket_status_string(__expected_status), \ |
| smbdirect_socket_status_string((__sc)->status), \ |
| SMBDIRECT_DEBUG_ERR_PTR((__sc)->first_error), \ |
| __src, __dst); \ |
| }, \ |
| { \ |
| const struct sockaddr_storage *__src = NULL; \ |
| const struct sockaddr_storage *__dst = NULL; \ |
| if ((__sc)->rdma.cm_id) { \ |
| __src = &(__sc)->rdma.cm_id->route.addr.src_addr; \ |
| __dst = &(__sc)->rdma.cm_id->route.addr.dst_addr; \ |
| } \ |
| smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR, \ |
| "expected[%s] != %s first_error=%1pe local=%pISpsfc remote=%pISpsfc\n", \ |
| smbdirect_socket_status_string(__expected_status), \ |
| smbdirect_socket_status_string((__sc)->status), \ |
| SMBDIRECT_DEBUG_ERR_PTR((__sc)->first_error), \ |
| __src, __dst); \ |
| WARN_ONCE(1, \ |
| "expected[%s] != %s first_error=%1pe local=%pISpsfc remote=%pISpsfc\n", \ |
| smbdirect_socket_status_string(__expected_status), \ |
| smbdirect_socket_status_string((__sc)->status), \ |
| SMBDIRECT_DEBUG_ERR_PTR((__sc)->first_error), \ |
| __src, __dst); \ |
| __unexpected_cmd \ |
| }) |
| |
| #define SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status) \ |
| __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, /* nothing */) |
| |
| #ifndef __SMBDIRECT_SOCKET_DISCONNECT |
| #define __SMBDIRECT_SOCKET_DISCONNECT(__sc) \ |
| smbdirect_socket_schedule_cleanup(__sc, -ECONNABORTED) |
| #endif /* ! __SMBDIRECT_SOCKET_DISCONNECT */ |
| |
| #define SMBDIRECT_CHECK_STATUS_DISCONNECT(__sc, __expected_status) \ |
| __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, \ |
| __SMBDIRECT_SOCKET_DISCONNECT(__sc);) |
| |
| struct smbdirect_send_io { |
| struct smbdirect_socket *socket; |
| struct ib_cqe cqe; |
| |
| /* |
| * The SGE entries for this work request |
| * |
| * The first points to the packet header |
| */ |
| #define SMBDIRECT_SEND_IO_MAX_SGE 6 |
| size_t num_sge; |
| struct ib_sge sge[SMBDIRECT_SEND_IO_MAX_SGE]; |
| |
| /* |
| * Link to the list of sibling smbdirect_send_io |
| * messages. |
| */ |
| struct list_head sibling_list; |
| struct ib_send_wr wr; |
| |
| /* SMBD packet header follows this structure */ |
| u8 packet[]; |
| }; |
| |
| struct smbdirect_send_batch { |
| /* |
| * List of smbdirect_send_io messages |
| */ |
| struct list_head msg_list; |
| /* |
| * Number of list entries |
| */ |
| size_t wr_cnt; |
| |
| /* |
| * Possible remote key invalidation state |
| */ |
| bool need_invalidate_rkey; |
| u32 remote_key; |
| |
| int credit; |
| }; |
| |
| struct smbdirect_recv_io { |
| struct smbdirect_socket *socket; |
| struct ib_cqe cqe; |
| |
| /* |
| * For now we only use a single SGE |
| * as we have just one large buffer |
| * per posted recv. |
| */ |
| #define SMBDIRECT_RECV_IO_MAX_SGE 1 |
| struct ib_sge sge; |
| |
| /* Link to free or reassembly list */ |
| struct list_head list; |
| |
| /* Indicate if this is the 1st packet of a payload */ |
| bool first_segment; |
| |
| /* SMBD packet header and payload follows this structure */ |
| u8 packet[]; |
| }; |
| |
| enum smbdirect_mr_state { |
| SMBDIRECT_MR_READY, |
| SMBDIRECT_MR_REGISTERED, |
| SMBDIRECT_MR_INVALIDATED, |
| SMBDIRECT_MR_ERROR, |
| SMBDIRECT_MR_DISABLED |
| }; |
| |
| struct smbdirect_mr_io { |
| struct smbdirect_socket *socket; |
| struct ib_cqe cqe; |
| |
| /* |
| * We can have up to two references: |
| * 1. by the connection |
| * 2. by the registration |
| */ |
| struct kref kref; |
| struct mutex mutex; |
| |
| struct list_head list; |
| |
| enum smbdirect_mr_state state; |
| struct ib_mr *mr; |
| struct sg_table sgt; |
| enum dma_data_direction dir; |
| union { |
| struct ib_reg_wr wr; |
| struct ib_send_wr inv_wr; |
| }; |
| |
| bool need_invalidate; |
| struct completion invalidate_done; |
| }; |
| |
| struct smbdirect_rw_io { |
| struct smbdirect_socket *socket; |
| struct ib_cqe cqe; |
| |
| struct list_head list; |
| |
| int error; |
| struct completion *completion; |
| |
| struct rdma_rw_ctx rdma_ctx; |
| struct sg_table sgt; |
| struct scatterlist sg_list[]; |
| }; |
| |
| static inline size_t smbdirect_get_buf_page_count(const void *buf, size_t size) |
| { |
| return DIV_ROUND_UP((uintptr_t)buf + size, PAGE_SIZE) - |
| (uintptr_t)buf / PAGE_SIZE; |
| } |
| |
| /* |
| * Maximum number of retries on data transfer operations |
| */ |
| #define SMBDIRECT_RDMA_CM_RETRY 6 |
| /* |
| * No need to retry on Receiver Not Ready since SMB_DIRECT manages credits |
| */ |
| #define SMBDIRECT_RDMA_CM_RNR_RETRY 0 |
| |
| #endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_SOCKET_H__ */ |