blob: c09eddd8ad16d58fb3205b365e4e0ed7f7b51a25 [file]
/* 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__ */