18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017, Microsoft Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author(s): Long Li <longli@microsoft.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/highmem.h> 98c2ecf20Sopenharmony_ci#include "smbdirect.h" 108c2ecf20Sopenharmony_ci#include "cifs_debug.h" 118c2ecf20Sopenharmony_ci#include "cifsproto.h" 128c2ecf20Sopenharmony_ci#include "smb2proto.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic struct smbd_response *get_empty_queue_buffer( 158c2ecf20Sopenharmony_ci struct smbd_connection *info); 168c2ecf20Sopenharmony_cistatic struct smbd_response *get_receive_buffer( 178c2ecf20Sopenharmony_ci struct smbd_connection *info); 188c2ecf20Sopenharmony_cistatic void put_receive_buffer( 198c2ecf20Sopenharmony_ci struct smbd_connection *info, 208c2ecf20Sopenharmony_ci struct smbd_response *response); 218c2ecf20Sopenharmony_cistatic int allocate_receive_buffers(struct smbd_connection *info, int num_buf); 228c2ecf20Sopenharmony_cistatic void destroy_receive_buffers(struct smbd_connection *info); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void put_empty_packet( 258c2ecf20Sopenharmony_ci struct smbd_connection *info, struct smbd_response *response); 268c2ecf20Sopenharmony_cistatic void enqueue_reassembly( 278c2ecf20Sopenharmony_ci struct smbd_connection *info, 288c2ecf20Sopenharmony_ci struct smbd_response *response, int data_length); 298c2ecf20Sopenharmony_cistatic struct smbd_response *_get_first_reassembly( 308c2ecf20Sopenharmony_ci struct smbd_connection *info); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int smbd_post_recv( 338c2ecf20Sopenharmony_ci struct smbd_connection *info, 348c2ecf20Sopenharmony_ci struct smbd_response *response); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int smbd_post_send_empty(struct smbd_connection *info); 378c2ecf20Sopenharmony_cistatic int smbd_post_send_data( 388c2ecf20Sopenharmony_ci struct smbd_connection *info, 398c2ecf20Sopenharmony_ci struct kvec *iov, int n_vec, int remaining_data_length); 408c2ecf20Sopenharmony_cistatic int smbd_post_send_page(struct smbd_connection *info, 418c2ecf20Sopenharmony_ci struct page *page, unsigned long offset, 428c2ecf20Sopenharmony_ci size_t size, int remaining_data_length); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void destroy_mr_list(struct smbd_connection *info); 458c2ecf20Sopenharmony_cistatic int allocate_mr_list(struct smbd_connection *info); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* SMBD version number */ 488c2ecf20Sopenharmony_ci#define SMBD_V1 0x0100 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Port numbers for SMBD transport */ 518c2ecf20Sopenharmony_ci#define SMB_PORT 445 528c2ecf20Sopenharmony_ci#define SMBD_PORT 5445 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Address lookup and resolve timeout in ms */ 558c2ecf20Sopenharmony_ci#define RDMA_RESOLVE_TIMEOUT 5000 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* SMBD negotiation timeout in seconds */ 588c2ecf20Sopenharmony_ci#define SMBD_NEGOTIATE_TIMEOUT 120 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* SMBD minimum receive size and fragmented sized defined in [MS-SMBD] */ 618c2ecf20Sopenharmony_ci#define SMBD_MIN_RECEIVE_SIZE 128 628c2ecf20Sopenharmony_ci#define SMBD_MIN_FRAGMENTED_SIZE 131072 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Default maximum number of RDMA read/write outstanding on this connection 668c2ecf20Sopenharmony_ci * This value is possibly decreased during QP creation on hardware limit 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci#define SMBD_CM_RESPONDER_RESOURCES 32 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Maximum number of retries on data transfer operations */ 718c2ecf20Sopenharmony_ci#define SMBD_CM_RETRY 6 728c2ecf20Sopenharmony_ci/* No need to retry on Receiver Not Ready since SMBD manages credits */ 738c2ecf20Sopenharmony_ci#define SMBD_CM_RNR_RETRY 0 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * User configurable initial values per SMBD transport connection 778c2ecf20Sopenharmony_ci * as defined in [MS-SMBD] 3.1.1.1 788c2ecf20Sopenharmony_ci * Those may change after a SMBD negotiation 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci/* The local peer's maximum number of credits to grant to the peer */ 818c2ecf20Sopenharmony_ciint smbd_receive_credit_max = 255; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* The remote peer's credit request of local peer */ 848c2ecf20Sopenharmony_ciint smbd_send_credit_target = 255; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* The maximum single message size can be sent to remote peer */ 878c2ecf20Sopenharmony_ciint smbd_max_send_size = 1364; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* The maximum fragmented upper-layer payload receive size supported */ 908c2ecf20Sopenharmony_ciint smbd_max_fragmented_recv_size = 1024 * 1024; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* The maximum single-message size which can be received */ 938c2ecf20Sopenharmony_ciint smbd_max_receive_size = 8192; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* The timeout to initiate send of a keepalive message on idle */ 968c2ecf20Sopenharmony_ciint smbd_keep_alive_interval = 120; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * User configurable initial values for RDMA transport 1008c2ecf20Sopenharmony_ci * The actual values used may be lower and are limited to hardware capabilities 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci/* Default maximum number of SGEs in a RDMA write/read */ 1038c2ecf20Sopenharmony_ciint smbd_max_frmr_depth = 2048; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* If payload is less than this byte, use RDMA send/recv not read/write */ 1068c2ecf20Sopenharmony_ciint rdma_readwrite_threshold = 4096; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* Transport logging functions 1098c2ecf20Sopenharmony_ci * Logging are defined as classes. They can be OR'ed to define the actual 1108c2ecf20Sopenharmony_ci * logging level via module parameter smbd_logging_class 1118c2ecf20Sopenharmony_ci * e.g. cifs.smbd_logging_class=0xa0 will log all log_rdma_recv() and 1128c2ecf20Sopenharmony_ci * log_rdma_event() 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci#define LOG_OUTGOING 0x1 1158c2ecf20Sopenharmony_ci#define LOG_INCOMING 0x2 1168c2ecf20Sopenharmony_ci#define LOG_READ 0x4 1178c2ecf20Sopenharmony_ci#define LOG_WRITE 0x8 1188c2ecf20Sopenharmony_ci#define LOG_RDMA_SEND 0x10 1198c2ecf20Sopenharmony_ci#define LOG_RDMA_RECV 0x20 1208c2ecf20Sopenharmony_ci#define LOG_KEEP_ALIVE 0x40 1218c2ecf20Sopenharmony_ci#define LOG_RDMA_EVENT 0x80 1228c2ecf20Sopenharmony_ci#define LOG_RDMA_MR 0x100 1238c2ecf20Sopenharmony_cistatic unsigned int smbd_logging_class; 1248c2ecf20Sopenharmony_cimodule_param(smbd_logging_class, uint, 0644); 1258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(smbd_logging_class, 1268c2ecf20Sopenharmony_ci "Logging class for SMBD transport 0x0 to 0x100"); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define ERR 0x0 1298c2ecf20Sopenharmony_ci#define INFO 0x1 1308c2ecf20Sopenharmony_cistatic unsigned int smbd_logging_level = ERR; 1318c2ecf20Sopenharmony_cimodule_param(smbd_logging_level, uint, 0644); 1328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(smbd_logging_level, 1338c2ecf20Sopenharmony_ci "Logging level for SMBD transport, 0 (default): error, 1: info"); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define log_rdma(level, class, fmt, args...) \ 1368c2ecf20Sopenharmony_cido { \ 1378c2ecf20Sopenharmony_ci if (level <= smbd_logging_level || class & smbd_logging_class) \ 1388c2ecf20Sopenharmony_ci cifs_dbg(VFS, "%s:%d " fmt, __func__, __LINE__, ##args);\ 1398c2ecf20Sopenharmony_ci} while (0) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#define log_outgoing(level, fmt, args...) \ 1428c2ecf20Sopenharmony_ci log_rdma(level, LOG_OUTGOING, fmt, ##args) 1438c2ecf20Sopenharmony_ci#define log_incoming(level, fmt, args...) \ 1448c2ecf20Sopenharmony_ci log_rdma(level, LOG_INCOMING, fmt, ##args) 1458c2ecf20Sopenharmony_ci#define log_read(level, fmt, args...) log_rdma(level, LOG_READ, fmt, ##args) 1468c2ecf20Sopenharmony_ci#define log_write(level, fmt, args...) log_rdma(level, LOG_WRITE, fmt, ##args) 1478c2ecf20Sopenharmony_ci#define log_rdma_send(level, fmt, args...) \ 1488c2ecf20Sopenharmony_ci log_rdma(level, LOG_RDMA_SEND, fmt, ##args) 1498c2ecf20Sopenharmony_ci#define log_rdma_recv(level, fmt, args...) \ 1508c2ecf20Sopenharmony_ci log_rdma(level, LOG_RDMA_RECV, fmt, ##args) 1518c2ecf20Sopenharmony_ci#define log_keep_alive(level, fmt, args...) \ 1528c2ecf20Sopenharmony_ci log_rdma(level, LOG_KEEP_ALIVE, fmt, ##args) 1538c2ecf20Sopenharmony_ci#define log_rdma_event(level, fmt, args...) \ 1548c2ecf20Sopenharmony_ci log_rdma(level, LOG_RDMA_EVENT, fmt, ##args) 1558c2ecf20Sopenharmony_ci#define log_rdma_mr(level, fmt, args...) \ 1568c2ecf20Sopenharmony_ci log_rdma(level, LOG_RDMA_MR, fmt, ##args) 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void smbd_disconnect_rdma_work(struct work_struct *work) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct smbd_connection *info = 1618c2ecf20Sopenharmony_ci container_of(work, struct smbd_connection, disconnect_work); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (info->transport_status == SMBD_CONNECTED) { 1648c2ecf20Sopenharmony_ci info->transport_status = SMBD_DISCONNECTING; 1658c2ecf20Sopenharmony_ci rdma_disconnect(info->id); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void smbd_disconnect_rdma_connection(struct smbd_connection *info) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci queue_work(info->workqueue, &info->disconnect_work); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* Upcall from RDMA CM */ 1758c2ecf20Sopenharmony_cistatic int smbd_conn_upcall( 1768c2ecf20Sopenharmony_ci struct rdma_cm_id *id, struct rdma_cm_event *event) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct smbd_connection *info = id->context; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci log_rdma_event(INFO, "event=%d status=%d\n", 1818c2ecf20Sopenharmony_ci event->event, event->status); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci switch (event->event) { 1848c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_ADDR_RESOLVED: 1858c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_ROUTE_RESOLVED: 1868c2ecf20Sopenharmony_ci info->ri_rc = 0; 1878c2ecf20Sopenharmony_ci complete(&info->ri_done); 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_ADDR_ERROR: 1918c2ecf20Sopenharmony_ci info->ri_rc = -EHOSTUNREACH; 1928c2ecf20Sopenharmony_ci complete(&info->ri_done); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_ROUTE_ERROR: 1968c2ecf20Sopenharmony_ci info->ri_rc = -ENETUNREACH; 1978c2ecf20Sopenharmony_ci complete(&info->ri_done); 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_ESTABLISHED: 2018c2ecf20Sopenharmony_ci log_rdma_event(INFO, "connected event=%d\n", event->event); 2028c2ecf20Sopenharmony_ci info->transport_status = SMBD_CONNECTED; 2038c2ecf20Sopenharmony_ci wake_up_interruptible(&info->conn_wait); 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_CONNECT_ERROR: 2078c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_UNREACHABLE: 2088c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_REJECTED: 2098c2ecf20Sopenharmony_ci log_rdma_event(INFO, "connecting failed event=%d\n", event->event); 2108c2ecf20Sopenharmony_ci info->transport_status = SMBD_DISCONNECTED; 2118c2ecf20Sopenharmony_ci wake_up_interruptible(&info->conn_wait); 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_DEVICE_REMOVAL: 2158c2ecf20Sopenharmony_ci case RDMA_CM_EVENT_DISCONNECTED: 2168c2ecf20Sopenharmony_ci /* This happenes when we fail the negotiation */ 2178c2ecf20Sopenharmony_ci if (info->transport_status == SMBD_NEGOTIATE_FAILED) { 2188c2ecf20Sopenharmony_ci info->transport_status = SMBD_DISCONNECTED; 2198c2ecf20Sopenharmony_ci wake_up(&info->conn_wait); 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci info->transport_status = SMBD_DISCONNECTED; 2248c2ecf20Sopenharmony_ci wake_up_interruptible(&info->disconn_wait); 2258c2ecf20Sopenharmony_ci wake_up_interruptible(&info->wait_reassembly_queue); 2268c2ecf20Sopenharmony_ci wake_up_interruptible_all(&info->wait_send_queue); 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci default: 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* Upcall from RDMA QP */ 2378c2ecf20Sopenharmony_cistatic void 2388c2ecf20Sopenharmony_cismbd_qp_async_error_upcall(struct ib_event *event, void *context) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct smbd_connection *info = context; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci log_rdma_event(ERR, "%s on device %s info %p\n", 2438c2ecf20Sopenharmony_ci ib_event_msg(event->event), event->device->name, info); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci switch (event->event) { 2468c2ecf20Sopenharmony_ci case IB_EVENT_CQ_ERR: 2478c2ecf20Sopenharmony_ci case IB_EVENT_QP_FATAL: 2488c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci default: 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic inline void *smbd_request_payload(struct smbd_request *request) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci return (void *)request->packet; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic inline void *smbd_response_payload(struct smbd_response *response) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci return (void *)response->packet; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* Called when a RDMA send is done */ 2668c2ecf20Sopenharmony_cistatic void send_done(struct ib_cq *cq, struct ib_wc *wc) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci int i; 2698c2ecf20Sopenharmony_ci struct smbd_request *request = 2708c2ecf20Sopenharmony_ci container_of(wc->wr_cqe, struct smbd_request, cqe); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci log_rdma_send(INFO, "smbd_request %p completed wc->status=%d\n", 2738c2ecf20Sopenharmony_ci request, wc->status); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { 2768c2ecf20Sopenharmony_ci log_rdma_send(ERR, "wc->status=%d wc->opcode=%d\n", 2778c2ecf20Sopenharmony_ci wc->status, wc->opcode); 2788c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(request->info); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci for (i = 0; i < request->num_sge; i++) 2828c2ecf20Sopenharmony_ci ib_dma_unmap_single(request->info->id->device, 2838c2ecf20Sopenharmony_ci request->sge[i].addr, 2848c2ecf20Sopenharmony_ci request->sge[i].length, 2858c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&request->info->send_pending)) 2888c2ecf20Sopenharmony_ci wake_up(&request->info->wait_send_pending); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci wake_up(&request->info->wait_post_send); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci mempool_free(request, request->info->request_mempool); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic void dump_smbd_negotiate_resp(struct smbd_negotiate_resp *resp) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci log_rdma_event(INFO, "resp message min_version %u max_version %u negotiated_version %u credits_requested %u credits_granted %u status %u max_readwrite_size %u preferred_send_size %u max_receive_size %u max_fragmented_size %u\n", 2988c2ecf20Sopenharmony_ci resp->min_version, resp->max_version, 2998c2ecf20Sopenharmony_ci resp->negotiated_version, resp->credits_requested, 3008c2ecf20Sopenharmony_ci resp->credits_granted, resp->status, 3018c2ecf20Sopenharmony_ci resp->max_readwrite_size, resp->preferred_send_size, 3028c2ecf20Sopenharmony_ci resp->max_receive_size, resp->max_fragmented_size); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci/* 3068c2ecf20Sopenharmony_ci * Process a negotiation response message, according to [MS-SMBD]3.1.5.7 3078c2ecf20Sopenharmony_ci * response, packet_length: the negotiation response message 3088c2ecf20Sopenharmony_ci * return value: true if negotiation is a success, false if failed 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_cistatic bool process_negotiation_response( 3118c2ecf20Sopenharmony_ci struct smbd_response *response, int packet_length) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct smbd_connection *info = response->info; 3148c2ecf20Sopenharmony_ci struct smbd_negotiate_resp *packet = smbd_response_payload(response); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (packet_length < sizeof(struct smbd_negotiate_resp)) { 3178c2ecf20Sopenharmony_ci log_rdma_event(ERR, 3188c2ecf20Sopenharmony_ci "error: packet_length=%d\n", packet_length); 3198c2ecf20Sopenharmony_ci return false; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (le16_to_cpu(packet->negotiated_version) != SMBD_V1) { 3238c2ecf20Sopenharmony_ci log_rdma_event(ERR, "error: negotiated_version=%x\n", 3248c2ecf20Sopenharmony_ci le16_to_cpu(packet->negotiated_version)); 3258c2ecf20Sopenharmony_ci return false; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci info->protocol = le16_to_cpu(packet->negotiated_version); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (packet->credits_requested == 0) { 3308c2ecf20Sopenharmony_ci log_rdma_event(ERR, "error: credits_requested==0\n"); 3318c2ecf20Sopenharmony_ci return false; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci info->receive_credit_target = le16_to_cpu(packet->credits_requested); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (packet->credits_granted == 0) { 3368c2ecf20Sopenharmony_ci log_rdma_event(ERR, "error: credits_granted==0\n"); 3378c2ecf20Sopenharmony_ci return false; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci atomic_set(&info->send_credits, le16_to_cpu(packet->credits_granted)); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci atomic_set(&info->receive_credits, 0); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (le32_to_cpu(packet->preferred_send_size) > info->max_receive_size) { 3448c2ecf20Sopenharmony_ci log_rdma_event(ERR, "error: preferred_send_size=%d\n", 3458c2ecf20Sopenharmony_ci le32_to_cpu(packet->preferred_send_size)); 3468c2ecf20Sopenharmony_ci return false; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci info->max_receive_size = le32_to_cpu(packet->preferred_send_size); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (le32_to_cpu(packet->max_receive_size) < SMBD_MIN_RECEIVE_SIZE) { 3518c2ecf20Sopenharmony_ci log_rdma_event(ERR, "error: max_receive_size=%d\n", 3528c2ecf20Sopenharmony_ci le32_to_cpu(packet->max_receive_size)); 3538c2ecf20Sopenharmony_ci return false; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci info->max_send_size = min_t(int, info->max_send_size, 3568c2ecf20Sopenharmony_ci le32_to_cpu(packet->max_receive_size)); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (le32_to_cpu(packet->max_fragmented_size) < 3598c2ecf20Sopenharmony_ci SMBD_MIN_FRAGMENTED_SIZE) { 3608c2ecf20Sopenharmony_ci log_rdma_event(ERR, "error: max_fragmented_size=%d\n", 3618c2ecf20Sopenharmony_ci le32_to_cpu(packet->max_fragmented_size)); 3628c2ecf20Sopenharmony_ci return false; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci info->max_fragmented_send_size = 3658c2ecf20Sopenharmony_ci le32_to_cpu(packet->max_fragmented_size); 3668c2ecf20Sopenharmony_ci info->rdma_readwrite_threshold = 3678c2ecf20Sopenharmony_ci rdma_readwrite_threshold > info->max_fragmented_send_size ? 3688c2ecf20Sopenharmony_ci info->max_fragmented_send_size : 3698c2ecf20Sopenharmony_ci rdma_readwrite_threshold; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci info->max_readwrite_size = min_t(u32, 3738c2ecf20Sopenharmony_ci le32_to_cpu(packet->max_readwrite_size), 3748c2ecf20Sopenharmony_ci info->max_frmr_depth * PAGE_SIZE); 3758c2ecf20Sopenharmony_ci info->max_frmr_depth = info->max_readwrite_size / PAGE_SIZE; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return true; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void smbd_post_send_credits(struct work_struct *work) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci int ret = 0; 3838c2ecf20Sopenharmony_ci int use_receive_queue = 1; 3848c2ecf20Sopenharmony_ci int rc; 3858c2ecf20Sopenharmony_ci struct smbd_response *response; 3868c2ecf20Sopenharmony_ci struct smbd_connection *info = 3878c2ecf20Sopenharmony_ci container_of(work, struct smbd_connection, 3888c2ecf20Sopenharmony_ci post_send_credits_work); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (info->transport_status != SMBD_CONNECTED) { 3918c2ecf20Sopenharmony_ci wake_up(&info->wait_receive_queues); 3928c2ecf20Sopenharmony_ci return; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (info->receive_credit_target > 3968c2ecf20Sopenharmony_ci atomic_read(&info->receive_credits)) { 3978c2ecf20Sopenharmony_ci while (true) { 3988c2ecf20Sopenharmony_ci if (use_receive_queue) 3998c2ecf20Sopenharmony_ci response = get_receive_buffer(info); 4008c2ecf20Sopenharmony_ci else 4018c2ecf20Sopenharmony_ci response = get_empty_queue_buffer(info); 4028c2ecf20Sopenharmony_ci if (!response) { 4038c2ecf20Sopenharmony_ci /* now switch to emtpy packet queue */ 4048c2ecf20Sopenharmony_ci if (use_receive_queue) { 4058c2ecf20Sopenharmony_ci use_receive_queue = 0; 4068c2ecf20Sopenharmony_ci continue; 4078c2ecf20Sopenharmony_ci } else 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci response->type = SMBD_TRANSFER_DATA; 4128c2ecf20Sopenharmony_ci response->first_segment = false; 4138c2ecf20Sopenharmony_ci rc = smbd_post_recv(info, response); 4148c2ecf20Sopenharmony_ci if (rc) { 4158c2ecf20Sopenharmony_ci log_rdma_recv(ERR, 4168c2ecf20Sopenharmony_ci "post_recv failed rc=%d\n", rc); 4178c2ecf20Sopenharmony_ci put_receive_buffer(info, response); 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ret++; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci spin_lock(&info->lock_new_credits_offered); 4268c2ecf20Sopenharmony_ci info->new_credits_offered += ret; 4278c2ecf20Sopenharmony_ci spin_unlock(&info->lock_new_credits_offered); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */ 4308c2ecf20Sopenharmony_ci info->send_immediate = true; 4318c2ecf20Sopenharmony_ci if (atomic_read(&info->receive_credits) < 4328c2ecf20Sopenharmony_ci info->receive_credit_target - 1) { 4338c2ecf20Sopenharmony_ci if (info->keep_alive_requested == KEEP_ALIVE_PENDING || 4348c2ecf20Sopenharmony_ci info->send_immediate) { 4358c2ecf20Sopenharmony_ci log_keep_alive(INFO, "send an empty message\n"); 4368c2ecf20Sopenharmony_ci smbd_post_send_empty(info); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* Called from softirq, when recv is done */ 4428c2ecf20Sopenharmony_cistatic void recv_done(struct ib_cq *cq, struct ib_wc *wc) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct smbd_data_transfer *data_transfer; 4458c2ecf20Sopenharmony_ci struct smbd_response *response = 4468c2ecf20Sopenharmony_ci container_of(wc->wr_cqe, struct smbd_response, cqe); 4478c2ecf20Sopenharmony_ci struct smbd_connection *info = response->info; 4488c2ecf20Sopenharmony_ci int data_length = 0; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci log_rdma_recv(INFO, "response=%p type=%d wc status=%d wc opcode %d byte_len=%d pkey_index=%x\n", 4518c2ecf20Sopenharmony_ci response, response->type, wc->status, wc->opcode, 4528c2ecf20Sopenharmony_ci wc->byte_len, wc->pkey_index); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { 4558c2ecf20Sopenharmony_ci log_rdma_recv(INFO, "wc->status=%d opcode=%d\n", 4568c2ecf20Sopenharmony_ci wc->status, wc->opcode); 4578c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 4588c2ecf20Sopenharmony_ci goto error; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci ib_dma_sync_single_for_cpu( 4628c2ecf20Sopenharmony_ci wc->qp->device, 4638c2ecf20Sopenharmony_ci response->sge.addr, 4648c2ecf20Sopenharmony_ci response->sge.length, 4658c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci switch (response->type) { 4688c2ecf20Sopenharmony_ci /* SMBD negotiation response */ 4698c2ecf20Sopenharmony_ci case SMBD_NEGOTIATE_RESP: 4708c2ecf20Sopenharmony_ci dump_smbd_negotiate_resp(smbd_response_payload(response)); 4718c2ecf20Sopenharmony_ci info->full_packet_received = true; 4728c2ecf20Sopenharmony_ci info->negotiate_done = 4738c2ecf20Sopenharmony_ci process_negotiation_response(response, wc->byte_len); 4748c2ecf20Sopenharmony_ci complete(&info->negotiate_completion); 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* SMBD data transfer packet */ 4788c2ecf20Sopenharmony_ci case SMBD_TRANSFER_DATA: 4798c2ecf20Sopenharmony_ci data_transfer = smbd_response_payload(response); 4808c2ecf20Sopenharmony_ci data_length = le32_to_cpu(data_transfer->data_length); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* 4838c2ecf20Sopenharmony_ci * If this is a packet with data playload place the data in 4848c2ecf20Sopenharmony_ci * reassembly queue and wake up the reading thread 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_ci if (data_length) { 4878c2ecf20Sopenharmony_ci if (info->full_packet_received) 4888c2ecf20Sopenharmony_ci response->first_segment = true; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (le32_to_cpu(data_transfer->remaining_data_length)) 4918c2ecf20Sopenharmony_ci info->full_packet_received = false; 4928c2ecf20Sopenharmony_ci else 4938c2ecf20Sopenharmony_ci info->full_packet_received = true; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci enqueue_reassembly( 4968c2ecf20Sopenharmony_ci info, 4978c2ecf20Sopenharmony_ci response, 4988c2ecf20Sopenharmony_ci data_length); 4998c2ecf20Sopenharmony_ci } else 5008c2ecf20Sopenharmony_ci put_empty_packet(info, response); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (data_length) 5038c2ecf20Sopenharmony_ci wake_up_interruptible(&info->wait_reassembly_queue); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci atomic_dec(&info->receive_credits); 5068c2ecf20Sopenharmony_ci info->receive_credit_target = 5078c2ecf20Sopenharmony_ci le16_to_cpu(data_transfer->credits_requested); 5088c2ecf20Sopenharmony_ci if (le16_to_cpu(data_transfer->credits_granted)) { 5098c2ecf20Sopenharmony_ci atomic_add(le16_to_cpu(data_transfer->credits_granted), 5108c2ecf20Sopenharmony_ci &info->send_credits); 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * We have new send credits granted from remote peer 5138c2ecf20Sopenharmony_ci * If any sender is waiting for credits, unblock it 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci wake_up_interruptible(&info->wait_send_queue); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci log_incoming(INFO, "data flags %d data_offset %d data_length %d remaining_data_length %d\n", 5198c2ecf20Sopenharmony_ci le16_to_cpu(data_transfer->flags), 5208c2ecf20Sopenharmony_ci le32_to_cpu(data_transfer->data_offset), 5218c2ecf20Sopenharmony_ci le32_to_cpu(data_transfer->data_length), 5228c2ecf20Sopenharmony_ci le32_to_cpu(data_transfer->remaining_data_length)); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Send a KEEP_ALIVE response right away if requested */ 5258c2ecf20Sopenharmony_ci info->keep_alive_requested = KEEP_ALIVE_NONE; 5268c2ecf20Sopenharmony_ci if (le16_to_cpu(data_transfer->flags) & 5278c2ecf20Sopenharmony_ci SMB_DIRECT_RESPONSE_REQUESTED) { 5288c2ecf20Sopenharmony_ci info->keep_alive_requested = KEEP_ALIVE_PENDING; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci default: 5348c2ecf20Sopenharmony_ci log_rdma_recv(ERR, 5358c2ecf20Sopenharmony_ci "unexpected response type=%d\n", response->type); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cierror: 5398c2ecf20Sopenharmony_ci put_receive_buffer(info, response); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic struct rdma_cm_id *smbd_create_id( 5438c2ecf20Sopenharmony_ci struct smbd_connection *info, 5448c2ecf20Sopenharmony_ci struct sockaddr *dstaddr, int port) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct rdma_cm_id *id; 5478c2ecf20Sopenharmony_ci int rc; 5488c2ecf20Sopenharmony_ci __be16 *sport; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci id = rdma_create_id(&init_net, smbd_conn_upcall, info, 5518c2ecf20Sopenharmony_ci RDMA_PS_TCP, IB_QPT_RC); 5528c2ecf20Sopenharmony_ci if (IS_ERR(id)) { 5538c2ecf20Sopenharmony_ci rc = PTR_ERR(id); 5548c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_create_id() failed %i\n", rc); 5558c2ecf20Sopenharmony_ci return id; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (dstaddr->sa_family == AF_INET6) 5598c2ecf20Sopenharmony_ci sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port; 5608c2ecf20Sopenharmony_ci else 5618c2ecf20Sopenharmony_ci sport = &((struct sockaddr_in *)dstaddr)->sin_port; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci *sport = htons(port); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci init_completion(&info->ri_done); 5668c2ecf20Sopenharmony_ci info->ri_rc = -ETIMEDOUT; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci rc = rdma_resolve_addr(id, NULL, (struct sockaddr *)dstaddr, 5698c2ecf20Sopenharmony_ci RDMA_RESOLVE_TIMEOUT); 5708c2ecf20Sopenharmony_ci if (rc) { 5718c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_resolve_addr() failed %i\n", rc); 5728c2ecf20Sopenharmony_ci goto out; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci rc = wait_for_completion_interruptible_timeout( 5758c2ecf20Sopenharmony_ci &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); 5768c2ecf20Sopenharmony_ci /* e.g. if interrupted returns -ERESTARTSYS */ 5778c2ecf20Sopenharmony_ci if (rc < 0) { 5788c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); 5798c2ecf20Sopenharmony_ci goto out; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci rc = info->ri_rc; 5828c2ecf20Sopenharmony_ci if (rc) { 5838c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc); 5848c2ecf20Sopenharmony_ci goto out; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci info->ri_rc = -ETIMEDOUT; 5888c2ecf20Sopenharmony_ci rc = rdma_resolve_route(id, RDMA_RESOLVE_TIMEOUT); 5898c2ecf20Sopenharmony_ci if (rc) { 5908c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_resolve_route() failed %i\n", rc); 5918c2ecf20Sopenharmony_ci goto out; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci rc = wait_for_completion_interruptible_timeout( 5948c2ecf20Sopenharmony_ci &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); 5958c2ecf20Sopenharmony_ci /* e.g. if interrupted returns -ERESTARTSYS */ 5968c2ecf20Sopenharmony_ci if (rc < 0) { 5978c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); 5988c2ecf20Sopenharmony_ci goto out; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci rc = info->ri_rc; 6018c2ecf20Sopenharmony_ci if (rc) { 6028c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc); 6038c2ecf20Sopenharmony_ci goto out; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return id; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ciout: 6098c2ecf20Sopenharmony_ci rdma_destroy_id(id); 6108c2ecf20Sopenharmony_ci return ERR_PTR(rc); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci/* 6148c2ecf20Sopenharmony_ci * Test if FRWR (Fast Registration Work Requests) is supported on the device 6158c2ecf20Sopenharmony_ci * This implementation requries FRWR on RDMA read/write 6168c2ecf20Sopenharmony_ci * return value: true if it is supported 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_cistatic bool frwr_is_supported(struct ib_device_attr *attrs) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) 6218c2ecf20Sopenharmony_ci return false; 6228c2ecf20Sopenharmony_ci if (attrs->max_fast_reg_page_list_len == 0) 6238c2ecf20Sopenharmony_ci return false; 6248c2ecf20Sopenharmony_ci return true; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic int smbd_ia_open( 6288c2ecf20Sopenharmony_ci struct smbd_connection *info, 6298c2ecf20Sopenharmony_ci struct sockaddr *dstaddr, int port) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci int rc; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci info->id = smbd_create_id(info, dstaddr, port); 6348c2ecf20Sopenharmony_ci if (IS_ERR(info->id)) { 6358c2ecf20Sopenharmony_ci rc = PTR_ERR(info->id); 6368c2ecf20Sopenharmony_ci goto out1; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (!frwr_is_supported(&info->id->device->attrs)) { 6408c2ecf20Sopenharmony_ci log_rdma_event(ERR, "Fast Registration Work Requests (FRWR) is not supported\n"); 6418c2ecf20Sopenharmony_ci log_rdma_event(ERR, "Device capability flags = %llx max_fast_reg_page_list_len = %u\n", 6428c2ecf20Sopenharmony_ci info->id->device->attrs.device_cap_flags, 6438c2ecf20Sopenharmony_ci info->id->device->attrs.max_fast_reg_page_list_len); 6448c2ecf20Sopenharmony_ci rc = -EPROTONOSUPPORT; 6458c2ecf20Sopenharmony_ci goto out2; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci info->max_frmr_depth = min_t(int, 6488c2ecf20Sopenharmony_ci smbd_max_frmr_depth, 6498c2ecf20Sopenharmony_ci info->id->device->attrs.max_fast_reg_page_list_len); 6508c2ecf20Sopenharmony_ci info->mr_type = IB_MR_TYPE_MEM_REG; 6518c2ecf20Sopenharmony_ci if (info->id->device->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG) 6528c2ecf20Sopenharmony_ci info->mr_type = IB_MR_TYPE_SG_GAPS; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci info->pd = ib_alloc_pd(info->id->device, 0); 6558c2ecf20Sopenharmony_ci if (IS_ERR(info->pd)) { 6568c2ecf20Sopenharmony_ci rc = PTR_ERR(info->pd); 6578c2ecf20Sopenharmony_ci log_rdma_event(ERR, "ib_alloc_pd() returned %d\n", rc); 6588c2ecf20Sopenharmony_ci goto out2; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ciout2: 6648c2ecf20Sopenharmony_ci rdma_destroy_id(info->id); 6658c2ecf20Sopenharmony_ci info->id = NULL; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ciout1: 6688c2ecf20Sopenharmony_ci return rc; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci/* 6728c2ecf20Sopenharmony_ci * Send a negotiation request message to the peer 6738c2ecf20Sopenharmony_ci * The negotiation procedure is in [MS-SMBD] 3.1.5.2 and 3.1.5.3 6748c2ecf20Sopenharmony_ci * After negotiation, the transport is connected and ready for 6758c2ecf20Sopenharmony_ci * carrying upper layer SMB payload 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_cistatic int smbd_post_send_negotiate_req(struct smbd_connection *info) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci struct ib_send_wr send_wr; 6808c2ecf20Sopenharmony_ci int rc = -ENOMEM; 6818c2ecf20Sopenharmony_ci struct smbd_request *request; 6828c2ecf20Sopenharmony_ci struct smbd_negotiate_req *packet; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci request = mempool_alloc(info->request_mempool, GFP_KERNEL); 6858c2ecf20Sopenharmony_ci if (!request) 6868c2ecf20Sopenharmony_ci return rc; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci request->info = info; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci packet = smbd_request_payload(request); 6918c2ecf20Sopenharmony_ci packet->min_version = cpu_to_le16(SMBD_V1); 6928c2ecf20Sopenharmony_ci packet->max_version = cpu_to_le16(SMBD_V1); 6938c2ecf20Sopenharmony_ci packet->reserved = 0; 6948c2ecf20Sopenharmony_ci packet->credits_requested = cpu_to_le16(info->send_credit_target); 6958c2ecf20Sopenharmony_ci packet->preferred_send_size = cpu_to_le32(info->max_send_size); 6968c2ecf20Sopenharmony_ci packet->max_receive_size = cpu_to_le32(info->max_receive_size); 6978c2ecf20Sopenharmony_ci packet->max_fragmented_size = 6988c2ecf20Sopenharmony_ci cpu_to_le32(info->max_fragmented_recv_size); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci request->num_sge = 1; 7018c2ecf20Sopenharmony_ci request->sge[0].addr = ib_dma_map_single( 7028c2ecf20Sopenharmony_ci info->id->device, (void *)packet, 7038c2ecf20Sopenharmony_ci sizeof(*packet), DMA_TO_DEVICE); 7048c2ecf20Sopenharmony_ci if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { 7058c2ecf20Sopenharmony_ci rc = -EIO; 7068c2ecf20Sopenharmony_ci goto dma_mapping_failed; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci request->sge[0].length = sizeof(*packet); 7108c2ecf20Sopenharmony_ci request->sge[0].lkey = info->pd->local_dma_lkey; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci ib_dma_sync_single_for_device( 7138c2ecf20Sopenharmony_ci info->id->device, request->sge[0].addr, 7148c2ecf20Sopenharmony_ci request->sge[0].length, DMA_TO_DEVICE); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci request->cqe.done = send_done; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci send_wr.next = NULL; 7198c2ecf20Sopenharmony_ci send_wr.wr_cqe = &request->cqe; 7208c2ecf20Sopenharmony_ci send_wr.sg_list = request->sge; 7218c2ecf20Sopenharmony_ci send_wr.num_sge = request->num_sge; 7228c2ecf20Sopenharmony_ci send_wr.opcode = IB_WR_SEND; 7238c2ecf20Sopenharmony_ci send_wr.send_flags = IB_SEND_SIGNALED; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci log_rdma_send(INFO, "sge addr=%llx length=%x lkey=%x\n", 7268c2ecf20Sopenharmony_ci request->sge[0].addr, 7278c2ecf20Sopenharmony_ci request->sge[0].length, request->sge[0].lkey); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci atomic_inc(&info->send_pending); 7308c2ecf20Sopenharmony_ci rc = ib_post_send(info->id->qp, &send_wr, NULL); 7318c2ecf20Sopenharmony_ci if (!rc) 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* if we reach here, post send failed */ 7358c2ecf20Sopenharmony_ci log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); 7368c2ecf20Sopenharmony_ci atomic_dec(&info->send_pending); 7378c2ecf20Sopenharmony_ci ib_dma_unmap_single(info->id->device, request->sge[0].addr, 7388c2ecf20Sopenharmony_ci request->sge[0].length, DMA_TO_DEVICE); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cidma_mapping_failed: 7438c2ecf20Sopenharmony_ci mempool_free(request, info->request_mempool); 7448c2ecf20Sopenharmony_ci return rc; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci/* 7488c2ecf20Sopenharmony_ci * Extend the credits to remote peer 7498c2ecf20Sopenharmony_ci * This implements [MS-SMBD] 3.1.5.9 7508c2ecf20Sopenharmony_ci * The idea is that we should extend credits to remote peer as quickly as 7518c2ecf20Sopenharmony_ci * it's allowed, to maintain data flow. We allocate as much receive 7528c2ecf20Sopenharmony_ci * buffer as possible, and extend the receive credits to remote peer 7538c2ecf20Sopenharmony_ci * return value: the new credtis being granted. 7548c2ecf20Sopenharmony_ci */ 7558c2ecf20Sopenharmony_cistatic int manage_credits_prior_sending(struct smbd_connection *info) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci int new_credits; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci spin_lock(&info->lock_new_credits_offered); 7608c2ecf20Sopenharmony_ci new_credits = info->new_credits_offered; 7618c2ecf20Sopenharmony_ci info->new_credits_offered = 0; 7628c2ecf20Sopenharmony_ci spin_unlock(&info->lock_new_credits_offered); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return new_credits; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci/* 7688c2ecf20Sopenharmony_ci * Check if we need to send a KEEP_ALIVE message 7698c2ecf20Sopenharmony_ci * The idle connection timer triggers a KEEP_ALIVE message when expires 7708c2ecf20Sopenharmony_ci * SMB_DIRECT_RESPONSE_REQUESTED is set in the message flag to have peer send 7718c2ecf20Sopenharmony_ci * back a response. 7728c2ecf20Sopenharmony_ci * return value: 7738c2ecf20Sopenharmony_ci * 1 if SMB_DIRECT_RESPONSE_REQUESTED needs to be set 7748c2ecf20Sopenharmony_ci * 0: otherwise 7758c2ecf20Sopenharmony_ci */ 7768c2ecf20Sopenharmony_cistatic int manage_keep_alive_before_sending(struct smbd_connection *info) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci if (info->keep_alive_requested == KEEP_ALIVE_PENDING) { 7798c2ecf20Sopenharmony_ci info->keep_alive_requested = KEEP_ALIVE_SENT; 7808c2ecf20Sopenharmony_ci return 1; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci return 0; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci/* Post the send request */ 7868c2ecf20Sopenharmony_cistatic int smbd_post_send(struct smbd_connection *info, 7878c2ecf20Sopenharmony_ci struct smbd_request *request) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci struct ib_send_wr send_wr; 7908c2ecf20Sopenharmony_ci int rc, i; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci for (i = 0; i < request->num_sge; i++) { 7938c2ecf20Sopenharmony_ci log_rdma_send(INFO, 7948c2ecf20Sopenharmony_ci "rdma_request sge[%d] addr=%llu length=%u\n", 7958c2ecf20Sopenharmony_ci i, request->sge[i].addr, request->sge[i].length); 7968c2ecf20Sopenharmony_ci ib_dma_sync_single_for_device( 7978c2ecf20Sopenharmony_ci info->id->device, 7988c2ecf20Sopenharmony_ci request->sge[i].addr, 7998c2ecf20Sopenharmony_ci request->sge[i].length, 8008c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci request->cqe.done = send_done; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci send_wr.next = NULL; 8068c2ecf20Sopenharmony_ci send_wr.wr_cqe = &request->cqe; 8078c2ecf20Sopenharmony_ci send_wr.sg_list = request->sge; 8088c2ecf20Sopenharmony_ci send_wr.num_sge = request->num_sge; 8098c2ecf20Sopenharmony_ci send_wr.opcode = IB_WR_SEND; 8108c2ecf20Sopenharmony_ci send_wr.send_flags = IB_SEND_SIGNALED; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci rc = ib_post_send(info->id->qp, &send_wr, NULL); 8138c2ecf20Sopenharmony_ci if (rc) { 8148c2ecf20Sopenharmony_ci log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); 8158c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 8168c2ecf20Sopenharmony_ci rc = -EAGAIN; 8178c2ecf20Sopenharmony_ci } else 8188c2ecf20Sopenharmony_ci /* Reset timer for idle connection after packet is sent */ 8198c2ecf20Sopenharmony_ci mod_delayed_work(info->workqueue, &info->idle_timer_work, 8208c2ecf20Sopenharmony_ci info->keep_alive_interval*HZ); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci return rc; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic int smbd_post_send_sgl(struct smbd_connection *info, 8268c2ecf20Sopenharmony_ci struct scatterlist *sgl, int data_length, int remaining_data_length) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci int num_sgs; 8298c2ecf20Sopenharmony_ci int i, rc; 8308c2ecf20Sopenharmony_ci int header_length; 8318c2ecf20Sopenharmony_ci struct smbd_request *request; 8328c2ecf20Sopenharmony_ci struct smbd_data_transfer *packet; 8338c2ecf20Sopenharmony_ci int new_credits; 8348c2ecf20Sopenharmony_ci struct scatterlist *sg; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ciwait_credit: 8378c2ecf20Sopenharmony_ci /* Wait for send credits. A SMBD packet needs one credit */ 8388c2ecf20Sopenharmony_ci rc = wait_event_interruptible(info->wait_send_queue, 8398c2ecf20Sopenharmony_ci atomic_read(&info->send_credits) > 0 || 8408c2ecf20Sopenharmony_ci info->transport_status != SMBD_CONNECTED); 8418c2ecf20Sopenharmony_ci if (rc) 8428c2ecf20Sopenharmony_ci goto err_wait_credit; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (info->transport_status != SMBD_CONNECTED) { 8458c2ecf20Sopenharmony_ci log_outgoing(ERR, "disconnected not sending on wait_credit\n"); 8468c2ecf20Sopenharmony_ci rc = -EAGAIN; 8478c2ecf20Sopenharmony_ci goto err_wait_credit; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci if (unlikely(atomic_dec_return(&info->send_credits) < 0)) { 8508c2ecf20Sopenharmony_ci atomic_inc(&info->send_credits); 8518c2ecf20Sopenharmony_ci goto wait_credit; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ciwait_send_queue: 8558c2ecf20Sopenharmony_ci wait_event(info->wait_post_send, 8568c2ecf20Sopenharmony_ci atomic_read(&info->send_pending) < info->send_credit_target || 8578c2ecf20Sopenharmony_ci info->transport_status != SMBD_CONNECTED); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (info->transport_status != SMBD_CONNECTED) { 8608c2ecf20Sopenharmony_ci log_outgoing(ERR, "disconnected not sending on wait_send_queue\n"); 8618c2ecf20Sopenharmony_ci rc = -EAGAIN; 8628c2ecf20Sopenharmony_ci goto err_wait_send_queue; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if (unlikely(atomic_inc_return(&info->send_pending) > 8668c2ecf20Sopenharmony_ci info->send_credit_target)) { 8678c2ecf20Sopenharmony_ci atomic_dec(&info->send_pending); 8688c2ecf20Sopenharmony_ci goto wait_send_queue; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci request = mempool_alloc(info->request_mempool, GFP_KERNEL); 8728c2ecf20Sopenharmony_ci if (!request) { 8738c2ecf20Sopenharmony_ci rc = -ENOMEM; 8748c2ecf20Sopenharmony_ci goto err_alloc; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci request->info = info; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* Fill in the packet header */ 8808c2ecf20Sopenharmony_ci packet = smbd_request_payload(request); 8818c2ecf20Sopenharmony_ci packet->credits_requested = cpu_to_le16(info->send_credit_target); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci new_credits = manage_credits_prior_sending(info); 8848c2ecf20Sopenharmony_ci atomic_add(new_credits, &info->receive_credits); 8858c2ecf20Sopenharmony_ci packet->credits_granted = cpu_to_le16(new_credits); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci info->send_immediate = false; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci packet->flags = 0; 8908c2ecf20Sopenharmony_ci if (manage_keep_alive_before_sending(info)) 8918c2ecf20Sopenharmony_ci packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci packet->reserved = 0; 8948c2ecf20Sopenharmony_ci if (!data_length) 8958c2ecf20Sopenharmony_ci packet->data_offset = 0; 8968c2ecf20Sopenharmony_ci else 8978c2ecf20Sopenharmony_ci packet->data_offset = cpu_to_le32(24); 8988c2ecf20Sopenharmony_ci packet->data_length = cpu_to_le32(data_length); 8998c2ecf20Sopenharmony_ci packet->remaining_data_length = cpu_to_le32(remaining_data_length); 9008c2ecf20Sopenharmony_ci packet->padding = 0; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci log_outgoing(INFO, "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", 9038c2ecf20Sopenharmony_ci le16_to_cpu(packet->credits_requested), 9048c2ecf20Sopenharmony_ci le16_to_cpu(packet->credits_granted), 9058c2ecf20Sopenharmony_ci le32_to_cpu(packet->data_offset), 9068c2ecf20Sopenharmony_ci le32_to_cpu(packet->data_length), 9078c2ecf20Sopenharmony_ci le32_to_cpu(packet->remaining_data_length)); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* Map the packet to DMA */ 9108c2ecf20Sopenharmony_ci header_length = sizeof(struct smbd_data_transfer); 9118c2ecf20Sopenharmony_ci /* If this is a packet without payload, don't send padding */ 9128c2ecf20Sopenharmony_ci if (!data_length) 9138c2ecf20Sopenharmony_ci header_length = offsetof(struct smbd_data_transfer, padding); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci request->num_sge = 1; 9168c2ecf20Sopenharmony_ci request->sge[0].addr = ib_dma_map_single(info->id->device, 9178c2ecf20Sopenharmony_ci (void *)packet, 9188c2ecf20Sopenharmony_ci header_length, 9198c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9208c2ecf20Sopenharmony_ci if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { 9218c2ecf20Sopenharmony_ci rc = -EIO; 9228c2ecf20Sopenharmony_ci request->sge[0].addr = 0; 9238c2ecf20Sopenharmony_ci goto err_dma; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci request->sge[0].length = header_length; 9278c2ecf20Sopenharmony_ci request->sge[0].lkey = info->pd->local_dma_lkey; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* Fill in the packet data payload */ 9308c2ecf20Sopenharmony_ci num_sgs = sgl ? sg_nents(sgl) : 0; 9318c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, num_sgs, i) { 9328c2ecf20Sopenharmony_ci request->sge[i+1].addr = 9338c2ecf20Sopenharmony_ci ib_dma_map_page(info->id->device, sg_page(sg), 9348c2ecf20Sopenharmony_ci sg->offset, sg->length, DMA_TO_DEVICE); 9358c2ecf20Sopenharmony_ci if (ib_dma_mapping_error( 9368c2ecf20Sopenharmony_ci info->id->device, request->sge[i+1].addr)) { 9378c2ecf20Sopenharmony_ci rc = -EIO; 9388c2ecf20Sopenharmony_ci request->sge[i+1].addr = 0; 9398c2ecf20Sopenharmony_ci goto err_dma; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci request->sge[i+1].length = sg->length; 9428c2ecf20Sopenharmony_ci request->sge[i+1].lkey = info->pd->local_dma_lkey; 9438c2ecf20Sopenharmony_ci request->num_sge++; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci rc = smbd_post_send(info, request); 9478c2ecf20Sopenharmony_ci if (!rc) 9488c2ecf20Sopenharmony_ci return 0; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cierr_dma: 9518c2ecf20Sopenharmony_ci for (i = 0; i < request->num_sge; i++) 9528c2ecf20Sopenharmony_ci if (request->sge[i].addr) 9538c2ecf20Sopenharmony_ci ib_dma_unmap_single(info->id->device, 9548c2ecf20Sopenharmony_ci request->sge[i].addr, 9558c2ecf20Sopenharmony_ci request->sge[i].length, 9568c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9578c2ecf20Sopenharmony_ci mempool_free(request, info->request_mempool); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* roll back receive credits and credits to be offered */ 9608c2ecf20Sopenharmony_ci spin_lock(&info->lock_new_credits_offered); 9618c2ecf20Sopenharmony_ci info->new_credits_offered += new_credits; 9628c2ecf20Sopenharmony_ci spin_unlock(&info->lock_new_credits_offered); 9638c2ecf20Sopenharmony_ci atomic_sub(new_credits, &info->receive_credits); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cierr_alloc: 9668c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&info->send_pending)) 9678c2ecf20Sopenharmony_ci wake_up(&info->wait_send_pending); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cierr_wait_send_queue: 9708c2ecf20Sopenharmony_ci /* roll back send credits and pending */ 9718c2ecf20Sopenharmony_ci atomic_inc(&info->send_credits); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cierr_wait_credit: 9748c2ecf20Sopenharmony_ci return rc; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci/* 9788c2ecf20Sopenharmony_ci * Send a page 9798c2ecf20Sopenharmony_ci * page: the page to send 9808c2ecf20Sopenharmony_ci * offset: offset in the page to send 9818c2ecf20Sopenharmony_ci * size: length in the page to send 9828c2ecf20Sopenharmony_ci * remaining_data_length: remaining data to send in this payload 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_cistatic int smbd_post_send_page(struct smbd_connection *info, struct page *page, 9858c2ecf20Sopenharmony_ci unsigned long offset, size_t size, int remaining_data_length) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct scatterlist sgl; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci sg_init_table(&sgl, 1); 9908c2ecf20Sopenharmony_ci sg_set_page(&sgl, page, size, offset); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci return smbd_post_send_sgl(info, &sgl, size, remaining_data_length); 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci/* 9968c2ecf20Sopenharmony_ci * Send an empty message 9978c2ecf20Sopenharmony_ci * Empty message is used to extend credits to peer to for keep live 9988c2ecf20Sopenharmony_ci * while there is no upper layer payload to send at the time 9998c2ecf20Sopenharmony_ci */ 10008c2ecf20Sopenharmony_cistatic int smbd_post_send_empty(struct smbd_connection *info) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci info->count_send_empty++; 10038c2ecf20Sopenharmony_ci return smbd_post_send_sgl(info, NULL, 0, 0); 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci/* 10078c2ecf20Sopenharmony_ci * Send a data buffer 10088c2ecf20Sopenharmony_ci * iov: the iov array describing the data buffers 10098c2ecf20Sopenharmony_ci * n_vec: number of iov array 10108c2ecf20Sopenharmony_ci * remaining_data_length: remaining data to send following this packet 10118c2ecf20Sopenharmony_ci * in segmented SMBD packet 10128c2ecf20Sopenharmony_ci */ 10138c2ecf20Sopenharmony_cistatic int smbd_post_send_data( 10148c2ecf20Sopenharmony_ci struct smbd_connection *info, struct kvec *iov, int n_vec, 10158c2ecf20Sopenharmony_ci int remaining_data_length) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci int i; 10188c2ecf20Sopenharmony_ci u32 data_length = 0; 10198c2ecf20Sopenharmony_ci struct scatterlist sgl[SMBDIRECT_MAX_SGE]; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (n_vec > SMBDIRECT_MAX_SGE) { 10228c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec); 10238c2ecf20Sopenharmony_ci return -EINVAL; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci sg_init_table(sgl, n_vec); 10278c2ecf20Sopenharmony_ci for (i = 0; i < n_vec; i++) { 10288c2ecf20Sopenharmony_ci data_length += iov[i].iov_len; 10298c2ecf20Sopenharmony_ci sg_set_buf(&sgl[i], iov[i].iov_base, iov[i].iov_len); 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci return smbd_post_send_sgl(info, sgl, data_length, remaining_data_length); 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci/* 10368c2ecf20Sopenharmony_ci * Post a receive request to the transport 10378c2ecf20Sopenharmony_ci * The remote peer can only send data when a receive request is posted 10388c2ecf20Sopenharmony_ci * The interaction is controlled by send/receive credit system 10398c2ecf20Sopenharmony_ci */ 10408c2ecf20Sopenharmony_cistatic int smbd_post_recv( 10418c2ecf20Sopenharmony_ci struct smbd_connection *info, struct smbd_response *response) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci struct ib_recv_wr recv_wr; 10448c2ecf20Sopenharmony_ci int rc = -EIO; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci response->sge.addr = ib_dma_map_single( 10478c2ecf20Sopenharmony_ci info->id->device, response->packet, 10488c2ecf20Sopenharmony_ci info->max_receive_size, DMA_FROM_DEVICE); 10498c2ecf20Sopenharmony_ci if (ib_dma_mapping_error(info->id->device, response->sge.addr)) 10508c2ecf20Sopenharmony_ci return rc; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci response->sge.length = info->max_receive_size; 10538c2ecf20Sopenharmony_ci response->sge.lkey = info->pd->local_dma_lkey; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci response->cqe.done = recv_done; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci recv_wr.wr_cqe = &response->cqe; 10588c2ecf20Sopenharmony_ci recv_wr.next = NULL; 10598c2ecf20Sopenharmony_ci recv_wr.sg_list = &response->sge; 10608c2ecf20Sopenharmony_ci recv_wr.num_sge = 1; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci rc = ib_post_recv(info->id->qp, &recv_wr, NULL); 10638c2ecf20Sopenharmony_ci if (rc) { 10648c2ecf20Sopenharmony_ci ib_dma_unmap_single(info->id->device, response->sge.addr, 10658c2ecf20Sopenharmony_ci response->sge.length, DMA_FROM_DEVICE); 10668c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 10678c2ecf20Sopenharmony_ci log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc); 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci return rc; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci/* Perform SMBD negotiate according to [MS-SMBD] 3.1.5.2 */ 10748c2ecf20Sopenharmony_cistatic int smbd_negotiate(struct smbd_connection *info) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci int rc; 10778c2ecf20Sopenharmony_ci struct smbd_response *response = get_receive_buffer(info); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci response->type = SMBD_NEGOTIATE_RESP; 10808c2ecf20Sopenharmony_ci rc = smbd_post_recv(info, response); 10818c2ecf20Sopenharmony_ci log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=%llx iov.length=%x iov.lkey=%x\n", 10828c2ecf20Sopenharmony_ci rc, response->sge.addr, 10838c2ecf20Sopenharmony_ci response->sge.length, response->sge.lkey); 10848c2ecf20Sopenharmony_ci if (rc) 10858c2ecf20Sopenharmony_ci return rc; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci init_completion(&info->negotiate_completion); 10888c2ecf20Sopenharmony_ci info->negotiate_done = false; 10898c2ecf20Sopenharmony_ci rc = smbd_post_send_negotiate_req(info); 10908c2ecf20Sopenharmony_ci if (rc) 10918c2ecf20Sopenharmony_ci return rc; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci rc = wait_for_completion_interruptible_timeout( 10948c2ecf20Sopenharmony_ci &info->negotiate_completion, SMBD_NEGOTIATE_TIMEOUT * HZ); 10958c2ecf20Sopenharmony_ci log_rdma_event(INFO, "wait_for_completion_timeout rc=%d\n", rc); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (info->negotiate_done) 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (rc == 0) 11018c2ecf20Sopenharmony_ci rc = -ETIMEDOUT; 11028c2ecf20Sopenharmony_ci else if (rc == -ERESTARTSYS) 11038c2ecf20Sopenharmony_ci rc = -EINTR; 11048c2ecf20Sopenharmony_ci else 11058c2ecf20Sopenharmony_ci rc = -ENOTCONN; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci return rc; 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic void put_empty_packet( 11118c2ecf20Sopenharmony_ci struct smbd_connection *info, struct smbd_response *response) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci spin_lock(&info->empty_packet_queue_lock); 11148c2ecf20Sopenharmony_ci list_add_tail(&response->list, &info->empty_packet_queue); 11158c2ecf20Sopenharmony_ci info->count_empty_packet_queue++; 11168c2ecf20Sopenharmony_ci spin_unlock(&info->empty_packet_queue_lock); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci queue_work(info->workqueue, &info->post_send_credits_work); 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci/* 11228c2ecf20Sopenharmony_ci * Implement Connection.FragmentReassemblyBuffer defined in [MS-SMBD] 3.1.1.1 11238c2ecf20Sopenharmony_ci * This is a queue for reassembling upper layer payload and present to upper 11248c2ecf20Sopenharmony_ci * layer. All the inncoming payload go to the reassembly queue, regardless of 11258c2ecf20Sopenharmony_ci * if reassembly is required. The uuper layer code reads from the queue for all 11268c2ecf20Sopenharmony_ci * incoming payloads. 11278c2ecf20Sopenharmony_ci * Put a received packet to the reassembly queue 11288c2ecf20Sopenharmony_ci * response: the packet received 11298c2ecf20Sopenharmony_ci * data_length: the size of payload in this packet 11308c2ecf20Sopenharmony_ci */ 11318c2ecf20Sopenharmony_cistatic void enqueue_reassembly( 11328c2ecf20Sopenharmony_ci struct smbd_connection *info, 11338c2ecf20Sopenharmony_ci struct smbd_response *response, 11348c2ecf20Sopenharmony_ci int data_length) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci spin_lock(&info->reassembly_queue_lock); 11378c2ecf20Sopenharmony_ci list_add_tail(&response->list, &info->reassembly_queue); 11388c2ecf20Sopenharmony_ci info->reassembly_queue_length++; 11398c2ecf20Sopenharmony_ci /* 11408c2ecf20Sopenharmony_ci * Make sure reassembly_data_length is updated after list and 11418c2ecf20Sopenharmony_ci * reassembly_queue_length are updated. On the dequeue side 11428c2ecf20Sopenharmony_ci * reassembly_data_length is checked without a lock to determine 11438c2ecf20Sopenharmony_ci * if reassembly_queue_length and list is up to date 11448c2ecf20Sopenharmony_ci */ 11458c2ecf20Sopenharmony_ci virt_wmb(); 11468c2ecf20Sopenharmony_ci info->reassembly_data_length += data_length; 11478c2ecf20Sopenharmony_ci spin_unlock(&info->reassembly_queue_lock); 11488c2ecf20Sopenharmony_ci info->count_reassembly_queue++; 11498c2ecf20Sopenharmony_ci info->count_enqueue_reassembly_queue++; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/* 11538c2ecf20Sopenharmony_ci * Get the first entry at the front of reassembly queue 11548c2ecf20Sopenharmony_ci * Caller is responsible for locking 11558c2ecf20Sopenharmony_ci * return value: the first entry if any, NULL if queue is empty 11568c2ecf20Sopenharmony_ci */ 11578c2ecf20Sopenharmony_cistatic struct smbd_response *_get_first_reassembly(struct smbd_connection *info) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct smbd_response *ret = NULL; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (!list_empty(&info->reassembly_queue)) { 11628c2ecf20Sopenharmony_ci ret = list_first_entry( 11638c2ecf20Sopenharmony_ci &info->reassembly_queue, 11648c2ecf20Sopenharmony_ci struct smbd_response, list); 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci return ret; 11678c2ecf20Sopenharmony_ci} 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_cistatic struct smbd_response *get_empty_queue_buffer( 11708c2ecf20Sopenharmony_ci struct smbd_connection *info) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci struct smbd_response *ret = NULL; 11738c2ecf20Sopenharmony_ci unsigned long flags; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->empty_packet_queue_lock, flags); 11768c2ecf20Sopenharmony_ci if (!list_empty(&info->empty_packet_queue)) { 11778c2ecf20Sopenharmony_ci ret = list_first_entry( 11788c2ecf20Sopenharmony_ci &info->empty_packet_queue, 11798c2ecf20Sopenharmony_ci struct smbd_response, list); 11808c2ecf20Sopenharmony_ci list_del(&ret->list); 11818c2ecf20Sopenharmony_ci info->count_empty_packet_queue--; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->empty_packet_queue_lock, flags); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return ret; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci/* 11898c2ecf20Sopenharmony_ci * Get a receive buffer 11908c2ecf20Sopenharmony_ci * For each remote send, we need to post a receive. The receive buffers are 11918c2ecf20Sopenharmony_ci * pre-allocated in advance. 11928c2ecf20Sopenharmony_ci * return value: the receive buffer, NULL if none is available 11938c2ecf20Sopenharmony_ci */ 11948c2ecf20Sopenharmony_cistatic struct smbd_response *get_receive_buffer(struct smbd_connection *info) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci struct smbd_response *ret = NULL; 11978c2ecf20Sopenharmony_ci unsigned long flags; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->receive_queue_lock, flags); 12008c2ecf20Sopenharmony_ci if (!list_empty(&info->receive_queue)) { 12018c2ecf20Sopenharmony_ci ret = list_first_entry( 12028c2ecf20Sopenharmony_ci &info->receive_queue, 12038c2ecf20Sopenharmony_ci struct smbd_response, list); 12048c2ecf20Sopenharmony_ci list_del(&ret->list); 12058c2ecf20Sopenharmony_ci info->count_receive_queue--; 12068c2ecf20Sopenharmony_ci info->count_get_receive_buffer++; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->receive_queue_lock, flags); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci return ret; 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci/* 12148c2ecf20Sopenharmony_ci * Return a receive buffer 12158c2ecf20Sopenharmony_ci * Upon returning of a receive buffer, we can post new receive and extend 12168c2ecf20Sopenharmony_ci * more receive credits to remote peer. This is done immediately after a 12178c2ecf20Sopenharmony_ci * receive buffer is returned. 12188c2ecf20Sopenharmony_ci */ 12198c2ecf20Sopenharmony_cistatic void put_receive_buffer( 12208c2ecf20Sopenharmony_ci struct smbd_connection *info, struct smbd_response *response) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci unsigned long flags; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci ib_dma_unmap_single(info->id->device, response->sge.addr, 12258c2ecf20Sopenharmony_ci response->sge.length, DMA_FROM_DEVICE); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->receive_queue_lock, flags); 12288c2ecf20Sopenharmony_ci list_add_tail(&response->list, &info->receive_queue); 12298c2ecf20Sopenharmony_ci info->count_receive_queue++; 12308c2ecf20Sopenharmony_ci info->count_put_receive_buffer++; 12318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->receive_queue_lock, flags); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci queue_work(info->workqueue, &info->post_send_credits_work); 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci/* Preallocate all receive buffer on transport establishment */ 12378c2ecf20Sopenharmony_cistatic int allocate_receive_buffers(struct smbd_connection *info, int num_buf) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci int i; 12408c2ecf20Sopenharmony_ci struct smbd_response *response; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&info->reassembly_queue); 12438c2ecf20Sopenharmony_ci spin_lock_init(&info->reassembly_queue_lock); 12448c2ecf20Sopenharmony_ci info->reassembly_data_length = 0; 12458c2ecf20Sopenharmony_ci info->reassembly_queue_length = 0; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&info->receive_queue); 12488c2ecf20Sopenharmony_ci spin_lock_init(&info->receive_queue_lock); 12498c2ecf20Sopenharmony_ci info->count_receive_queue = 0; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&info->empty_packet_queue); 12528c2ecf20Sopenharmony_ci spin_lock_init(&info->empty_packet_queue_lock); 12538c2ecf20Sopenharmony_ci info->count_empty_packet_queue = 0; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci init_waitqueue_head(&info->wait_receive_queues); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci for (i = 0; i < num_buf; i++) { 12588c2ecf20Sopenharmony_ci response = mempool_alloc(info->response_mempool, GFP_KERNEL); 12598c2ecf20Sopenharmony_ci if (!response) 12608c2ecf20Sopenharmony_ci goto allocate_failed; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci response->info = info; 12638c2ecf20Sopenharmony_ci list_add_tail(&response->list, &info->receive_queue); 12648c2ecf20Sopenharmony_ci info->count_receive_queue++; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return 0; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ciallocate_failed: 12708c2ecf20Sopenharmony_ci while (!list_empty(&info->receive_queue)) { 12718c2ecf20Sopenharmony_ci response = list_first_entry( 12728c2ecf20Sopenharmony_ci &info->receive_queue, 12738c2ecf20Sopenharmony_ci struct smbd_response, list); 12748c2ecf20Sopenharmony_ci list_del(&response->list); 12758c2ecf20Sopenharmony_ci info->count_receive_queue--; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci mempool_free(response, info->response_mempool); 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci return -ENOMEM; 12808c2ecf20Sopenharmony_ci} 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic void destroy_receive_buffers(struct smbd_connection *info) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci struct smbd_response *response; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci while ((response = get_receive_buffer(info))) 12878c2ecf20Sopenharmony_ci mempool_free(response, info->response_mempool); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci while ((response = get_empty_queue_buffer(info))) 12908c2ecf20Sopenharmony_ci mempool_free(response, info->response_mempool); 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */ 12948c2ecf20Sopenharmony_cistatic void idle_connection_timer(struct work_struct *work) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci struct smbd_connection *info = container_of( 12978c2ecf20Sopenharmony_ci work, struct smbd_connection, 12988c2ecf20Sopenharmony_ci idle_timer_work.work); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci if (info->keep_alive_requested != KEEP_ALIVE_NONE) { 13018c2ecf20Sopenharmony_ci log_keep_alive(ERR, 13028c2ecf20Sopenharmony_ci "error status info->keep_alive_requested=%d\n", 13038c2ecf20Sopenharmony_ci info->keep_alive_requested); 13048c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 13058c2ecf20Sopenharmony_ci return; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci log_keep_alive(INFO, "about to send an empty idle message\n"); 13098c2ecf20Sopenharmony_ci smbd_post_send_empty(info); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci /* Setup the next idle timeout work */ 13128c2ecf20Sopenharmony_ci queue_delayed_work(info->workqueue, &info->idle_timer_work, 13138c2ecf20Sopenharmony_ci info->keep_alive_interval*HZ); 13148c2ecf20Sopenharmony_ci} 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci/* 13178c2ecf20Sopenharmony_ci * Destroy the transport and related RDMA and memory resources 13188c2ecf20Sopenharmony_ci * Need to go through all the pending counters and make sure on one is using 13198c2ecf20Sopenharmony_ci * the transport while it is destroyed 13208c2ecf20Sopenharmony_ci */ 13218c2ecf20Sopenharmony_civoid smbd_destroy(struct TCP_Server_Info *server) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci struct smbd_connection *info = server->smbd_conn; 13248c2ecf20Sopenharmony_ci struct smbd_response *response; 13258c2ecf20Sopenharmony_ci unsigned long flags; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci if (!info) { 13288c2ecf20Sopenharmony_ci log_rdma_event(INFO, "rdma session already destroyed\n"); 13298c2ecf20Sopenharmony_ci return; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci log_rdma_event(INFO, "destroying rdma session\n"); 13338c2ecf20Sopenharmony_ci if (info->transport_status != SMBD_DISCONNECTED) { 13348c2ecf20Sopenharmony_ci rdma_disconnect(server->smbd_conn->id); 13358c2ecf20Sopenharmony_ci log_rdma_event(INFO, "wait for transport being disconnected\n"); 13368c2ecf20Sopenharmony_ci wait_event_interruptible( 13378c2ecf20Sopenharmony_ci info->disconn_wait, 13388c2ecf20Sopenharmony_ci info->transport_status == SMBD_DISCONNECTED); 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci log_rdma_event(INFO, "destroying qp\n"); 13428c2ecf20Sopenharmony_ci ib_drain_qp(info->id->qp); 13438c2ecf20Sopenharmony_ci rdma_destroy_qp(info->id); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci log_rdma_event(INFO, "cancelling idle timer\n"); 13468c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&info->idle_timer_work); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci log_rdma_event(INFO, "wait for all send posted to IB to finish\n"); 13498c2ecf20Sopenharmony_ci wait_event(info->wait_send_pending, 13508c2ecf20Sopenharmony_ci atomic_read(&info->send_pending) == 0); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* It's not posssible for upper layer to get to reassembly */ 13538c2ecf20Sopenharmony_ci log_rdma_event(INFO, "drain the reassembly queue\n"); 13548c2ecf20Sopenharmony_ci do { 13558c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->reassembly_queue_lock, flags); 13568c2ecf20Sopenharmony_ci response = _get_first_reassembly(info); 13578c2ecf20Sopenharmony_ci if (response) { 13588c2ecf20Sopenharmony_ci list_del(&response->list); 13598c2ecf20Sopenharmony_ci spin_unlock_irqrestore( 13608c2ecf20Sopenharmony_ci &info->reassembly_queue_lock, flags); 13618c2ecf20Sopenharmony_ci put_receive_buffer(info, response); 13628c2ecf20Sopenharmony_ci } else 13638c2ecf20Sopenharmony_ci spin_unlock_irqrestore( 13648c2ecf20Sopenharmony_ci &info->reassembly_queue_lock, flags); 13658c2ecf20Sopenharmony_ci } while (response); 13668c2ecf20Sopenharmony_ci info->reassembly_data_length = 0; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci log_rdma_event(INFO, "free receive buffers\n"); 13698c2ecf20Sopenharmony_ci wait_event(info->wait_receive_queues, 13708c2ecf20Sopenharmony_ci info->count_receive_queue + info->count_empty_packet_queue 13718c2ecf20Sopenharmony_ci == info->receive_credit_max); 13728c2ecf20Sopenharmony_ci destroy_receive_buffers(info); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci /* 13758c2ecf20Sopenharmony_ci * For performance reasons, memory registration and deregistration 13768c2ecf20Sopenharmony_ci * are not locked by srv_mutex. It is possible some processes are 13778c2ecf20Sopenharmony_ci * blocked on transport srv_mutex while holding memory registration. 13788c2ecf20Sopenharmony_ci * Release the transport srv_mutex to allow them to hit the failure 13798c2ecf20Sopenharmony_ci * path when sending data, and then release memory registartions. 13808c2ecf20Sopenharmony_ci */ 13818c2ecf20Sopenharmony_ci log_rdma_event(INFO, "freeing mr list\n"); 13828c2ecf20Sopenharmony_ci wake_up_interruptible_all(&info->wait_mr); 13838c2ecf20Sopenharmony_ci while (atomic_read(&info->mr_used_count)) { 13848c2ecf20Sopenharmony_ci mutex_unlock(&server->srv_mutex); 13858c2ecf20Sopenharmony_ci msleep(1000); 13868c2ecf20Sopenharmony_ci mutex_lock(&server->srv_mutex); 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci destroy_mr_list(info); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci ib_free_cq(info->send_cq); 13918c2ecf20Sopenharmony_ci ib_free_cq(info->recv_cq); 13928c2ecf20Sopenharmony_ci ib_dealloc_pd(info->pd); 13938c2ecf20Sopenharmony_ci rdma_destroy_id(info->id); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci /* free mempools */ 13968c2ecf20Sopenharmony_ci mempool_destroy(info->request_mempool); 13978c2ecf20Sopenharmony_ci kmem_cache_destroy(info->request_cache); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci mempool_destroy(info->response_mempool); 14008c2ecf20Sopenharmony_ci kmem_cache_destroy(info->response_cache); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci info->transport_status = SMBD_DESTROYED; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci destroy_workqueue(info->workqueue); 14058c2ecf20Sopenharmony_ci log_rdma_event(INFO, "rdma session destroyed\n"); 14068c2ecf20Sopenharmony_ci kfree(info); 14078c2ecf20Sopenharmony_ci server->smbd_conn = NULL; 14088c2ecf20Sopenharmony_ci} 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci/* 14118c2ecf20Sopenharmony_ci * Reconnect this SMBD connection, called from upper layer 14128c2ecf20Sopenharmony_ci * return value: 0 on success, or actual error code 14138c2ecf20Sopenharmony_ci */ 14148c2ecf20Sopenharmony_ciint smbd_reconnect(struct TCP_Server_Info *server) 14158c2ecf20Sopenharmony_ci{ 14168c2ecf20Sopenharmony_ci log_rdma_event(INFO, "reconnecting rdma session\n"); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci if (!server->smbd_conn) { 14198c2ecf20Sopenharmony_ci log_rdma_event(INFO, "rdma session already destroyed\n"); 14208c2ecf20Sopenharmony_ci goto create_conn; 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* 14248c2ecf20Sopenharmony_ci * This is possible if transport is disconnected and we haven't received 14258c2ecf20Sopenharmony_ci * notification from RDMA, but upper layer has detected timeout 14268c2ecf20Sopenharmony_ci */ 14278c2ecf20Sopenharmony_ci if (server->smbd_conn->transport_status == SMBD_CONNECTED) { 14288c2ecf20Sopenharmony_ci log_rdma_event(INFO, "disconnecting transport\n"); 14298c2ecf20Sopenharmony_ci smbd_destroy(server); 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_cicreate_conn: 14338c2ecf20Sopenharmony_ci log_rdma_event(INFO, "creating rdma session\n"); 14348c2ecf20Sopenharmony_ci server->smbd_conn = smbd_get_connection( 14358c2ecf20Sopenharmony_ci server, (struct sockaddr *) &server->dstaddr); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (server->smbd_conn) 14388c2ecf20Sopenharmony_ci cifs_dbg(VFS, "RDMA transport re-established\n"); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci return server->smbd_conn ? 0 : -ENOENT; 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic void destroy_caches_and_workqueue(struct smbd_connection *info) 14448c2ecf20Sopenharmony_ci{ 14458c2ecf20Sopenharmony_ci destroy_receive_buffers(info); 14468c2ecf20Sopenharmony_ci destroy_workqueue(info->workqueue); 14478c2ecf20Sopenharmony_ci mempool_destroy(info->response_mempool); 14488c2ecf20Sopenharmony_ci kmem_cache_destroy(info->response_cache); 14498c2ecf20Sopenharmony_ci mempool_destroy(info->request_mempool); 14508c2ecf20Sopenharmony_ci kmem_cache_destroy(info->request_cache); 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci#define MAX_NAME_LEN 80 14548c2ecf20Sopenharmony_cistatic int allocate_caches_and_workqueue(struct smbd_connection *info) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci char name[MAX_NAME_LEN]; 14578c2ecf20Sopenharmony_ci int rc; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info); 14608c2ecf20Sopenharmony_ci info->request_cache = 14618c2ecf20Sopenharmony_ci kmem_cache_create( 14628c2ecf20Sopenharmony_ci name, 14638c2ecf20Sopenharmony_ci sizeof(struct smbd_request) + 14648c2ecf20Sopenharmony_ci sizeof(struct smbd_data_transfer), 14658c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, NULL); 14668c2ecf20Sopenharmony_ci if (!info->request_cache) 14678c2ecf20Sopenharmony_ci return -ENOMEM; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci info->request_mempool = 14708c2ecf20Sopenharmony_ci mempool_create(info->send_credit_target, mempool_alloc_slab, 14718c2ecf20Sopenharmony_ci mempool_free_slab, info->request_cache); 14728c2ecf20Sopenharmony_ci if (!info->request_mempool) 14738c2ecf20Sopenharmony_ci goto out1; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info); 14768c2ecf20Sopenharmony_ci info->response_cache = 14778c2ecf20Sopenharmony_ci kmem_cache_create( 14788c2ecf20Sopenharmony_ci name, 14798c2ecf20Sopenharmony_ci sizeof(struct smbd_response) + 14808c2ecf20Sopenharmony_ci info->max_receive_size, 14818c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, NULL); 14828c2ecf20Sopenharmony_ci if (!info->response_cache) 14838c2ecf20Sopenharmony_ci goto out2; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci info->response_mempool = 14868c2ecf20Sopenharmony_ci mempool_create(info->receive_credit_max, mempool_alloc_slab, 14878c2ecf20Sopenharmony_ci mempool_free_slab, info->response_cache); 14888c2ecf20Sopenharmony_ci if (!info->response_mempool) 14898c2ecf20Sopenharmony_ci goto out3; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci scnprintf(name, MAX_NAME_LEN, "smbd_%p", info); 14928c2ecf20Sopenharmony_ci info->workqueue = create_workqueue(name); 14938c2ecf20Sopenharmony_ci if (!info->workqueue) 14948c2ecf20Sopenharmony_ci goto out4; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci rc = allocate_receive_buffers(info, info->receive_credit_max); 14978c2ecf20Sopenharmony_ci if (rc) { 14988c2ecf20Sopenharmony_ci log_rdma_event(ERR, "failed to allocate receive buffers\n"); 14998c2ecf20Sopenharmony_ci goto out5; 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci return 0; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ciout5: 15058c2ecf20Sopenharmony_ci destroy_workqueue(info->workqueue); 15068c2ecf20Sopenharmony_ciout4: 15078c2ecf20Sopenharmony_ci mempool_destroy(info->response_mempool); 15088c2ecf20Sopenharmony_ciout3: 15098c2ecf20Sopenharmony_ci kmem_cache_destroy(info->response_cache); 15108c2ecf20Sopenharmony_ciout2: 15118c2ecf20Sopenharmony_ci mempool_destroy(info->request_mempool); 15128c2ecf20Sopenharmony_ciout1: 15138c2ecf20Sopenharmony_ci kmem_cache_destroy(info->request_cache); 15148c2ecf20Sopenharmony_ci return -ENOMEM; 15158c2ecf20Sopenharmony_ci} 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci/* Create a SMBD connection, called by upper layer */ 15188c2ecf20Sopenharmony_cistatic struct smbd_connection *_smbd_get_connection( 15198c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, struct sockaddr *dstaddr, int port) 15208c2ecf20Sopenharmony_ci{ 15218c2ecf20Sopenharmony_ci int rc; 15228c2ecf20Sopenharmony_ci struct smbd_connection *info; 15238c2ecf20Sopenharmony_ci struct rdma_conn_param conn_param; 15248c2ecf20Sopenharmony_ci struct ib_qp_init_attr qp_attr; 15258c2ecf20Sopenharmony_ci struct sockaddr_in *addr_in = (struct sockaddr_in *) dstaddr; 15268c2ecf20Sopenharmony_ci struct ib_port_immutable port_immutable; 15278c2ecf20Sopenharmony_ci u32 ird_ord_hdr[2]; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci info = kzalloc(sizeof(struct smbd_connection), GFP_KERNEL); 15308c2ecf20Sopenharmony_ci if (!info) 15318c2ecf20Sopenharmony_ci return NULL; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci info->transport_status = SMBD_CONNECTING; 15348c2ecf20Sopenharmony_ci rc = smbd_ia_open(info, dstaddr, port); 15358c2ecf20Sopenharmony_ci if (rc) { 15368c2ecf20Sopenharmony_ci log_rdma_event(INFO, "smbd_ia_open rc=%d\n", rc); 15378c2ecf20Sopenharmony_ci goto create_id_failed; 15388c2ecf20Sopenharmony_ci } 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci if (smbd_send_credit_target > info->id->device->attrs.max_cqe || 15418c2ecf20Sopenharmony_ci smbd_send_credit_target > info->id->device->attrs.max_qp_wr) { 15428c2ecf20Sopenharmony_ci log_rdma_event(ERR, "consider lowering send_credit_target = %d. Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", 15438c2ecf20Sopenharmony_ci smbd_send_credit_target, 15448c2ecf20Sopenharmony_ci info->id->device->attrs.max_cqe, 15458c2ecf20Sopenharmony_ci info->id->device->attrs.max_qp_wr); 15468c2ecf20Sopenharmony_ci goto config_failed; 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci if (smbd_receive_credit_max > info->id->device->attrs.max_cqe || 15508c2ecf20Sopenharmony_ci smbd_receive_credit_max > info->id->device->attrs.max_qp_wr) { 15518c2ecf20Sopenharmony_ci log_rdma_event(ERR, "consider lowering receive_credit_max = %d. Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", 15528c2ecf20Sopenharmony_ci smbd_receive_credit_max, 15538c2ecf20Sopenharmony_ci info->id->device->attrs.max_cqe, 15548c2ecf20Sopenharmony_ci info->id->device->attrs.max_qp_wr); 15558c2ecf20Sopenharmony_ci goto config_failed; 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci info->receive_credit_max = smbd_receive_credit_max; 15598c2ecf20Sopenharmony_ci info->send_credit_target = smbd_send_credit_target; 15608c2ecf20Sopenharmony_ci info->max_send_size = smbd_max_send_size; 15618c2ecf20Sopenharmony_ci info->max_fragmented_recv_size = smbd_max_fragmented_recv_size; 15628c2ecf20Sopenharmony_ci info->max_receive_size = smbd_max_receive_size; 15638c2ecf20Sopenharmony_ci info->keep_alive_interval = smbd_keep_alive_interval; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci if (info->id->device->attrs.max_send_sge < SMBDIRECT_MAX_SGE) { 15668c2ecf20Sopenharmony_ci log_rdma_event(ERR, 15678c2ecf20Sopenharmony_ci "warning: device max_send_sge = %d too small\n", 15688c2ecf20Sopenharmony_ci info->id->device->attrs.max_send_sge); 15698c2ecf20Sopenharmony_ci log_rdma_event(ERR, "Queue Pair creation may fail\n"); 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci if (info->id->device->attrs.max_recv_sge < SMBDIRECT_MAX_SGE) { 15728c2ecf20Sopenharmony_ci log_rdma_event(ERR, 15738c2ecf20Sopenharmony_ci "warning: device max_recv_sge = %d too small\n", 15748c2ecf20Sopenharmony_ci info->id->device->attrs.max_recv_sge); 15758c2ecf20Sopenharmony_ci log_rdma_event(ERR, "Queue Pair creation may fail\n"); 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci info->send_cq = NULL; 15798c2ecf20Sopenharmony_ci info->recv_cq = NULL; 15808c2ecf20Sopenharmony_ci info->send_cq = 15818c2ecf20Sopenharmony_ci ib_alloc_cq_any(info->id->device, info, 15828c2ecf20Sopenharmony_ci info->send_credit_target, IB_POLL_SOFTIRQ); 15838c2ecf20Sopenharmony_ci if (IS_ERR(info->send_cq)) { 15848c2ecf20Sopenharmony_ci info->send_cq = NULL; 15858c2ecf20Sopenharmony_ci goto alloc_cq_failed; 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci info->recv_cq = 15898c2ecf20Sopenharmony_ci ib_alloc_cq_any(info->id->device, info, 15908c2ecf20Sopenharmony_ci info->receive_credit_max, IB_POLL_SOFTIRQ); 15918c2ecf20Sopenharmony_ci if (IS_ERR(info->recv_cq)) { 15928c2ecf20Sopenharmony_ci info->recv_cq = NULL; 15938c2ecf20Sopenharmony_ci goto alloc_cq_failed; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci memset(&qp_attr, 0, sizeof(qp_attr)); 15978c2ecf20Sopenharmony_ci qp_attr.event_handler = smbd_qp_async_error_upcall; 15988c2ecf20Sopenharmony_ci qp_attr.qp_context = info; 15998c2ecf20Sopenharmony_ci qp_attr.cap.max_send_wr = info->send_credit_target; 16008c2ecf20Sopenharmony_ci qp_attr.cap.max_recv_wr = info->receive_credit_max; 16018c2ecf20Sopenharmony_ci qp_attr.cap.max_send_sge = SMBDIRECT_MAX_SGE; 16028c2ecf20Sopenharmony_ci qp_attr.cap.max_recv_sge = SMBDIRECT_MAX_SGE; 16038c2ecf20Sopenharmony_ci qp_attr.cap.max_inline_data = 0; 16048c2ecf20Sopenharmony_ci qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; 16058c2ecf20Sopenharmony_ci qp_attr.qp_type = IB_QPT_RC; 16068c2ecf20Sopenharmony_ci qp_attr.send_cq = info->send_cq; 16078c2ecf20Sopenharmony_ci qp_attr.recv_cq = info->recv_cq; 16088c2ecf20Sopenharmony_ci qp_attr.port_num = ~0; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci rc = rdma_create_qp(info->id, info->pd, &qp_attr); 16118c2ecf20Sopenharmony_ci if (rc) { 16128c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_create_qp failed %i\n", rc); 16138c2ecf20Sopenharmony_ci goto create_qp_failed; 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci memset(&conn_param, 0, sizeof(conn_param)); 16178c2ecf20Sopenharmony_ci conn_param.initiator_depth = 0; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci conn_param.responder_resources = 16208c2ecf20Sopenharmony_ci info->id->device->attrs.max_qp_rd_atom 16218c2ecf20Sopenharmony_ci < SMBD_CM_RESPONDER_RESOURCES ? 16228c2ecf20Sopenharmony_ci info->id->device->attrs.max_qp_rd_atom : 16238c2ecf20Sopenharmony_ci SMBD_CM_RESPONDER_RESOURCES; 16248c2ecf20Sopenharmony_ci info->responder_resources = conn_param.responder_resources; 16258c2ecf20Sopenharmony_ci log_rdma_mr(INFO, "responder_resources=%d\n", 16268c2ecf20Sopenharmony_ci info->responder_resources); 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci /* Need to send IRD/ORD in private data for iWARP */ 16298c2ecf20Sopenharmony_ci info->id->device->ops.get_port_immutable( 16308c2ecf20Sopenharmony_ci info->id->device, info->id->port_num, &port_immutable); 16318c2ecf20Sopenharmony_ci if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { 16328c2ecf20Sopenharmony_ci ird_ord_hdr[0] = info->responder_resources; 16338c2ecf20Sopenharmony_ci ird_ord_hdr[1] = 1; 16348c2ecf20Sopenharmony_ci conn_param.private_data = ird_ord_hdr; 16358c2ecf20Sopenharmony_ci conn_param.private_data_len = sizeof(ird_ord_hdr); 16368c2ecf20Sopenharmony_ci } else { 16378c2ecf20Sopenharmony_ci conn_param.private_data = NULL; 16388c2ecf20Sopenharmony_ci conn_param.private_data_len = 0; 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci conn_param.retry_count = SMBD_CM_RETRY; 16428c2ecf20Sopenharmony_ci conn_param.rnr_retry_count = SMBD_CM_RNR_RETRY; 16438c2ecf20Sopenharmony_ci conn_param.flow_control = 0; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci log_rdma_event(INFO, "connecting to IP %pI4 port %d\n", 16468c2ecf20Sopenharmony_ci &addr_in->sin_addr, port); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci init_waitqueue_head(&info->conn_wait); 16498c2ecf20Sopenharmony_ci init_waitqueue_head(&info->disconn_wait); 16508c2ecf20Sopenharmony_ci init_waitqueue_head(&info->wait_reassembly_queue); 16518c2ecf20Sopenharmony_ci rc = rdma_connect(info->id, &conn_param); 16528c2ecf20Sopenharmony_ci if (rc) { 16538c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc); 16548c2ecf20Sopenharmony_ci goto rdma_connect_failed; 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci wait_event_interruptible( 16588c2ecf20Sopenharmony_ci info->conn_wait, info->transport_status != SMBD_CONNECTING); 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci if (info->transport_status != SMBD_CONNECTED) { 16618c2ecf20Sopenharmony_ci log_rdma_event(ERR, "rdma_connect failed port=%d\n", port); 16628c2ecf20Sopenharmony_ci goto rdma_connect_failed; 16638c2ecf20Sopenharmony_ci } 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci log_rdma_event(INFO, "rdma_connect connected\n"); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci rc = allocate_caches_and_workqueue(info); 16688c2ecf20Sopenharmony_ci if (rc) { 16698c2ecf20Sopenharmony_ci log_rdma_event(ERR, "cache allocation failed\n"); 16708c2ecf20Sopenharmony_ci goto allocate_cache_failed; 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci init_waitqueue_head(&info->wait_send_queue); 16748c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer); 16758c2ecf20Sopenharmony_ci queue_delayed_work(info->workqueue, &info->idle_timer_work, 16768c2ecf20Sopenharmony_ci info->keep_alive_interval*HZ); 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci init_waitqueue_head(&info->wait_send_pending); 16798c2ecf20Sopenharmony_ci atomic_set(&info->send_pending, 0); 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci init_waitqueue_head(&info->wait_post_send); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work); 16848c2ecf20Sopenharmony_ci INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits); 16858c2ecf20Sopenharmony_ci info->new_credits_offered = 0; 16868c2ecf20Sopenharmony_ci spin_lock_init(&info->lock_new_credits_offered); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci rc = smbd_negotiate(info); 16898c2ecf20Sopenharmony_ci if (rc) { 16908c2ecf20Sopenharmony_ci log_rdma_event(ERR, "smbd_negotiate rc=%d\n", rc); 16918c2ecf20Sopenharmony_ci goto negotiation_failed; 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci rc = allocate_mr_list(info); 16958c2ecf20Sopenharmony_ci if (rc) { 16968c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "memory registration allocation failed\n"); 16978c2ecf20Sopenharmony_ci goto allocate_mr_failed; 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci return info; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ciallocate_mr_failed: 17038c2ecf20Sopenharmony_ci /* At this point, need to a full transport shutdown */ 17048c2ecf20Sopenharmony_ci server->smbd_conn = info; 17058c2ecf20Sopenharmony_ci smbd_destroy(server); 17068c2ecf20Sopenharmony_ci return NULL; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_cinegotiation_failed: 17098c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&info->idle_timer_work); 17108c2ecf20Sopenharmony_ci destroy_caches_and_workqueue(info); 17118c2ecf20Sopenharmony_ci info->transport_status = SMBD_NEGOTIATE_FAILED; 17128c2ecf20Sopenharmony_ci init_waitqueue_head(&info->conn_wait); 17138c2ecf20Sopenharmony_ci rdma_disconnect(info->id); 17148c2ecf20Sopenharmony_ci wait_event(info->conn_wait, 17158c2ecf20Sopenharmony_ci info->transport_status == SMBD_DISCONNECTED); 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ciallocate_cache_failed: 17188c2ecf20Sopenharmony_cirdma_connect_failed: 17198c2ecf20Sopenharmony_ci rdma_destroy_qp(info->id); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_cicreate_qp_failed: 17228c2ecf20Sopenharmony_cialloc_cq_failed: 17238c2ecf20Sopenharmony_ci if (info->send_cq) 17248c2ecf20Sopenharmony_ci ib_free_cq(info->send_cq); 17258c2ecf20Sopenharmony_ci if (info->recv_cq) 17268c2ecf20Sopenharmony_ci ib_free_cq(info->recv_cq); 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ciconfig_failed: 17298c2ecf20Sopenharmony_ci ib_dealloc_pd(info->pd); 17308c2ecf20Sopenharmony_ci rdma_destroy_id(info->id); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_cicreate_id_failed: 17338c2ecf20Sopenharmony_ci kfree(info); 17348c2ecf20Sopenharmony_ci return NULL; 17358c2ecf20Sopenharmony_ci} 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_cistruct smbd_connection *smbd_get_connection( 17388c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, struct sockaddr *dstaddr) 17398c2ecf20Sopenharmony_ci{ 17408c2ecf20Sopenharmony_ci struct smbd_connection *ret; 17418c2ecf20Sopenharmony_ci int port = SMBD_PORT; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_citry_again: 17448c2ecf20Sopenharmony_ci ret = _smbd_get_connection(server, dstaddr, port); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci /* Try SMB_PORT if SMBD_PORT doesn't work */ 17478c2ecf20Sopenharmony_ci if (!ret && port == SMBD_PORT) { 17488c2ecf20Sopenharmony_ci port = SMB_PORT; 17498c2ecf20Sopenharmony_ci goto try_again; 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci return ret; 17528c2ecf20Sopenharmony_ci} 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci/* 17558c2ecf20Sopenharmony_ci * Receive data from receive reassembly queue 17568c2ecf20Sopenharmony_ci * All the incoming data packets are placed in reassembly queue 17578c2ecf20Sopenharmony_ci * buf: the buffer to read data into 17588c2ecf20Sopenharmony_ci * size: the length of data to read 17598c2ecf20Sopenharmony_ci * return value: actual data read 17608c2ecf20Sopenharmony_ci * Note: this implementation copies the data from reassebmly queue to receive 17618c2ecf20Sopenharmony_ci * buffers used by upper layer. This is not the optimal code path. A better way 17628c2ecf20Sopenharmony_ci * to do it is to not have upper layer allocate its receive buffers but rather 17638c2ecf20Sopenharmony_ci * borrow the buffer from reassembly queue, and return it after data is 17648c2ecf20Sopenharmony_ci * consumed. But this will require more changes to upper layer code, and also 17658c2ecf20Sopenharmony_ci * need to consider packet boundaries while they still being reassembled. 17668c2ecf20Sopenharmony_ci */ 17678c2ecf20Sopenharmony_cistatic int smbd_recv_buf(struct smbd_connection *info, char *buf, 17688c2ecf20Sopenharmony_ci unsigned int size) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci struct smbd_response *response; 17718c2ecf20Sopenharmony_ci struct smbd_data_transfer *data_transfer; 17728c2ecf20Sopenharmony_ci int to_copy, to_read, data_read, offset; 17738c2ecf20Sopenharmony_ci u32 data_length, remaining_data_length, data_offset; 17748c2ecf20Sopenharmony_ci int rc; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ciagain: 17778c2ecf20Sopenharmony_ci /* 17788c2ecf20Sopenharmony_ci * No need to hold the reassembly queue lock all the time as we are 17798c2ecf20Sopenharmony_ci * the only one reading from the front of the queue. The transport 17808c2ecf20Sopenharmony_ci * may add more entries to the back of the queue at the same time 17818c2ecf20Sopenharmony_ci */ 17828c2ecf20Sopenharmony_ci log_read(INFO, "size=%d info->reassembly_data_length=%d\n", size, 17838c2ecf20Sopenharmony_ci info->reassembly_data_length); 17848c2ecf20Sopenharmony_ci if (info->reassembly_data_length >= size) { 17858c2ecf20Sopenharmony_ci int queue_length; 17868c2ecf20Sopenharmony_ci int queue_removed = 0; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci /* 17898c2ecf20Sopenharmony_ci * Need to make sure reassembly_data_length is read before 17908c2ecf20Sopenharmony_ci * reading reassembly_queue_length and calling 17918c2ecf20Sopenharmony_ci * _get_first_reassembly. This call is lock free 17928c2ecf20Sopenharmony_ci * as we never read at the end of the queue which are being 17938c2ecf20Sopenharmony_ci * updated in SOFTIRQ as more data is received 17948c2ecf20Sopenharmony_ci */ 17958c2ecf20Sopenharmony_ci virt_rmb(); 17968c2ecf20Sopenharmony_ci queue_length = info->reassembly_queue_length; 17978c2ecf20Sopenharmony_ci data_read = 0; 17988c2ecf20Sopenharmony_ci to_read = size; 17998c2ecf20Sopenharmony_ci offset = info->first_entry_offset; 18008c2ecf20Sopenharmony_ci while (data_read < size) { 18018c2ecf20Sopenharmony_ci response = _get_first_reassembly(info); 18028c2ecf20Sopenharmony_ci data_transfer = smbd_response_payload(response); 18038c2ecf20Sopenharmony_ci data_length = le32_to_cpu(data_transfer->data_length); 18048c2ecf20Sopenharmony_ci remaining_data_length = 18058c2ecf20Sopenharmony_ci le32_to_cpu( 18068c2ecf20Sopenharmony_ci data_transfer->remaining_data_length); 18078c2ecf20Sopenharmony_ci data_offset = le32_to_cpu(data_transfer->data_offset); 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci /* 18108c2ecf20Sopenharmony_ci * The upper layer expects RFC1002 length at the 18118c2ecf20Sopenharmony_ci * beginning of the payload. Return it to indicate 18128c2ecf20Sopenharmony_ci * the total length of the packet. This minimize the 18138c2ecf20Sopenharmony_ci * change to upper layer packet processing logic. This 18148c2ecf20Sopenharmony_ci * will be eventually remove when an intermediate 18158c2ecf20Sopenharmony_ci * transport layer is added 18168c2ecf20Sopenharmony_ci */ 18178c2ecf20Sopenharmony_ci if (response->first_segment && size == 4) { 18188c2ecf20Sopenharmony_ci unsigned int rfc1002_len = 18198c2ecf20Sopenharmony_ci data_length + remaining_data_length; 18208c2ecf20Sopenharmony_ci *((__be32 *)buf) = cpu_to_be32(rfc1002_len); 18218c2ecf20Sopenharmony_ci data_read = 4; 18228c2ecf20Sopenharmony_ci response->first_segment = false; 18238c2ecf20Sopenharmony_ci log_read(INFO, "returning rfc1002 length %d\n", 18248c2ecf20Sopenharmony_ci rfc1002_len); 18258c2ecf20Sopenharmony_ci goto read_rfc1002_done; 18268c2ecf20Sopenharmony_ci } 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci to_copy = min_t(int, data_length - offset, to_read); 18298c2ecf20Sopenharmony_ci memcpy( 18308c2ecf20Sopenharmony_ci buf + data_read, 18318c2ecf20Sopenharmony_ci (char *)data_transfer + data_offset + offset, 18328c2ecf20Sopenharmony_ci to_copy); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci /* move on to the next buffer? */ 18358c2ecf20Sopenharmony_ci if (to_copy == data_length - offset) { 18368c2ecf20Sopenharmony_ci queue_length--; 18378c2ecf20Sopenharmony_ci /* 18388c2ecf20Sopenharmony_ci * No need to lock if we are not at the 18398c2ecf20Sopenharmony_ci * end of the queue 18408c2ecf20Sopenharmony_ci */ 18418c2ecf20Sopenharmony_ci if (queue_length) 18428c2ecf20Sopenharmony_ci list_del(&response->list); 18438c2ecf20Sopenharmony_ci else { 18448c2ecf20Sopenharmony_ci spin_lock_irq( 18458c2ecf20Sopenharmony_ci &info->reassembly_queue_lock); 18468c2ecf20Sopenharmony_ci list_del(&response->list); 18478c2ecf20Sopenharmony_ci spin_unlock_irq( 18488c2ecf20Sopenharmony_ci &info->reassembly_queue_lock); 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci queue_removed++; 18518c2ecf20Sopenharmony_ci info->count_reassembly_queue--; 18528c2ecf20Sopenharmony_ci info->count_dequeue_reassembly_queue++; 18538c2ecf20Sopenharmony_ci put_receive_buffer(info, response); 18548c2ecf20Sopenharmony_ci offset = 0; 18558c2ecf20Sopenharmony_ci log_read(INFO, "put_receive_buffer offset=0\n"); 18568c2ecf20Sopenharmony_ci } else 18578c2ecf20Sopenharmony_ci offset += to_copy; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci to_read -= to_copy; 18608c2ecf20Sopenharmony_ci data_read += to_copy; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci log_read(INFO, "_get_first_reassembly memcpy %d bytes data_transfer_length-offset=%d after that to_read=%d data_read=%d offset=%d\n", 18638c2ecf20Sopenharmony_ci to_copy, data_length - offset, 18648c2ecf20Sopenharmony_ci to_read, data_read, offset); 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci spin_lock_irq(&info->reassembly_queue_lock); 18688c2ecf20Sopenharmony_ci info->reassembly_data_length -= data_read; 18698c2ecf20Sopenharmony_ci info->reassembly_queue_length -= queue_removed; 18708c2ecf20Sopenharmony_ci spin_unlock_irq(&info->reassembly_queue_lock); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci info->first_entry_offset = offset; 18738c2ecf20Sopenharmony_ci log_read(INFO, "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", 18748c2ecf20Sopenharmony_ci data_read, info->reassembly_data_length, 18758c2ecf20Sopenharmony_ci info->first_entry_offset); 18768c2ecf20Sopenharmony_ciread_rfc1002_done: 18778c2ecf20Sopenharmony_ci return data_read; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci log_read(INFO, "wait_event on more data\n"); 18818c2ecf20Sopenharmony_ci rc = wait_event_interruptible( 18828c2ecf20Sopenharmony_ci info->wait_reassembly_queue, 18838c2ecf20Sopenharmony_ci info->reassembly_data_length >= size || 18848c2ecf20Sopenharmony_ci info->transport_status != SMBD_CONNECTED); 18858c2ecf20Sopenharmony_ci /* Don't return any data if interrupted */ 18868c2ecf20Sopenharmony_ci if (rc) 18878c2ecf20Sopenharmony_ci return rc; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci if (info->transport_status != SMBD_CONNECTED) { 18908c2ecf20Sopenharmony_ci log_read(ERR, "disconnected\n"); 18918c2ecf20Sopenharmony_ci return -ECONNABORTED; 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci goto again; 18958c2ecf20Sopenharmony_ci} 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci/* 18988c2ecf20Sopenharmony_ci * Receive a page from receive reassembly queue 18998c2ecf20Sopenharmony_ci * page: the page to read data into 19008c2ecf20Sopenharmony_ci * to_read: the length of data to read 19018c2ecf20Sopenharmony_ci * return value: actual data read 19028c2ecf20Sopenharmony_ci */ 19038c2ecf20Sopenharmony_cistatic int smbd_recv_page(struct smbd_connection *info, 19048c2ecf20Sopenharmony_ci struct page *page, unsigned int page_offset, 19058c2ecf20Sopenharmony_ci unsigned int to_read) 19068c2ecf20Sopenharmony_ci{ 19078c2ecf20Sopenharmony_ci int ret; 19088c2ecf20Sopenharmony_ci char *to_address; 19098c2ecf20Sopenharmony_ci void *page_address; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci /* make sure we have the page ready for read */ 19128c2ecf20Sopenharmony_ci ret = wait_event_interruptible( 19138c2ecf20Sopenharmony_ci info->wait_reassembly_queue, 19148c2ecf20Sopenharmony_ci info->reassembly_data_length >= to_read || 19158c2ecf20Sopenharmony_ci info->transport_status != SMBD_CONNECTED); 19168c2ecf20Sopenharmony_ci if (ret) 19178c2ecf20Sopenharmony_ci return ret; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci /* now we can read from reassembly queue and not sleep */ 19208c2ecf20Sopenharmony_ci page_address = kmap_atomic(page); 19218c2ecf20Sopenharmony_ci to_address = (char *) page_address + page_offset; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci log_read(INFO, "reading from page=%p address=%p to_read=%d\n", 19248c2ecf20Sopenharmony_ci page, to_address, to_read); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci ret = smbd_recv_buf(info, to_address, to_read); 19278c2ecf20Sopenharmony_ci kunmap_atomic(page_address); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci return ret; 19308c2ecf20Sopenharmony_ci} 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci/* 19338c2ecf20Sopenharmony_ci * Receive data from transport 19348c2ecf20Sopenharmony_ci * msg: a msghdr point to the buffer, can be ITER_KVEC or ITER_BVEC 19358c2ecf20Sopenharmony_ci * return: total bytes read, or 0. SMB Direct will not do partial read. 19368c2ecf20Sopenharmony_ci */ 19378c2ecf20Sopenharmony_ciint smbd_recv(struct smbd_connection *info, struct msghdr *msg) 19388c2ecf20Sopenharmony_ci{ 19398c2ecf20Sopenharmony_ci char *buf; 19408c2ecf20Sopenharmony_ci struct page *page; 19418c2ecf20Sopenharmony_ci unsigned int to_read, page_offset; 19428c2ecf20Sopenharmony_ci int rc; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci if (iov_iter_rw(&msg->msg_iter) == WRITE) { 19458c2ecf20Sopenharmony_ci /* It's a bug in upper layer to get there */ 19468c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Invalid msg iter dir %u\n", 19478c2ecf20Sopenharmony_ci iov_iter_rw(&msg->msg_iter)); 19488c2ecf20Sopenharmony_ci rc = -EINVAL; 19498c2ecf20Sopenharmony_ci goto out; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci switch (iov_iter_type(&msg->msg_iter)) { 19538c2ecf20Sopenharmony_ci case ITER_KVEC: 19548c2ecf20Sopenharmony_ci buf = msg->msg_iter.kvec->iov_base; 19558c2ecf20Sopenharmony_ci to_read = msg->msg_iter.kvec->iov_len; 19568c2ecf20Sopenharmony_ci rc = smbd_recv_buf(info, buf, to_read); 19578c2ecf20Sopenharmony_ci break; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci case ITER_BVEC: 19608c2ecf20Sopenharmony_ci page = msg->msg_iter.bvec->bv_page; 19618c2ecf20Sopenharmony_ci page_offset = msg->msg_iter.bvec->bv_offset; 19628c2ecf20Sopenharmony_ci to_read = msg->msg_iter.bvec->bv_len; 19638c2ecf20Sopenharmony_ci rc = smbd_recv_page(info, page, page_offset, to_read); 19648c2ecf20Sopenharmony_ci break; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci default: 19678c2ecf20Sopenharmony_ci /* It's a bug in upper layer to get there */ 19688c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Invalid msg type %d\n", 19698c2ecf20Sopenharmony_ci iov_iter_type(&msg->msg_iter)); 19708c2ecf20Sopenharmony_ci rc = -EINVAL; 19718c2ecf20Sopenharmony_ci } 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ciout: 19748c2ecf20Sopenharmony_ci /* SMBDirect will read it all or nothing */ 19758c2ecf20Sopenharmony_ci if (rc > 0) 19768c2ecf20Sopenharmony_ci msg->msg_iter.count = 0; 19778c2ecf20Sopenharmony_ci return rc; 19788c2ecf20Sopenharmony_ci} 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci/* 19818c2ecf20Sopenharmony_ci * Send data to transport 19828c2ecf20Sopenharmony_ci * Each rqst is transported as a SMBDirect payload 19838c2ecf20Sopenharmony_ci * rqst: the data to write 19848c2ecf20Sopenharmony_ci * return value: 0 if successfully write, otherwise error code 19858c2ecf20Sopenharmony_ci */ 19868c2ecf20Sopenharmony_ciint smbd_send(struct TCP_Server_Info *server, 19878c2ecf20Sopenharmony_ci int num_rqst, struct smb_rqst *rqst_array) 19888c2ecf20Sopenharmony_ci{ 19898c2ecf20Sopenharmony_ci struct smbd_connection *info = server->smbd_conn; 19908c2ecf20Sopenharmony_ci struct kvec vec; 19918c2ecf20Sopenharmony_ci int nvecs; 19928c2ecf20Sopenharmony_ci int size; 19938c2ecf20Sopenharmony_ci unsigned int buflen, remaining_data_length; 19948c2ecf20Sopenharmony_ci int start, i, j; 19958c2ecf20Sopenharmony_ci int max_iov_size = 19968c2ecf20Sopenharmony_ci info->max_send_size - sizeof(struct smbd_data_transfer); 19978c2ecf20Sopenharmony_ci struct kvec *iov; 19988c2ecf20Sopenharmony_ci int rc; 19998c2ecf20Sopenharmony_ci struct smb_rqst *rqst; 20008c2ecf20Sopenharmony_ci int rqst_idx; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci if (info->transport_status != SMBD_CONNECTED) { 20038c2ecf20Sopenharmony_ci rc = -EAGAIN; 20048c2ecf20Sopenharmony_ci goto done; 20058c2ecf20Sopenharmony_ci } 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci /* 20088c2ecf20Sopenharmony_ci * Add in the page array if there is one. The caller needs to set 20098c2ecf20Sopenharmony_ci * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and 20108c2ecf20Sopenharmony_ci * ends at page boundary 20118c2ecf20Sopenharmony_ci */ 20128c2ecf20Sopenharmony_ci remaining_data_length = 0; 20138c2ecf20Sopenharmony_ci for (i = 0; i < num_rqst; i++) 20148c2ecf20Sopenharmony_ci remaining_data_length += smb_rqst_len(server, &rqst_array[i]); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci if (remaining_data_length > info->max_fragmented_send_size) { 20178c2ecf20Sopenharmony_ci log_write(ERR, "payload size %d > max size %d\n", 20188c2ecf20Sopenharmony_ci remaining_data_length, info->max_fragmented_send_size); 20198c2ecf20Sopenharmony_ci rc = -EINVAL; 20208c2ecf20Sopenharmony_ci goto done; 20218c2ecf20Sopenharmony_ci } 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci log_write(INFO, "num_rqst=%d total length=%u\n", 20248c2ecf20Sopenharmony_ci num_rqst, remaining_data_length); 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci rqst_idx = 0; 20278c2ecf20Sopenharmony_cinext_rqst: 20288c2ecf20Sopenharmony_ci rqst = &rqst_array[rqst_idx]; 20298c2ecf20Sopenharmony_ci iov = rqst->rq_iov; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Sending smb (RDMA): idx=%d smb_len=%lu\n", 20328c2ecf20Sopenharmony_ci rqst_idx, smb_rqst_len(server, rqst)); 20338c2ecf20Sopenharmony_ci for (i = 0; i < rqst->rq_nvec; i++) 20348c2ecf20Sopenharmony_ci dump_smb(iov[i].iov_base, iov[i].iov_len); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci log_write(INFO, "rqst_idx=%d nvec=%d rqst->rq_npages=%d rq_pagesz=%d rq_tailsz=%d buflen=%lu\n", 20388c2ecf20Sopenharmony_ci rqst_idx, rqst->rq_nvec, rqst->rq_npages, rqst->rq_pagesz, 20398c2ecf20Sopenharmony_ci rqst->rq_tailsz, smb_rqst_len(server, rqst)); 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci start = i = 0; 20428c2ecf20Sopenharmony_ci buflen = 0; 20438c2ecf20Sopenharmony_ci while (true) { 20448c2ecf20Sopenharmony_ci buflen += iov[i].iov_len; 20458c2ecf20Sopenharmony_ci if (buflen > max_iov_size) { 20468c2ecf20Sopenharmony_ci if (i > start) { 20478c2ecf20Sopenharmony_ci remaining_data_length -= 20488c2ecf20Sopenharmony_ci (buflen-iov[i].iov_len); 20498c2ecf20Sopenharmony_ci log_write(INFO, "sending iov[] from start=%d i=%d nvecs=%d remaining_data_length=%d\n", 20508c2ecf20Sopenharmony_ci start, i, i - start, 20518c2ecf20Sopenharmony_ci remaining_data_length); 20528c2ecf20Sopenharmony_ci rc = smbd_post_send_data( 20538c2ecf20Sopenharmony_ci info, &iov[start], i-start, 20548c2ecf20Sopenharmony_ci remaining_data_length); 20558c2ecf20Sopenharmony_ci if (rc) 20568c2ecf20Sopenharmony_ci goto done; 20578c2ecf20Sopenharmony_ci } else { 20588c2ecf20Sopenharmony_ci /* iov[start] is too big, break it */ 20598c2ecf20Sopenharmony_ci nvecs = (buflen+max_iov_size-1)/max_iov_size; 20608c2ecf20Sopenharmony_ci log_write(INFO, "iov[%d] iov_base=%p buflen=%d break to %d vectors\n", 20618c2ecf20Sopenharmony_ci start, iov[start].iov_base, 20628c2ecf20Sopenharmony_ci buflen, nvecs); 20638c2ecf20Sopenharmony_ci for (j = 0; j < nvecs; j++) { 20648c2ecf20Sopenharmony_ci vec.iov_base = 20658c2ecf20Sopenharmony_ci (char *)iov[start].iov_base + 20668c2ecf20Sopenharmony_ci j*max_iov_size; 20678c2ecf20Sopenharmony_ci vec.iov_len = max_iov_size; 20688c2ecf20Sopenharmony_ci if (j == nvecs-1) 20698c2ecf20Sopenharmony_ci vec.iov_len = 20708c2ecf20Sopenharmony_ci buflen - 20718c2ecf20Sopenharmony_ci max_iov_size*(nvecs-1); 20728c2ecf20Sopenharmony_ci remaining_data_length -= vec.iov_len; 20738c2ecf20Sopenharmony_ci log_write(INFO, 20748c2ecf20Sopenharmony_ci "sending vec j=%d iov_base=%p iov_len=%zu remaining_data_length=%d\n", 20758c2ecf20Sopenharmony_ci j, vec.iov_base, vec.iov_len, 20768c2ecf20Sopenharmony_ci remaining_data_length); 20778c2ecf20Sopenharmony_ci rc = smbd_post_send_data( 20788c2ecf20Sopenharmony_ci info, &vec, 1, 20798c2ecf20Sopenharmony_ci remaining_data_length); 20808c2ecf20Sopenharmony_ci if (rc) 20818c2ecf20Sopenharmony_ci goto done; 20828c2ecf20Sopenharmony_ci } 20838c2ecf20Sopenharmony_ci i++; 20848c2ecf20Sopenharmony_ci if (i == rqst->rq_nvec) 20858c2ecf20Sopenharmony_ci break; 20868c2ecf20Sopenharmony_ci } 20878c2ecf20Sopenharmony_ci start = i; 20888c2ecf20Sopenharmony_ci buflen = 0; 20898c2ecf20Sopenharmony_ci } else { 20908c2ecf20Sopenharmony_ci i++; 20918c2ecf20Sopenharmony_ci if (i == rqst->rq_nvec) { 20928c2ecf20Sopenharmony_ci /* send out all remaining vecs */ 20938c2ecf20Sopenharmony_ci remaining_data_length -= buflen; 20948c2ecf20Sopenharmony_ci log_write(INFO, "sending iov[] from start=%d i=%d nvecs=%d remaining_data_length=%d\n", 20958c2ecf20Sopenharmony_ci start, i, i - start, 20968c2ecf20Sopenharmony_ci remaining_data_length); 20978c2ecf20Sopenharmony_ci rc = smbd_post_send_data(info, &iov[start], 20988c2ecf20Sopenharmony_ci i-start, remaining_data_length); 20998c2ecf20Sopenharmony_ci if (rc) 21008c2ecf20Sopenharmony_ci goto done; 21018c2ecf20Sopenharmony_ci break; 21028c2ecf20Sopenharmony_ci } 21038c2ecf20Sopenharmony_ci } 21048c2ecf20Sopenharmony_ci log_write(INFO, "looping i=%d buflen=%d\n", i, buflen); 21058c2ecf20Sopenharmony_ci } 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci /* now sending pages if there are any */ 21088c2ecf20Sopenharmony_ci for (i = 0; i < rqst->rq_npages; i++) { 21098c2ecf20Sopenharmony_ci unsigned int offset; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci rqst_page_get_length(rqst, i, &buflen, &offset); 21128c2ecf20Sopenharmony_ci nvecs = (buflen + max_iov_size - 1) / max_iov_size; 21138c2ecf20Sopenharmony_ci log_write(INFO, "sending pages buflen=%d nvecs=%d\n", 21148c2ecf20Sopenharmony_ci buflen, nvecs); 21158c2ecf20Sopenharmony_ci for (j = 0; j < nvecs; j++) { 21168c2ecf20Sopenharmony_ci size = max_iov_size; 21178c2ecf20Sopenharmony_ci if (j == nvecs-1) 21188c2ecf20Sopenharmony_ci size = buflen - j*max_iov_size; 21198c2ecf20Sopenharmony_ci remaining_data_length -= size; 21208c2ecf20Sopenharmony_ci log_write(INFO, "sending pages i=%d offset=%d size=%d remaining_data_length=%d\n", 21218c2ecf20Sopenharmony_ci i, j * max_iov_size + offset, size, 21228c2ecf20Sopenharmony_ci remaining_data_length); 21238c2ecf20Sopenharmony_ci rc = smbd_post_send_page( 21248c2ecf20Sopenharmony_ci info, rqst->rq_pages[i], 21258c2ecf20Sopenharmony_ci j*max_iov_size + offset, 21268c2ecf20Sopenharmony_ci size, remaining_data_length); 21278c2ecf20Sopenharmony_ci if (rc) 21288c2ecf20Sopenharmony_ci goto done; 21298c2ecf20Sopenharmony_ci } 21308c2ecf20Sopenharmony_ci } 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci rqst_idx++; 21338c2ecf20Sopenharmony_ci if (rqst_idx < num_rqst) 21348c2ecf20Sopenharmony_ci goto next_rqst; 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_cidone: 21378c2ecf20Sopenharmony_ci /* 21388c2ecf20Sopenharmony_ci * As an optimization, we don't wait for individual I/O to finish 21398c2ecf20Sopenharmony_ci * before sending the next one. 21408c2ecf20Sopenharmony_ci * Send them all and wait for pending send count to get to 0 21418c2ecf20Sopenharmony_ci * that means all the I/Os have been out and we are good to return 21428c2ecf20Sopenharmony_ci */ 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci wait_event(info->wait_send_pending, 21458c2ecf20Sopenharmony_ci atomic_read(&info->send_pending) == 0); 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci return rc; 21488c2ecf20Sopenharmony_ci} 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_cistatic void register_mr_done(struct ib_cq *cq, struct ib_wc *wc) 21518c2ecf20Sopenharmony_ci{ 21528c2ecf20Sopenharmony_ci struct smbd_mr *mr; 21538c2ecf20Sopenharmony_ci struct ib_cqe *cqe; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci if (wc->status) { 21568c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "status=%d\n", wc->status); 21578c2ecf20Sopenharmony_ci cqe = wc->wr_cqe; 21588c2ecf20Sopenharmony_ci mr = container_of(cqe, struct smbd_mr, cqe); 21598c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(mr->conn); 21608c2ecf20Sopenharmony_ci } 21618c2ecf20Sopenharmony_ci} 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci/* 21648c2ecf20Sopenharmony_ci * The work queue function that recovers MRs 21658c2ecf20Sopenharmony_ci * We need to call ib_dereg_mr() and ib_alloc_mr() before this MR can be used 21668c2ecf20Sopenharmony_ci * again. Both calls are slow, so finish them in a workqueue. This will not 21678c2ecf20Sopenharmony_ci * block I/O path. 21688c2ecf20Sopenharmony_ci * There is one workqueue that recovers MRs, there is no need to lock as the 21698c2ecf20Sopenharmony_ci * I/O requests calling smbd_register_mr will never update the links in the 21708c2ecf20Sopenharmony_ci * mr_list. 21718c2ecf20Sopenharmony_ci */ 21728c2ecf20Sopenharmony_cistatic void smbd_mr_recovery_work(struct work_struct *work) 21738c2ecf20Sopenharmony_ci{ 21748c2ecf20Sopenharmony_ci struct smbd_connection *info = 21758c2ecf20Sopenharmony_ci container_of(work, struct smbd_connection, mr_recovery_work); 21768c2ecf20Sopenharmony_ci struct smbd_mr *smbdirect_mr; 21778c2ecf20Sopenharmony_ci int rc; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci list_for_each_entry(smbdirect_mr, &info->mr_list, list) { 21808c2ecf20Sopenharmony_ci if (smbdirect_mr->state == MR_ERROR) { 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci /* recover this MR entry */ 21838c2ecf20Sopenharmony_ci rc = ib_dereg_mr(smbdirect_mr->mr); 21848c2ecf20Sopenharmony_ci if (rc) { 21858c2ecf20Sopenharmony_ci log_rdma_mr(ERR, 21868c2ecf20Sopenharmony_ci "ib_dereg_mr failed rc=%x\n", 21878c2ecf20Sopenharmony_ci rc); 21888c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 21898c2ecf20Sopenharmony_ci continue; 21908c2ecf20Sopenharmony_ci } 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci smbdirect_mr->mr = ib_alloc_mr( 21938c2ecf20Sopenharmony_ci info->pd, info->mr_type, 21948c2ecf20Sopenharmony_ci info->max_frmr_depth); 21958c2ecf20Sopenharmony_ci if (IS_ERR(smbdirect_mr->mr)) { 21968c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", 21978c2ecf20Sopenharmony_ci info->mr_type, 21988c2ecf20Sopenharmony_ci info->max_frmr_depth); 21998c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 22008c2ecf20Sopenharmony_ci continue; 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci } else 22038c2ecf20Sopenharmony_ci /* This MR is being used, don't recover it */ 22048c2ecf20Sopenharmony_ci continue; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci smbdirect_mr->state = MR_READY; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci /* smbdirect_mr->state is updated by this function 22098c2ecf20Sopenharmony_ci * and is read and updated by I/O issuing CPUs trying 22108c2ecf20Sopenharmony_ci * to get a MR, the call to atomic_inc_return 22118c2ecf20Sopenharmony_ci * implicates a memory barrier and guarantees this 22128c2ecf20Sopenharmony_ci * value is updated before waking up any calls to 22138c2ecf20Sopenharmony_ci * get_mr() from the I/O issuing CPUs 22148c2ecf20Sopenharmony_ci */ 22158c2ecf20Sopenharmony_ci if (atomic_inc_return(&info->mr_ready_count) == 1) 22168c2ecf20Sopenharmony_ci wake_up_interruptible(&info->wait_mr); 22178c2ecf20Sopenharmony_ci } 22188c2ecf20Sopenharmony_ci} 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_cistatic void destroy_mr_list(struct smbd_connection *info) 22218c2ecf20Sopenharmony_ci{ 22228c2ecf20Sopenharmony_ci struct smbd_mr *mr, *tmp; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci cancel_work_sync(&info->mr_recovery_work); 22258c2ecf20Sopenharmony_ci list_for_each_entry_safe(mr, tmp, &info->mr_list, list) { 22268c2ecf20Sopenharmony_ci if (mr->state == MR_INVALIDATED) 22278c2ecf20Sopenharmony_ci ib_dma_unmap_sg(info->id->device, mr->sgl, 22288c2ecf20Sopenharmony_ci mr->sgl_count, mr->dir); 22298c2ecf20Sopenharmony_ci ib_dereg_mr(mr->mr); 22308c2ecf20Sopenharmony_ci kfree(mr->sgl); 22318c2ecf20Sopenharmony_ci kfree(mr); 22328c2ecf20Sopenharmony_ci } 22338c2ecf20Sopenharmony_ci} 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci/* 22368c2ecf20Sopenharmony_ci * Allocate MRs used for RDMA read/write 22378c2ecf20Sopenharmony_ci * The number of MRs will not exceed hardware capability in responder_resources 22388c2ecf20Sopenharmony_ci * All MRs are kept in mr_list. The MR can be recovered after it's used 22398c2ecf20Sopenharmony_ci * Recovery is done in smbd_mr_recovery_work. The content of list entry changes 22408c2ecf20Sopenharmony_ci * as MRs are used and recovered for I/O, but the list links will not change 22418c2ecf20Sopenharmony_ci */ 22428c2ecf20Sopenharmony_cistatic int allocate_mr_list(struct smbd_connection *info) 22438c2ecf20Sopenharmony_ci{ 22448c2ecf20Sopenharmony_ci int i; 22458c2ecf20Sopenharmony_ci struct smbd_mr *smbdirect_mr, *tmp; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&info->mr_list); 22488c2ecf20Sopenharmony_ci init_waitqueue_head(&info->wait_mr); 22498c2ecf20Sopenharmony_ci spin_lock_init(&info->mr_list_lock); 22508c2ecf20Sopenharmony_ci atomic_set(&info->mr_ready_count, 0); 22518c2ecf20Sopenharmony_ci atomic_set(&info->mr_used_count, 0); 22528c2ecf20Sopenharmony_ci init_waitqueue_head(&info->wait_for_mr_cleanup); 22538c2ecf20Sopenharmony_ci INIT_WORK(&info->mr_recovery_work, smbd_mr_recovery_work); 22548c2ecf20Sopenharmony_ci /* Allocate more MRs (2x) than hardware responder_resources */ 22558c2ecf20Sopenharmony_ci for (i = 0; i < info->responder_resources * 2; i++) { 22568c2ecf20Sopenharmony_ci smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL); 22578c2ecf20Sopenharmony_ci if (!smbdirect_mr) 22588c2ecf20Sopenharmony_ci goto out; 22598c2ecf20Sopenharmony_ci smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type, 22608c2ecf20Sopenharmony_ci info->max_frmr_depth); 22618c2ecf20Sopenharmony_ci if (IS_ERR(smbdirect_mr->mr)) { 22628c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", 22638c2ecf20Sopenharmony_ci info->mr_type, info->max_frmr_depth); 22648c2ecf20Sopenharmony_ci goto out; 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci smbdirect_mr->sgl = kcalloc( 22678c2ecf20Sopenharmony_ci info->max_frmr_depth, 22688c2ecf20Sopenharmony_ci sizeof(struct scatterlist), 22698c2ecf20Sopenharmony_ci GFP_KERNEL); 22708c2ecf20Sopenharmony_ci if (!smbdirect_mr->sgl) { 22718c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "failed to allocate sgl\n"); 22728c2ecf20Sopenharmony_ci ib_dereg_mr(smbdirect_mr->mr); 22738c2ecf20Sopenharmony_ci goto out; 22748c2ecf20Sopenharmony_ci } 22758c2ecf20Sopenharmony_ci smbdirect_mr->state = MR_READY; 22768c2ecf20Sopenharmony_ci smbdirect_mr->conn = info; 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci list_add_tail(&smbdirect_mr->list, &info->mr_list); 22798c2ecf20Sopenharmony_ci atomic_inc(&info->mr_ready_count); 22808c2ecf20Sopenharmony_ci } 22818c2ecf20Sopenharmony_ci return 0; 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ciout: 22848c2ecf20Sopenharmony_ci kfree(smbdirect_mr); 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) { 22878c2ecf20Sopenharmony_ci list_del(&smbdirect_mr->list); 22888c2ecf20Sopenharmony_ci ib_dereg_mr(smbdirect_mr->mr); 22898c2ecf20Sopenharmony_ci kfree(smbdirect_mr->sgl); 22908c2ecf20Sopenharmony_ci kfree(smbdirect_mr); 22918c2ecf20Sopenharmony_ci } 22928c2ecf20Sopenharmony_ci return -ENOMEM; 22938c2ecf20Sopenharmony_ci} 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci/* 22968c2ecf20Sopenharmony_ci * Get a MR from mr_list. This function waits until there is at least one 22978c2ecf20Sopenharmony_ci * MR available in the list. It may access the list while the 22988c2ecf20Sopenharmony_ci * smbd_mr_recovery_work is recovering the MR list. This doesn't need a lock 22998c2ecf20Sopenharmony_ci * as they never modify the same places. However, there may be several CPUs 23008c2ecf20Sopenharmony_ci * issueing I/O trying to get MR at the same time, mr_list_lock is used to 23018c2ecf20Sopenharmony_ci * protect this situation. 23028c2ecf20Sopenharmony_ci */ 23038c2ecf20Sopenharmony_cistatic struct smbd_mr *get_mr(struct smbd_connection *info) 23048c2ecf20Sopenharmony_ci{ 23058c2ecf20Sopenharmony_ci struct smbd_mr *ret; 23068c2ecf20Sopenharmony_ci int rc; 23078c2ecf20Sopenharmony_ciagain: 23088c2ecf20Sopenharmony_ci rc = wait_event_interruptible(info->wait_mr, 23098c2ecf20Sopenharmony_ci atomic_read(&info->mr_ready_count) || 23108c2ecf20Sopenharmony_ci info->transport_status != SMBD_CONNECTED); 23118c2ecf20Sopenharmony_ci if (rc) { 23128c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "wait_event_interruptible rc=%x\n", rc); 23138c2ecf20Sopenharmony_ci return NULL; 23148c2ecf20Sopenharmony_ci } 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci if (info->transport_status != SMBD_CONNECTED) { 23178c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "info->transport_status=%x\n", 23188c2ecf20Sopenharmony_ci info->transport_status); 23198c2ecf20Sopenharmony_ci return NULL; 23208c2ecf20Sopenharmony_ci } 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci spin_lock(&info->mr_list_lock); 23238c2ecf20Sopenharmony_ci list_for_each_entry(ret, &info->mr_list, list) { 23248c2ecf20Sopenharmony_ci if (ret->state == MR_READY) { 23258c2ecf20Sopenharmony_ci ret->state = MR_REGISTERED; 23268c2ecf20Sopenharmony_ci spin_unlock(&info->mr_list_lock); 23278c2ecf20Sopenharmony_ci atomic_dec(&info->mr_ready_count); 23288c2ecf20Sopenharmony_ci atomic_inc(&info->mr_used_count); 23298c2ecf20Sopenharmony_ci return ret; 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci } 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci spin_unlock(&info->mr_list_lock); 23348c2ecf20Sopenharmony_ci /* 23358c2ecf20Sopenharmony_ci * It is possible that we could fail to get MR because other processes may 23368c2ecf20Sopenharmony_ci * try to acquire a MR at the same time. If this is the case, retry it. 23378c2ecf20Sopenharmony_ci */ 23388c2ecf20Sopenharmony_ci goto again; 23398c2ecf20Sopenharmony_ci} 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci/* 23428c2ecf20Sopenharmony_ci * Register memory for RDMA read/write 23438c2ecf20Sopenharmony_ci * pages[]: the list of pages to register memory with 23448c2ecf20Sopenharmony_ci * num_pages: the number of pages to register 23458c2ecf20Sopenharmony_ci * tailsz: if non-zero, the bytes to register in the last page 23468c2ecf20Sopenharmony_ci * writing: true if this is a RDMA write (SMB read), false for RDMA read 23478c2ecf20Sopenharmony_ci * need_invalidate: true if this MR needs to be locally invalidated after I/O 23488c2ecf20Sopenharmony_ci * return value: the MR registered, NULL if failed. 23498c2ecf20Sopenharmony_ci */ 23508c2ecf20Sopenharmony_cistruct smbd_mr *smbd_register_mr( 23518c2ecf20Sopenharmony_ci struct smbd_connection *info, struct page *pages[], int num_pages, 23528c2ecf20Sopenharmony_ci int offset, int tailsz, bool writing, bool need_invalidate) 23538c2ecf20Sopenharmony_ci{ 23548c2ecf20Sopenharmony_ci struct smbd_mr *smbdirect_mr; 23558c2ecf20Sopenharmony_ci int rc, i; 23568c2ecf20Sopenharmony_ci enum dma_data_direction dir; 23578c2ecf20Sopenharmony_ci struct ib_reg_wr *reg_wr; 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci if (num_pages > info->max_frmr_depth) { 23608c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "num_pages=%d max_frmr_depth=%d\n", 23618c2ecf20Sopenharmony_ci num_pages, info->max_frmr_depth); 23628c2ecf20Sopenharmony_ci return NULL; 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci smbdirect_mr = get_mr(info); 23668c2ecf20Sopenharmony_ci if (!smbdirect_mr) { 23678c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "get_mr returning NULL\n"); 23688c2ecf20Sopenharmony_ci return NULL; 23698c2ecf20Sopenharmony_ci } 23708c2ecf20Sopenharmony_ci smbdirect_mr->need_invalidate = need_invalidate; 23718c2ecf20Sopenharmony_ci smbdirect_mr->sgl_count = num_pages; 23728c2ecf20Sopenharmony_ci sg_init_table(smbdirect_mr->sgl, num_pages); 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci log_rdma_mr(INFO, "num_pages=0x%x offset=0x%x tailsz=0x%x\n", 23758c2ecf20Sopenharmony_ci num_pages, offset, tailsz); 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci if (num_pages == 1) { 23788c2ecf20Sopenharmony_ci sg_set_page(&smbdirect_mr->sgl[0], pages[0], tailsz, offset); 23798c2ecf20Sopenharmony_ci goto skip_multiple_pages; 23808c2ecf20Sopenharmony_ci } 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci /* We have at least two pages to register */ 23838c2ecf20Sopenharmony_ci sg_set_page( 23848c2ecf20Sopenharmony_ci &smbdirect_mr->sgl[0], pages[0], PAGE_SIZE - offset, offset); 23858c2ecf20Sopenharmony_ci i = 1; 23868c2ecf20Sopenharmony_ci while (i < num_pages - 1) { 23878c2ecf20Sopenharmony_ci sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0); 23888c2ecf20Sopenharmony_ci i++; 23898c2ecf20Sopenharmony_ci } 23908c2ecf20Sopenharmony_ci sg_set_page(&smbdirect_mr->sgl[i], pages[i], 23918c2ecf20Sopenharmony_ci tailsz ? tailsz : PAGE_SIZE, 0); 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ciskip_multiple_pages: 23948c2ecf20Sopenharmony_ci dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE; 23958c2ecf20Sopenharmony_ci smbdirect_mr->dir = dir; 23968c2ecf20Sopenharmony_ci rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgl, num_pages, dir); 23978c2ecf20Sopenharmony_ci if (!rc) { 23988c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n", 23998c2ecf20Sopenharmony_ci num_pages, dir, rc); 24008c2ecf20Sopenharmony_ci goto dma_map_error; 24018c2ecf20Sopenharmony_ci } 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgl, num_pages, 24048c2ecf20Sopenharmony_ci NULL, PAGE_SIZE); 24058c2ecf20Sopenharmony_ci if (rc != num_pages) { 24068c2ecf20Sopenharmony_ci log_rdma_mr(ERR, 24078c2ecf20Sopenharmony_ci "ib_map_mr_sg failed rc = %d num_pages = %x\n", 24088c2ecf20Sopenharmony_ci rc, num_pages); 24098c2ecf20Sopenharmony_ci goto map_mr_error; 24108c2ecf20Sopenharmony_ci } 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci ib_update_fast_reg_key(smbdirect_mr->mr, 24138c2ecf20Sopenharmony_ci ib_inc_rkey(smbdirect_mr->mr->rkey)); 24148c2ecf20Sopenharmony_ci reg_wr = &smbdirect_mr->wr; 24158c2ecf20Sopenharmony_ci reg_wr->wr.opcode = IB_WR_REG_MR; 24168c2ecf20Sopenharmony_ci smbdirect_mr->cqe.done = register_mr_done; 24178c2ecf20Sopenharmony_ci reg_wr->wr.wr_cqe = &smbdirect_mr->cqe; 24188c2ecf20Sopenharmony_ci reg_wr->wr.num_sge = 0; 24198c2ecf20Sopenharmony_ci reg_wr->wr.send_flags = IB_SEND_SIGNALED; 24208c2ecf20Sopenharmony_ci reg_wr->mr = smbdirect_mr->mr; 24218c2ecf20Sopenharmony_ci reg_wr->key = smbdirect_mr->mr->rkey; 24228c2ecf20Sopenharmony_ci reg_wr->access = writing ? 24238c2ecf20Sopenharmony_ci IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE : 24248c2ecf20Sopenharmony_ci IB_ACCESS_REMOTE_READ; 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci /* 24278c2ecf20Sopenharmony_ci * There is no need for waiting for complemtion on ib_post_send 24288c2ecf20Sopenharmony_ci * on IB_WR_REG_MR. Hardware enforces a barrier and order of execution 24298c2ecf20Sopenharmony_ci * on the next ib_post_send when we actaully send I/O to remote peer 24308c2ecf20Sopenharmony_ci */ 24318c2ecf20Sopenharmony_ci rc = ib_post_send(info->id->qp, ®_wr->wr, NULL); 24328c2ecf20Sopenharmony_ci if (!rc) 24338c2ecf20Sopenharmony_ci return smbdirect_mr; 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "ib_post_send failed rc=%x reg_wr->key=%x\n", 24368c2ecf20Sopenharmony_ci rc, reg_wr->key); 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci /* If all failed, attempt to recover this MR by setting it MR_ERROR*/ 24398c2ecf20Sopenharmony_cimap_mr_error: 24408c2ecf20Sopenharmony_ci ib_dma_unmap_sg(info->id->device, smbdirect_mr->sgl, 24418c2ecf20Sopenharmony_ci smbdirect_mr->sgl_count, smbdirect_mr->dir); 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_cidma_map_error: 24448c2ecf20Sopenharmony_ci smbdirect_mr->state = MR_ERROR; 24458c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&info->mr_used_count)) 24468c2ecf20Sopenharmony_ci wake_up(&info->wait_for_mr_cleanup); 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci return NULL; 24518c2ecf20Sopenharmony_ci} 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_cistatic void local_inv_done(struct ib_cq *cq, struct ib_wc *wc) 24548c2ecf20Sopenharmony_ci{ 24558c2ecf20Sopenharmony_ci struct smbd_mr *smbdirect_mr; 24568c2ecf20Sopenharmony_ci struct ib_cqe *cqe; 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci cqe = wc->wr_cqe; 24598c2ecf20Sopenharmony_ci smbdirect_mr = container_of(cqe, struct smbd_mr, cqe); 24608c2ecf20Sopenharmony_ci smbdirect_mr->state = MR_INVALIDATED; 24618c2ecf20Sopenharmony_ci if (wc->status != IB_WC_SUCCESS) { 24628c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "invalidate failed status=%x\n", wc->status); 24638c2ecf20Sopenharmony_ci smbdirect_mr->state = MR_ERROR; 24648c2ecf20Sopenharmony_ci } 24658c2ecf20Sopenharmony_ci complete(&smbdirect_mr->invalidate_done); 24668c2ecf20Sopenharmony_ci} 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci/* 24698c2ecf20Sopenharmony_ci * Deregister a MR after I/O is done 24708c2ecf20Sopenharmony_ci * This function may wait if remote invalidation is not used 24718c2ecf20Sopenharmony_ci * and we have to locally invalidate the buffer to prevent data is being 24728c2ecf20Sopenharmony_ci * modified by remote peer after upper layer consumes it 24738c2ecf20Sopenharmony_ci */ 24748c2ecf20Sopenharmony_ciint smbd_deregister_mr(struct smbd_mr *smbdirect_mr) 24758c2ecf20Sopenharmony_ci{ 24768c2ecf20Sopenharmony_ci struct ib_send_wr *wr; 24778c2ecf20Sopenharmony_ci struct smbd_connection *info = smbdirect_mr->conn; 24788c2ecf20Sopenharmony_ci int rc = 0; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci if (smbdirect_mr->need_invalidate) { 24818c2ecf20Sopenharmony_ci /* Need to finish local invalidation before returning */ 24828c2ecf20Sopenharmony_ci wr = &smbdirect_mr->inv_wr; 24838c2ecf20Sopenharmony_ci wr->opcode = IB_WR_LOCAL_INV; 24848c2ecf20Sopenharmony_ci smbdirect_mr->cqe.done = local_inv_done; 24858c2ecf20Sopenharmony_ci wr->wr_cqe = &smbdirect_mr->cqe; 24868c2ecf20Sopenharmony_ci wr->num_sge = 0; 24878c2ecf20Sopenharmony_ci wr->ex.invalidate_rkey = smbdirect_mr->mr->rkey; 24888c2ecf20Sopenharmony_ci wr->send_flags = IB_SEND_SIGNALED; 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci init_completion(&smbdirect_mr->invalidate_done); 24918c2ecf20Sopenharmony_ci rc = ib_post_send(info->id->qp, wr, NULL); 24928c2ecf20Sopenharmony_ci if (rc) { 24938c2ecf20Sopenharmony_ci log_rdma_mr(ERR, "ib_post_send failed rc=%x\n", rc); 24948c2ecf20Sopenharmony_ci smbd_disconnect_rdma_connection(info); 24958c2ecf20Sopenharmony_ci goto done; 24968c2ecf20Sopenharmony_ci } 24978c2ecf20Sopenharmony_ci wait_for_completion(&smbdirect_mr->invalidate_done); 24988c2ecf20Sopenharmony_ci smbdirect_mr->need_invalidate = false; 24998c2ecf20Sopenharmony_ci } else 25008c2ecf20Sopenharmony_ci /* 25018c2ecf20Sopenharmony_ci * For remote invalidation, just set it to MR_INVALIDATED 25028c2ecf20Sopenharmony_ci * and defer to mr_recovery_work to recover the MR for next use 25038c2ecf20Sopenharmony_ci */ 25048c2ecf20Sopenharmony_ci smbdirect_mr->state = MR_INVALIDATED; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci if (smbdirect_mr->state == MR_INVALIDATED) { 25078c2ecf20Sopenharmony_ci ib_dma_unmap_sg( 25088c2ecf20Sopenharmony_ci info->id->device, smbdirect_mr->sgl, 25098c2ecf20Sopenharmony_ci smbdirect_mr->sgl_count, 25108c2ecf20Sopenharmony_ci smbdirect_mr->dir); 25118c2ecf20Sopenharmony_ci smbdirect_mr->state = MR_READY; 25128c2ecf20Sopenharmony_ci if (atomic_inc_return(&info->mr_ready_count) == 1) 25138c2ecf20Sopenharmony_ci wake_up_interruptible(&info->wait_mr); 25148c2ecf20Sopenharmony_ci } else 25158c2ecf20Sopenharmony_ci /* 25168c2ecf20Sopenharmony_ci * Schedule the work to do MR recovery for future I/Os MR 25178c2ecf20Sopenharmony_ci * recovery is slow and don't want it to block current I/O 25188c2ecf20Sopenharmony_ci */ 25198c2ecf20Sopenharmony_ci queue_work(info->workqueue, &info->mr_recovery_work); 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_cidone: 25228c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&info->mr_used_count)) 25238c2ecf20Sopenharmony_ci wake_up(&info->wait_for_mr_cleanup); 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci return rc; 25268c2ecf20Sopenharmony_ci} 2527