| // SPDX-License-Identifier: GPL-2.0-or-later | |
| /* | |
| * Copyright (C) 2017, Microsoft Corporation. | |
| * | |
| * Author(s): Long Li <longli@microsoft.com> | |
| */ | |
| #include "smbdirect.h" | |
| #include "cifs_debug.h" | |
| #include "cifsproto.h" | |
| #include "smb2proto.h" | |
| /* Port numbers for SMBD transport */ | |
| #define SMB_PORT 445 | |
| #define SMBD_PORT 5445 | |
| /* Address lookup and resolve timeout in ms */ | |
| #define RDMA_RESOLVE_TIMEOUT 5000 | |
| /* SMBD negotiation timeout in seconds */ | |
| #define SMBD_NEGOTIATE_TIMEOUT 120 | |
| /* The timeout to wait for a keepalive message from peer in seconds */ | |
| #define KEEPALIVE_RECV_TIMEOUT 5 | |
| /* | |
| * Default maximum number of RDMA read/write outstanding on this connection | |
| * This value is possibly decreased during QP creation on hardware limit | |
| */ | |
| #define SMBD_CM_RESPONDER_RESOURCES 32 | |
| /* | |
| * User configurable initial values per SMBD transport connection | |
| * as defined in [MS-SMBD] 3.1.1.1 | |
| * Those may change after a SMBD negotiation | |
| */ | |
| /* The local peer's maximum number of credits to grant to the peer */ | |
| int smbd_receive_credit_max = 255; | |
| /* The remote peer's credit request of local peer */ | |
| 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 | |
| * | |
| * 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; | |
| /* The timeout to initiate send of a keepalive message on idle */ | |
| int smbd_keep_alive_interval = 120; | |
| /* | |
| * User configurable initial values for RDMA transport | |
| * The actual values used may be lower and are limited to hardware capabilities | |
| */ | |
| /* Default maximum number of pages in a single RDMA write/read */ | |
| int smbd_max_frmr_depth = 2048; | |
| /* If payload is less than this byte, use RDMA send/recv not read/write */ | |
| int rdma_readwrite_threshold = 4096; | |
| /* Transport logging functions | |
| * Logging are defined as classes. They can be OR'ed to define the actual | |
| * logging level via module parameter smbd_logging_class | |
| * e.g. cifs.smbd_logging_class=0xa0 will log all log_rdma_recv() and | |
| * log_rdma_event() | |
| */ | |
| #define LOG_OUTGOING 0x1 | |
| #define LOG_INCOMING 0x2 | |
| #define LOG_READ 0x4 | |
| #define LOG_WRITE 0x8 | |
| #define LOG_RDMA_SEND 0x10 | |
| #define LOG_RDMA_RECV 0x20 | |
| #define LOG_KEEP_ALIVE 0x40 | |
| #define LOG_RDMA_EVENT 0x80 | |
| #define LOG_RDMA_MR 0x100 | |
| static unsigned int smbd_logging_class; | |
| module_param(smbd_logging_class, uint, 0644); | |
| MODULE_PARM_DESC(smbd_logging_class, | |
| "Logging class for SMBD transport 0x0 to 0x100"); | |
| #define ERR 0x0 | |
| #define INFO 0x1 | |
| static unsigned int smbd_logging_level = ERR; | |
| module_param(smbd_logging_level, uint, 0644); | |
| MODULE_PARM_DESC(smbd_logging_level, | |
| "Logging level for SMBD transport, 0 (default): error, 1: info"); | |
| static bool smbd_logging_needed(struct smbdirect_socket *sc, | |
| void *private_ptr, | |
| unsigned int lvl, | |
| unsigned int cls) | |
| { | |
| #define BUILD_BUG_SAME(x) BUILD_BUG_ON(x != SMBDIRECT_LOG_ ##x) | |
| BUILD_BUG_SAME(ERR); | |
| BUILD_BUG_SAME(INFO); | |
| #undef BUILD_BUG_SAME | |
| #define BUILD_BUG_SAME(x) BUILD_BUG_ON(x != SMBDIRECT_ ##x) | |
| BUILD_BUG_SAME(LOG_OUTGOING); | |
| BUILD_BUG_SAME(LOG_INCOMING); | |
| BUILD_BUG_SAME(LOG_READ); | |
| BUILD_BUG_SAME(LOG_WRITE); | |
| BUILD_BUG_SAME(LOG_RDMA_SEND); | |
| BUILD_BUG_SAME(LOG_RDMA_RECV); | |
| BUILD_BUG_SAME(LOG_KEEP_ALIVE); | |
| BUILD_BUG_SAME(LOG_RDMA_EVENT); | |
| BUILD_BUG_SAME(LOG_RDMA_MR); | |
| #undef BUILD_BUG_SAME | |
| if (lvl <= smbd_logging_level || cls & smbd_logging_class) | |
| return true; | |
| return false; | |
| } | |
| static void smbd_logging_vaprintf(struct smbdirect_socket *sc, | |
| const char *func, | |
| unsigned int line, | |
| void *private_ptr, | |
| unsigned int lvl, | |
| unsigned int cls, | |
| struct va_format *vaf) | |
| { | |
| cifs_dbg(VFS, "%s:%u %pV", func, line, vaf); | |
| } | |
| #define log_rdma(level, class, fmt, args...) \ | |
| do { \ | |
| if (level <= smbd_logging_level || class & smbd_logging_class) \ | |
| cifs_dbg(VFS, "%s:%d " fmt, __func__, __LINE__, ##args);\ | |
| } while (0) | |
| #define log_outgoing(level, fmt, args...) \ | |
| log_rdma(level, LOG_OUTGOING, fmt, ##args) | |
| #define log_incoming(level, fmt, args...) \ | |
| log_rdma(level, LOG_INCOMING, fmt, ##args) | |
| #define log_read(level, fmt, args...) log_rdma(level, LOG_READ, fmt, ##args) | |
| #define log_write(level, fmt, args...) log_rdma(level, LOG_WRITE, fmt, ##args) | |
| #define log_rdma_send(level, fmt, args...) \ | |
| log_rdma(level, LOG_RDMA_SEND, fmt, ##args) | |
| #define log_rdma_recv(level, fmt, args...) \ | |
| log_rdma(level, LOG_RDMA_RECV, fmt, ##args) | |
| #define log_keep_alive(level, fmt, args...) \ | |
| log_rdma(level, LOG_KEEP_ALIVE, fmt, ##args) | |
| #define log_rdma_event(level, fmt, args...) \ | |
| log_rdma(level, LOG_RDMA_EVENT, fmt, ##args) | |
| #define log_rdma_mr(level, fmt, args...) \ | |
| log_rdma(level, LOG_RDMA_MR, fmt, ##args) | |
| static int smbd_post_send_full_iter(struct smbdirect_socket *sc, | |
| struct smbdirect_send_batch *batch, | |
| struct iov_iter *iter, | |
| u32 remaining_data_length) | |
| { | |
| int bytes = 0; | |
| /* | |
| * smbdirect_connection_send_single_iter() respects the | |
| * negotiated max_send_size, so we need to | |
| * loop until the full iter is posted | |
| */ | |
| while (iov_iter_count(iter) > 0) { | |
| int rc; | |
| rc = smbdirect_connection_send_single_iter(sc, | |
| batch, | |
| iter, | |
| 0, /* flags */ | |
| remaining_data_length); | |
| if (rc < 0) | |
| return rc; | |
| remaining_data_length -= rc; | |
| bytes += rc; | |
| } | |
| return bytes; | |
| } | |
| /* | |
| * Destroy the transport and related RDMA and memory resources | |
| * Need to go through all the pending counters and make sure on one is using | |
| * the transport while it is destroyed | |
| */ | |
| void smbd_destroy(struct TCP_Server_Info *server) | |
| { | |
| struct smbd_connection *info = server->smbd_conn; | |
| if (!info) { | |
| log_rdma_event(INFO, "rdma session already destroyed\n"); | |
| return; | |
| } | |
| smbdirect_socket_release(info->socket); | |
| kfree(info); | |
| server->smbd_conn = NULL; | |
| } | |
| /* | |
| * Reconnect this SMBD connection, called from upper layer | |
| * return value: 0 on success, or actual error code | |
| */ | |
| int smbd_reconnect(struct TCP_Server_Info *server) | |
| { | |
| log_rdma_event(INFO, "reconnecting rdma session\n"); | |
| if (!server->smbd_conn) { | |
| log_rdma_event(INFO, "rdma session already destroyed\n"); | |
| goto create_conn; | |
| } | |
| /* | |
| * This is possible if transport is disconnected and we haven't received | |
| * notification from RDMA, but upper layer has detected timeout | |
| */ | |
| log_rdma_event(INFO, "disconnecting transport\n"); | |
| smbd_destroy(server); | |
| create_conn: | |
| log_rdma_event(INFO, "creating rdma session\n"); | |
| server->smbd_conn = smbd_get_connection( | |
| server, (struct sockaddr *) &server->dstaddr); | |
| if (server->smbd_conn) { | |
| cifs_dbg(VFS, "RDMA transport re-established\n"); | |
| trace_smb3_smbd_connect_done(server->hostname, server->conn_id, &server->dstaddr); | |
| return 0; | |
| } | |
| trace_smb3_smbd_connect_err(server->hostname, server->conn_id, &server->dstaddr); | |
| return -ENOENT; | |
| } | |
| /* Create a SMBD connection, called by upper layer */ | |
| static struct smbd_connection *_smbd_get_connection( | |
| struct TCP_Server_Info *server, struct sockaddr *dstaddr, int port) | |
| { | |
| struct net *net = cifs_net_ns(server); | |
| struct smbd_connection *info; | |
| struct smbdirect_socket *sc; | |
| struct smbdirect_socket_parameters init_params = {}; | |
| struct smbdirect_socket_parameters *sp; | |
| __be16 *sport; | |
| u64 port_flags = 0; | |
| int ret; | |
| switch (port) { | |
| case SMBD_PORT: | |
| /* | |
| * only allow iWarp devices | |
| * for port 5445. | |
| */ | |
| port_flags |= SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW; | |
| break; | |
| case SMB_PORT: | |
| /* | |
| * only allow InfiniBand, RoCEv1 or RoCEv2 | |
| * devices for port 445. | |
| * | |
| * (Basically don't allow iWarp devices) | |
| */ | |
| port_flags |= SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB; | |
| break; | |
| } | |
| /* | |
| * Create the initial parameters | |
| */ | |
| sp = &init_params; | |
| sp->flags = port_flags; | |
| sp->resolve_addr_timeout_msec = RDMA_RESOLVE_TIMEOUT; | |
| sp->resolve_route_timeout_msec = RDMA_RESOLVE_TIMEOUT; | |
| sp->rdma_connect_timeout_msec = RDMA_RESOLVE_TIMEOUT; | |
| sp->negotiate_timeout_msec = SMBD_NEGOTIATE_TIMEOUT * 1000; | |
| sp->initiator_depth = 1; | |
| sp->responder_resources = SMBD_CM_RESPONDER_RESOURCES; | |
| sp->recv_credit_max = smbd_receive_credit_max; | |
| sp->send_credit_target = smbd_send_credit_target; | |
| sp->max_send_size = smbd_max_send_size; | |
| sp->max_fragmented_recv_size = smbd_max_fragmented_recv_size; | |
| sp->max_recv_size = smbd_max_receive_size; | |
| sp->max_frmr_depth = smbd_max_frmr_depth; | |
| sp->keepalive_interval_msec = smbd_keep_alive_interval * 1000; | |
| sp->keepalive_timeout_msec = KEEPALIVE_RECV_TIMEOUT * 1000; | |
| info = kzalloc_obj(*info); | |
| if (!info) | |
| return NULL; | |
| ret = smbdirect_socket_create_kern(net, &sc); | |
| if (ret) | |
| goto socket_init_failed; | |
| smbdirect_socket_set_logging(sc, NULL, smbd_logging_needed, smbd_logging_vaprintf); | |
| ret = smbdirect_socket_set_initial_parameters(sc, sp); | |
| if (ret) | |
| goto set_params_failed; | |
| ret = smbdirect_socket_set_kernel_settings(sc, IB_POLL_SOFTIRQ, GFP_KERNEL); | |
| if (ret) | |
| goto set_settings_failed; | |
| if (dstaddr->sa_family == AF_INET6) | |
| sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port; | |
| else | |
| sport = &((struct sockaddr_in *)dstaddr)->sin_port; | |
| *sport = htons(port); | |
| ret = smbdirect_connect_sync(sc, dstaddr); | |
| if (ret) { | |
| log_rdma_event(ERR, "connect to %pISpsfc failed: %1pe\n", | |
| dstaddr, ERR_PTR(ret)); | |
| goto connect_failed; | |
| } | |
| info->socket = sc; | |
| return info; | |
| connect_failed: | |
| set_settings_failed: | |
| set_params_failed: | |
| smbdirect_socket_release(sc); | |
| socket_init_failed: | |
| kfree(info); | |
| return NULL; | |
| } | |
| const struct smbdirect_socket_parameters *smbd_get_parameters(struct smbd_connection *conn) | |
| { | |
| if (unlikely(!conn->socket)) { | |
| static const struct smbdirect_socket_parameters zero_params; | |
| return &zero_params; | |
| } | |
| return smbdirect_socket_get_current_parameters(conn->socket); | |
| } | |
| struct smbd_connection *smbd_get_connection( | |
| struct TCP_Server_Info *server, struct sockaddr *dstaddr) | |
| { | |
| struct smbd_connection *ret; | |
| const struct smbdirect_socket_parameters *sp; | |
| int port = SMBD_PORT; | |
| try_again: | |
| ret = _smbd_get_connection(server, dstaddr, port); | |
| /* Try SMB_PORT if SMBD_PORT doesn't work */ | |
| if (!ret && port == SMBD_PORT) { | |
| port = SMB_PORT; | |
| goto try_again; | |
| } | |
| if (!ret) | |
| return NULL; | |
| sp = smbd_get_parameters(ret); | |
| server->rdma_readwrite_threshold = | |
| rdma_readwrite_threshold > sp->max_fragmented_send_size ? | |
| sp->max_fragmented_send_size : | |
| rdma_readwrite_threshold; | |
| return ret; | |
| } | |
| /* | |
| * Receive data from the transport's receive reassembly queue | |
| * All the incoming data packets are placed in reassembly queue | |
| * iter: the buffer to read data into | |
| * size: the length of data to read | |
| * return value: actual data read | |
| * | |
| * Note: this implementation copies the data from reassembly queue to receive | |
| * buffers used by upper layer. This is not the optimal code path. A better way | |
| * to do it is to not have upper layer allocate its receive buffers but rather | |
| * borrow the buffer from reassembly queue, and return it after data is | |
| * consumed. But this will require more changes to upper layer code, and also | |
| * need to consider packet boundaries while they still being reassembled. | |
| */ | |
| int smbd_recv(struct smbd_connection *info, struct msghdr *msg) | |
| { | |
| struct smbdirect_socket *sc = info->socket; | |
| if (!smbdirect_connection_is_connected(sc)) | |
| return -ENOTCONN; | |
| return smbdirect_connection_recvmsg(sc, msg, 0); | |
| } | |
| /* | |
| * Send data to transport | |
| * Each rqst is transported as a SMBDirect payload | |
| * rqst: the data to write | |
| * return value: 0 if successfully write, otherwise error code | |
| */ | |
| int smbd_send(struct TCP_Server_Info *server, | |
| int num_rqst, struct smb_rqst *rqst_array) | |
| { | |
| struct smbd_connection *info = server->smbd_conn; | |
| struct smbdirect_socket *sc = info->socket; | |
| const struct smbdirect_socket_parameters *sp = smbd_get_parameters(info); | |
| struct smb_rqst *rqst; | |
| struct iov_iter iter; | |
| struct smbdirect_send_batch_storage bstorage; | |
| struct smbdirect_send_batch *batch; | |
| unsigned int remaining_data_length, klen; | |
| int rc, i, rqst_idx; | |
| int error = 0; | |
| if (!smbdirect_connection_is_connected(sc)) | |
| return -EAGAIN; | |
| /* | |
| * Add in the page array if there is one. The caller needs to set | |
| * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and | |
| * ends at page boundary | |
| */ | |
| remaining_data_length = 0; | |
| for (i = 0; i < num_rqst; i++) | |
| remaining_data_length += smb_rqst_len(server, &rqst_array[i]); | |
| if (unlikely(remaining_data_length > sp->max_fragmented_send_size)) { | |
| /* assertion: payload never exceeds negotiated maximum */ | |
| log_write(ERR, "payload size %d > max size %d\n", | |
| remaining_data_length, sp->max_fragmented_send_size); | |
| return -EINVAL; | |
| } | |
| log_write(INFO, "num_rqst=%d total length=%u\n", | |
| num_rqst, remaining_data_length); | |
| rqst_idx = 0; | |
| batch = smbdirect_init_send_batch_storage(&bstorage, false, 0); | |
| do { | |
| rqst = &rqst_array[rqst_idx]; | |
| cifs_dbg(FYI, "Sending smb (RDMA): idx=%d smb_len=%lu\n", | |
| rqst_idx, smb_rqst_len(server, rqst)); | |
| for (i = 0; i < rqst->rq_nvec; i++) | |
| dump_smb(rqst->rq_iov[i].iov_base, rqst->rq_iov[i].iov_len); | |
| log_write(INFO, "RDMA-WR[%u] nvec=%d len=%u iter=%zu rqlen=%lu\n", | |
| rqst_idx, rqst->rq_nvec, remaining_data_length, | |
| iov_iter_count(&rqst->rq_iter), smb_rqst_len(server, rqst)); | |
| /* Send the metadata pages. */ | |
| klen = 0; | |
| for (i = 0; i < rqst->rq_nvec; i++) | |
| 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, batch, &iter, remaining_data_length); | |
| if (rc < 0) { | |
| error = rc; | |
| break; | |
| } | |
| remaining_data_length -= rc; | |
| if (iov_iter_count(&rqst->rq_iter) > 0) { | |
| /* And then the data pages if there are any */ | |
| rc = smbd_post_send_full_iter(sc, batch, &rqst->rq_iter, | |
| remaining_data_length); | |
| if (rc < 0) { | |
| error = rc; | |
| break; | |
| } | |
| remaining_data_length -= rc; | |
| } | |
| } while (++rqst_idx < num_rqst); | |
| rc = smbdirect_connection_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. | |
| * Send them all and wait for pending send count to get to 0 | |
| * that means all the I/Os have been out and we are good to return | |
| */ | |
| error = rc; | |
| rc = smbdirect_connection_send_wait_zero_pending(sc); | |
| if (unlikely(rc && !error)) | |
| error = -EAGAIN; | |
| if (unlikely(error)) | |
| return error; | |
| return 0; | |
| } | |
| /* | |
| * Register memory for RDMA read/write | |
| * iter: the buffer to register memory with | |
| * writing: true if this is a RDMA write (SMB read), false for RDMA read | |
| * need_invalidate: true if this MR needs to be locally invalidated after I/O | |
| * return value: the MR registered, NULL if failed. | |
| */ | |
| struct smbdirect_mr_io *smbd_register_mr(struct smbd_connection *info, | |
| struct iov_iter *iter, | |
| bool writing, bool need_invalidate) | |
| { | |
| struct smbdirect_socket *sc = info->socket; | |
| if (!smbdirect_connection_is_connected(sc)) | |
| return NULL; | |
| return smbdirect_connection_register_mr_io(sc, iter, writing, need_invalidate); | |
| } | |
| void smbd_mr_fill_buffer_descriptor(struct smbdirect_mr_io *mr, | |
| struct smbdirect_buffer_descriptor_v1 *v1) | |
| { | |
| smbdirect_mr_io_fill_buffer_descriptor(mr, v1); | |
| } | |
| /* | |
| * Deregister a MR after I/O is done | |
| * This function may wait if remote invalidation is not used | |
| * and we have to locally invalidate the buffer to prevent data is being | |
| * modified by remote peer after upper layer consumes it | |
| */ | |
| void smbd_deregister_mr(struct smbdirect_mr_io *mr) | |
| { | |
| smbdirect_connection_deregister_mr_io(mr); | |
| } | |
| void smbd_debug_proc_show(struct TCP_Server_Info *server, struct seq_file *m) | |
| { | |
| if (!server->rdma) | |
| return; | |
| if (!server->smbd_conn) { | |
| seq_puts(m, "\nSMBDirect transport not available"); | |
| return; | |
| } | |
| smbdirect_connection_legacy_debug_proc_show(server->smbd_conn->socket, | |
| server->rdma_readwrite_threshold, | |
| m); | |
| } | |
| MODULE_IMPORT_NS("SMBDIRECT"); |