| 1 | |
| 2 | |
| 3 | * RDMA transport layer based on the trans_fd.c implementation.
|
| 4 | *
|
| 5 | * Copyright (C) 2008 by Tom Tucker <[email protected]>
|
| 6 | * Copyright (C) 2006 by Russ Cox <[email protected]>
|
| 7 | * Copyright (C) 2004-2005 by Latchesar Ionkov <[email protected]>
|
| 8 | * Copyright (C) 2004-2008 by Eric Van Hensbergen <[email protected]>
|
| 9 | * Copyright (C) 1997-2002 by Ron Minnich <[email protected]>
|
| 10 |
|
| 11 |
|
| 12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 13 |
|
| 14 | #include <linux/in.h>
|
| 15 | #include <linux/module.h>
|
| 16 | #include <linux/net.h>
|
| 17 | #include <linux/ipv6.h>
|
| 18 | #include <linux/kthread.h>
|
| 19 | #include <linux/errno.h>
|
| 20 | #include <linux/kernel.h>
|
| 21 | #include <linux/un.h>
|
| 22 | #include <linux/uaccess.h>
|
| 23 | #include <linux/inet.h>
|
| 24 | #include <linux/file.h>
|
| 25 | #include <linux/fs_context.h>
|
| 26 | #include <linux/semaphore.h>
|
| 27 | #include <linux/slab.h>
|
| 28 | #include <linux/seq_file.h>
|
| 29 | #include <net/9p/9p.h>
|
| 30 | #include <net/9p/client.h>
|
| 31 | #include <net/9p/transport.h>
|
| 32 | #include <rdma/ib_verbs.h>
|
| 33 | #include <rdma/rdma_cm.h>
|
| 34 |
|
| 35 | #define P9_RDMA_SEND_SGE 4
|
| 36 | #define P9_RDMA_RECV_SGE 4
|
| 37 | #define P9_RDMA_IRD 0
|
| 38 | #define P9_RDMA_ORD 0
|
| 39 | #define P9_RDMA_MAXSIZE (1024*1024)
|
| 40 |
|
| 41 | |
| 42 | * struct p9_trans_rdma - RDMA transport instance
|
| 43 | *
|
| 44 | * @state: tracks the transport state machine for connection setup and tear down
|
| 45 | * @cm_id: The RDMA CM ID
|
| 46 | * @pd: Protection Domain pointer
|
| 47 | * @qp: Queue Pair pointer
|
| 48 | * @cq: Completion Queue pointer
|
| 49 | * @timeout: Number of uSecs to wait for connection management events
|
| 50 | * @privport: Whether a privileged port may be used
|
| 51 | * @port: The port to use
|
| 52 | * @sq_depth: The depth of the Send Queue
|
| 53 | * @sq_sem: Semaphore for the SQ
|
| 54 | * @rq_depth: The depth of the Receive Queue.
|
| 55 | * @rq_sem: Semaphore for the RQ
|
| 56 | * @excess_rc : Amount of posted Receive Contexts without a pending request.
|
| 57 | * See rdma_request()
|
| 58 | * @addr: The remote peer's address
|
| 59 | * @req_lock: Protects the active request list
|
| 60 | * @cm_done: Completion event for connection management tracking
|
| 61 |
|
| 62 | struct p9_trans_rdma {
|
| 63 | enum {
|
| 64 | P9_RDMA_INIT,
|
| 65 | P9_RDMA_ADDR_RESOLVED,
|
| 66 | P9_RDMA_ROUTE_RESOLVED,
|
| 67 | P9_RDMA_CONNECTED,
|
| 68 | P9_RDMA_FLUSHING,
|
| 69 | P9_RDMA_CLOSING,
|
| 70 | P9_RDMA_CLOSED,
|
| 71 | } state;
|
| 72 | struct rdma_cm_id *cm_id;
|
| 73 | struct ib_pd *pd;
|
| 74 | struct ib_qp *qp;
|
| 75 | struct ib_cq *cq;
|
| 76 | long timeout;
|
| 77 | bool privport;
|
| 78 | u16 port;
|
| 79 | int sq_depth;
|
| 80 | struct semaphore sq_sem;
|
| 81 | int rq_depth;
|
| 82 | struct semaphore rq_sem;
|
| 83 | atomic_t excess_rc;
|
| 84 | struct sockaddr_in addr;
|
| 85 | spinlock_t req_lock;
|
| 86 |
|
| 87 | struct completion cm_done;
|
| 88 | };
|
| 89 |
|
| 90 | struct p9_rdma_req;
|
| 91 |
|
| 92 | |
| 93 | * struct p9_rdma_context - Keeps track of in-process WR
|
| 94 | *
|
| 95 | * @cqe: completion queue entry
|
| 96 | * @busa: Bus address to unmap when the WR completes
|
| 97 | * @req: Keeps track of requests (send)
|
| 98 | * @rc: Keepts track of replies (receive)
|
| 99 |
|
| 100 | struct p9_rdma_context {
|
| 101 | struct ib_cqe cqe;
|
| 102 | dma_addr_t busa;
|
| 103 | union {
|
| 104 | struct p9_req_t *req;
|
| 105 | struct p9_fcall rc;
|
| 106 | };
|
| 107 | };
|
| 108 |
|
| 109 | static int p9_rdma_show_options(struct seq_file *m, struct p9_client *clnt)
|
| 110 | {
|
| 111 | struct p9_trans_rdma *rdma = clnt->trans;
|
| 112 |
|
| 113 | if (rdma->port != P9_RDMA_PORT)
|
| 114 | seq_printf(m, ",port=%u", rdma->port);
|
| 115 | if (rdma->sq_depth != P9_RDMA_SQ_DEPTH)
|
| 116 | seq_printf(m, ",sq=%u", rdma->sq_depth);
|
| 117 | if (rdma->rq_depth != P9_RDMA_RQ_DEPTH)
|
| 118 | seq_printf(m, ",rq=%u", rdma->rq_depth);
|
| 119 | if (rdma->timeout != P9_RDMA_TIMEOUT)
|
| 120 | seq_printf(m, ",timeout=%lu", rdma->timeout);
|
| 121 | if (rdma->privport)
|
| 122 | seq_puts(m, ",privport");
|
| 123 | return 0;
|
| 124 | }
|
| 125 |
|
| 126 | static int
|
| 127 | p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
|
| 128 | {
|
| 129 | struct p9_client *c = id->context;
|
| 130 | struct p9_trans_rdma *rdma = c->trans;
|
| 131 | unsigned long flags;
|
| 132 |
|
| 133 | switch (event->event) {
|
| 134 | case RDMA_CM_EVENT_ADDR_RESOLVED:
|
| 135 | spin_lock_irqsave(&rdma->req_lock, flags);
|
| 136 | BUG_ON(rdma->state != P9_RDMA_INIT);
|
| 137 | rdma->state = P9_RDMA_ADDR_RESOLVED;
|
| 138 | spin_unlock_irqrestore(&rdma->req_lock, flags);
|
| 139 | break;
|
| 140 |
|
| 141 | case RDMA_CM_EVENT_ROUTE_RESOLVED:
|
| 142 | spin_lock_irqsave(&rdma->req_lock, flags);
|
| 143 | BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED);
|
| 144 | rdma->state = P9_RDMA_ROUTE_RESOLVED;
|
| 145 | spin_unlock_irqrestore(&rdma->req_lock, flags);
|
| 146 | break;
|
| 147 |
|
| 148 | case RDMA_CM_EVENT_ESTABLISHED:
|
| 149 | spin_lock_irqsave(&rdma->req_lock, flags);
|
| 150 | BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED);
|
| 151 | rdma->state = P9_RDMA_CONNECTED;
|
| 152 | spin_unlock_irqrestore(&rdma->req_lock, flags);
|
| 153 | break;
|
| 154 |
|
| 155 | case RDMA_CM_EVENT_DISCONNECTED:
|
| 156 | if (rdma) {
|
| 157 | spin_lock_irqsave(&rdma->req_lock, flags);
|
| 158 | rdma->state = P9_RDMA_CLOSED;
|
| 159 | spin_unlock_irqrestore(&rdma->req_lock, flags);
|
| 160 | }
|
| 161 | c->status = Disconnected;
|
| 162 | break;
|
| 163 |
|
| 164 | case RDMA_CM_EVENT_TIMEWAIT_EXIT:
|
| 165 | break;
|
| 166 |
|
| 167 | case RDMA_CM_EVENT_ADDR_CHANGE:
|
| 168 | case RDMA_CM_EVENT_ROUTE_ERROR:
|
| 169 | case RDMA_CM_EVENT_DEVICE_REMOVAL:
|
| 170 | case RDMA_CM_EVENT_MULTICAST_JOIN:
|
| 171 | case RDMA_CM_EVENT_MULTICAST_ERROR:
|
| 172 | case RDMA_CM_EVENT_REJECTED:
|
| 173 | case RDMA_CM_EVENT_CONNECT_REQUEST:
|
| 174 | case RDMA_CM_EVENT_CONNECT_RESPONSE:
|
| 175 | case RDMA_CM_EVENT_CONNECT_ERROR:
|
| 176 | case RDMA_CM_EVENT_ADDR_ERROR:
|
| 177 | case RDMA_CM_EVENT_UNREACHABLE:
|
| 178 | c->status = Disconnected;
|
| 179 | rdma_disconnect(rdma->cm_id);
|
| 180 | break;
|
| 181 | default:
|
| 182 | BUG();
|
| 183 | }
|
| 184 | complete(&rdma->cm_done);
|
| 185 | return 0;
|
| 186 | }
|
| 187 |
|
| 188 | static void
|
| 189 | recv_done(struct ib_cq *cq, struct ib_wc *wc)
|
| 190 | {
|
| 191 | struct p9_client *client = cq->cq_context;
|
| 192 | struct p9_trans_rdma *rdma = client->trans;
|
| 193 | struct p9_rdma_context *c =
|
| 194 | container_of(wc->wr_cqe, struct p9_rdma_context, cqe);
|
| 195 | struct p9_req_t *req;
|
| 196 | int err = 0;
|
| 197 | int16_t tag;
|
| 198 | unsigned long flags;
|
| 199 |
|
| 200 | req = NULL;
|
| 201 | ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize,
|
| 202 | DMA_FROM_DEVICE);
|
| 203 |
|
| 204 | if (wc->status != IB_WC_SUCCESS)
|
| 205 | goto err_out;
|
| 206 |
|
| 207 | c->rc.size = wc->byte_len;
|
| 208 | err = p9_parse_header(&c->rc, NULL, NULL, &tag, 1);
|
| 209 | if (err)
|
| 210 | goto err_out;
|
| 211 |
|
| 212 | req = p9_tag_lookup(client, tag);
|
| 213 | if (!req)
|
| 214 | goto err_out;
|
| 215 |
|
| 216 | |
| 217 |
|
| 218 | if (unlikely(req->rc.sdata)) {
|
| 219 | pr_err("Duplicate reply for request %d", tag);
|
| 220 | goto err_out;
|
| 221 | }
|
| 222 |
|
| 223 | req->rc.size = c->rc.size;
|
| 224 | req->rc.sdata = c->rc.sdata;
|
| 225 | p9_client_cb(client, req, REQ_STATUS_RCVD);
|
| 226 |
|
| 227 | out:
|
| 228 | up(&rdma->rq_sem);
|
| 229 | kfree(c);
|
| 230 | return;
|
| 231 |
|
| 232 | err_out:
|
| 233 | p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n",
|
| 234 | req, err, wc->status);
|
| 235 | spin_lock_irqsave(&rdma->req_lock, flags);
|
| 236 | if (rdma->state < P9_RDMA_FLUSHING)
|
| 237 | rdma->state = P9_RDMA_FLUSHING;
|
| 238 | spin_unlock_irqrestore(&rdma->req_lock, flags);
|
| 239 | client->status = Disconnected;
|
| 240 | goto out;
|
| 241 | }
|
| 242 |
|
| 243 | static void
|
| 244 | send_done(struct ib_cq *cq, struct ib_wc *wc)
|
| 245 | {
|
| 246 | struct p9_client *client = cq->cq_context;
|
| 247 | struct p9_trans_rdma *rdma = client->trans;
|
| 248 | struct p9_rdma_context *c =
|
| 249 | container_of(wc->wr_cqe, struct p9_rdma_context, cqe);
|
| 250 |
|
| 251 | ib_dma_unmap_single(rdma->cm_id->device,
|
| 252 | c->busa, c->req->tc.size,
|
| 253 | DMA_TO_DEVICE);
|
| 254 | up(&rdma->sq_sem);
|
| 255 | p9_req_put(client, c->req);
|
| 256 | kfree(c);
|
| 257 | }
|
| 258 |
|
| 259 | static void qp_event_handler(struct ib_event *event, void *context)
|
| 260 | {
|
| 261 | p9_debug(P9_DEBUG_ERROR, "QP event %d context %p\n",
|
| 262 | event->event, context);
|
| 263 | }
|
| 264 |
|
| 265 | static void rdma_destroy_trans(struct p9_trans_rdma *rdma)
|
| 266 | {
|
| 267 | if (!rdma)
|
| 268 | return;
|
| 269 |
|
| 270 | if (rdma->qp && !IS_ERR(rdma->qp))
|
| 271 | ib_destroy_qp(rdma->qp);
|
| 272 |
|
| 273 | if (rdma->pd && !IS_ERR(rdma->pd))
|
| 274 | ib_dealloc_pd(rdma->pd);
|
| 275 |
|
| 276 | if (rdma->cq && !IS_ERR(rdma->cq))
|
| 277 | ib_free_cq(rdma->cq);
|
| 278 |
|
| 279 | if (rdma->cm_id && !IS_ERR(rdma->cm_id))
|
| 280 | rdma_destroy_id(rdma->cm_id);
|
| 281 |
|
| 282 | kfree(rdma);
|
| 283 | }
|
| 284 |
|
| 285 | static int
|
| 286 | post_recv(struct p9_client *client, struct p9_rdma_context *c)
|
| 287 | {
|
| 288 | struct p9_trans_rdma *rdma = client->trans;
|
| 289 | struct ib_recv_wr wr;
|
| 290 | struct ib_sge sge;
|
| 291 | int ret;
|
| 292 |
|
| 293 | c->busa = ib_dma_map_single(rdma->cm_id->device,
|
| 294 | c->rc.sdata, client->msize,
|
| 295 | DMA_FROM_DEVICE);
|
| 296 | if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
|
| 297 | goto error;
|
| 298 |
|
| 299 | c->cqe.done = recv_done;
|
| 300 |
|
| 301 | sge.addr = c->busa;
|
| 302 | sge.length = client->msize;
|
| 303 | sge.lkey = rdma->pd->local_dma_lkey;
|
| 304 |
|
| 305 | wr.next = NULL;
|
| 306 | wr.wr_cqe = &c->cqe;
|
| 307 | wr.sg_list = &sge;
|
| 308 | wr.num_sge = 1;
|
| 309 |
|
| 310 | ret = ib_post_recv(rdma->qp, &wr, NULL);
|
| 311 | if (ret)
|
| 312 | ib_dma_unmap_single(rdma->cm_id->device, c->busa,
|
| 313 | client->msize, DMA_FROM_DEVICE);
|
| 314 | return ret;
|
| 315 |
|
| 316 | error:
|
| 317 | p9_debug(P9_DEBUG_ERROR, "EIO\n");
|
| 318 | return -EIO;
|
| 319 | }
|
| 320 |
|
| 321 | static int rdma_request(struct p9_client *client, struct p9_req_t *req)
|
| 322 | {
|
| 323 | struct p9_trans_rdma *rdma = client->trans;
|
| 324 | struct ib_send_wr wr;
|
| 325 | struct ib_sge sge;
|
| 326 | int err = 0;
|
| 327 | unsigned long flags;
|
| 328 | struct p9_rdma_context *c = NULL;
|
| 329 | struct p9_rdma_context *rpl_context = NULL;
|
| 330 |
|
| 331 | |
| 332 | * there will be a receive context posted without a pending request.
|
| 333 | * Since there is no way to "un-post" it, we remember it and skip
|
| 334 | * post_recv() for the next request.
|
| 335 | * So here,
|
| 336 | * see if we are this `next request' and need to absorb an excess rc.
|
| 337 | * If yes, then drop and free our own, and do not recv_post().
|
| 338 | *
|
| 339 | if (unlikely(atomic_read(&rdma->excess_rc) > 0)) {
|
| 340 | if ((atomic_sub_return(1, &rdma->excess_rc) >= 0)) {
|
| 341 |
|
| 342 | p9_fcall_fini(&req->rc);
|
| 343 | req->rc.sdata = NULL;
|
| 344 | goto dont_need_post_recv;
|
| 345 | } else {
|
| 346 |
|
| 347 | atomic_inc(&rdma->excess_rc);
|
| 348 | }
|
| 349 | }
|
| 350 |
|
| 351 |
|
| 352 | rpl_context = kmalloc_obj(*rpl_context, GFP_NOFS);
|
| 353 | if (!rpl_context) {
|
| 354 | err = -ENOMEM;
|
| 355 | goto recv_error;
|
| 356 | }
|
| 357 | rpl_context->rc.sdata = req->rc.sdata;
|
| 358 |
|
| 359 | |
| 360 | * Post a receive buffer for this request. We need to ensure
|
| 361 | * there is a reply buffer available for every outstanding
|
| 362 | * request. A flushed request can result in no reply for an
|
| 363 | * outstanding request, so we must keep a count to avoid
|
| 364 | * overflowing the RQ.
|
| 365 |
|
| 366 | if (down_interruptible(&rdma->rq_sem)) {
|
| 367 | err = -EINTR;
|
| 368 | goto recv_error;
|
| 369 | }
|
| 370 |
|
| 371 | err = post_recv(client, rpl_context);
|
| 372 | if (err) {
|
| 373 | p9_debug(P9_DEBUG_ERROR, "POST RECV failed: %d\n", err);
|
| 374 | goto recv_error;
|
| 375 | }
|
| 376 |
|
| 377 | req->rc.sdata = NULL;
|
| 378 |
|
| 379 | dont_need_post_recv:
|
| 380 |
|
| 381 | c = kmalloc_obj(*c, GFP_NOFS);
|
| 382 | if (!c) {
|
| 383 | err = -ENOMEM;
|
| 384 | goto send_error;
|
| 385 | }
|
| 386 | c->req = req;
|
| 387 |
|
| 388 | c->busa = ib_dma_map_single(rdma->cm_id->device,
|
| 389 | c->req->tc.sdata, c->req->tc.size,
|
| 390 | DMA_TO_DEVICE);
|
| 391 | if (ib_dma_mapping_error(rdma->cm_id->device, c->busa)) {
|
| 392 | err = -EIO;
|
| 393 | goto send_error;
|
| 394 | }
|
| 395 |
|
| 396 | c->cqe.done = send_done;
|
| 397 |
|
| 398 | sge.addr = c->busa;
|
| 399 | sge.length = c->req->tc.size;
|
| 400 | sge.lkey = rdma->pd->local_dma_lkey;
|
| 401 |
|
| 402 | wr.next = NULL;
|
| 403 | wr.wr_cqe = &c->cqe;
|
| 404 | wr.opcode = IB_WR_SEND;
|
| 405 | wr.send_flags = IB_SEND_SIGNALED;
|
| 406 | wr.sg_list = &sge;
|
| 407 | wr.num_sge = 1;
|
| 408 |
|
| 409 | if (down_interruptible(&rdma->sq_sem)) {
|
| 410 | err = -EINTR;
|
| 411 | goto dma_unmap;
|
| 412 | }
|
| 413 |
|
| 414 | |
| 415 | * because doing if after could erase the REQ_STATUS_RCVD
|
| 416 | * status in case of a very fast reply.
|
| 417 |
|
| 418 | WRITE_ONCE(req->status, REQ_STATUS_SENT);
|
| 419 | err = ib_post_send(rdma->qp, &wr, NULL);
|
| 420 | if (err)
|
| 421 | goto dma_unmap;
|
| 422 |
|
| 423 |
|
| 424 | return 0;
|
| 425 |
|
| 426 | dma_unmap:
|
| 427 | ib_dma_unmap_single(rdma->cm_id->device, c->busa,
|
| 428 | c->req->tc.size, DMA_TO_DEVICE);
|
| 429 |
|
| 430 | send_error:
|
| 431 | WRITE_ONCE(req->status, REQ_STATUS_ERROR);
|
| 432 | kfree(c);
|
| 433 | p9_debug(P9_DEBUG_ERROR, "Error %d in rdma_request()\n", err);
|
| 434 |
|
| 435 | |
| 436 | * We did recv_post(), but not send. We have one recv_post in excess.
|
| 437 |
|
| 438 | atomic_inc(&rdma->excess_rc);
|
| 439 | return err;
|
| 440 |
|
| 441 |
|
| 442 | recv_error:
|
| 443 | kfree(rpl_context);
|
| 444 | spin_lock_irqsave(&rdma->req_lock, flags);
|
| 445 | if (err != -EINTR && rdma->state < P9_RDMA_CLOSING) {
|
| 446 | rdma->state = P9_RDMA_CLOSING;
|
| 447 | spin_unlock_irqrestore(&rdma->req_lock, flags);
|
| 448 | rdma_disconnect(rdma->cm_id);
|
| 449 | } else
|
| 450 | spin_unlock_irqrestore(&rdma->req_lock, flags);
|
| 451 | return err;
|
| 452 | }
|
| 453 |
|
| 454 | static void rdma_close(struct p9_client *client)
|
| 455 | {
|
| 456 | struct p9_trans_rdma *rdma;
|
| 457 |
|
| 458 | if (!client)
|
| 459 | return;
|
| 460 |
|
| 461 | rdma = client->trans;
|
| 462 | if (!rdma)
|
| 463 | return;
|
| 464 |
|
| 465 | client->status = Disconnected;
|
| 466 | rdma_disconnect(rdma->cm_id);
|
| 467 | rdma_destroy_trans(rdma);
|
| 468 | }
|
| 469 |
|
| 470 | |
| 471 | * alloc_rdma - Allocate and initialize the rdma transport structure
|
| 472 | * @opts: Mount options structure
|
| 473 |
|
| 474 | static struct p9_trans_rdma *alloc_rdma(struct p9_rdma_opts *opts)
|
| 475 | {
|
| 476 | struct p9_trans_rdma *rdma;
|
| 477 |
|
| 478 | rdma = kzalloc_obj(struct p9_trans_rdma);
|
| 479 | if (!rdma)
|
| 480 | return NULL;
|
| 481 |
|
| 482 | rdma->port = opts->port;
|
| 483 | rdma->privport = opts->privport;
|
| 484 | rdma->sq_depth = opts->sq_depth;
|
| 485 | rdma->rq_depth = opts->rq_depth;
|
| 486 | rdma->timeout = opts->timeout;
|
| 487 | spin_lock_init(&rdma->req_lock);
|
| 488 | init_completion(&rdma->cm_done);
|
| 489 | sema_init(&rdma->sq_sem, rdma->sq_depth);
|
| 490 | sema_init(&rdma->rq_sem, rdma->rq_depth);
|
| 491 | atomic_set(&rdma->excess_rc, 0);
|
| 492 |
|
| 493 | return rdma;
|
| 494 | }
|
| 495 |
|
| 496 | static int rdma_cancel(struct p9_client *client, struct p9_req_t *req)
|
| 497 | {
|
| 498 | |
| 499 | * We will take care of it (if we have to) in rdma_cancelled()
|
| 500 |
|
| 501 | return 1;
|
| 502 | }
|
| 503 |
|
| 504 | |
| 505 | * That means we have posted one buffer in excess.
|
| 506 |
|
| 507 | static int rdma_cancelled(struct p9_client *client, struct p9_req_t *req)
|
| 508 | {
|
| 509 | struct p9_trans_rdma *rdma = client->trans;
|
| 510 | atomic_inc(&rdma->excess_rc);
|
| 511 | return 0;
|
| 512 | }
|
| 513 |
|
| 514 | static int p9_rdma_bind_privport(struct p9_trans_rdma *rdma)
|
| 515 | {
|
| 516 | struct sockaddr_in cl = {
|
| 517 | .sin_family = AF_INET,
|
| 518 | .sin_addr.s_addr = htonl(INADDR_ANY),
|
| 519 | };
|
| 520 | int port, err = -EINVAL;
|
| 521 |
|
| 522 | for (port = P9_DEF_MAX_RESVPORT; port >= P9_DEF_MIN_RESVPORT; port--) {
|
| 523 | cl.sin_port = htons((ushort)port);
|
| 524 | err = rdma_bind_addr(rdma->cm_id, (struct sockaddr *)&cl);
|
| 525 | if (err != -EADDRINUSE)
|
| 526 | break;
|
| 527 | }
|
| 528 | return err;
|
| 529 | }
|
| 530 |
|
| 531 | |
| 532 | * rdma_create_trans - Transport method for creating a transport instance
|
| 533 | * @client: client instance
|
| 534 | * @fc: The filesystem context
|
| 535 |
|
| 536 | static int
|
| 537 | rdma_create_trans(struct p9_client *client, struct fs_context *fc)
|
| 538 | {
|
| 539 | const char *addr = fc->source;
|
| 540 | struct v9fs_context *ctx = fc->fs_private;
|
| 541 | struct p9_rdma_opts opts = ctx->rdma_opts;
|
| 542 | int err;
|
| 543 | struct p9_trans_rdma *rdma;
|
| 544 | struct rdma_conn_param conn_param;
|
| 545 | struct ib_qp_init_attr qp_attr;
|
| 546 |
|
| 547 | if (addr == NULL)
|
| 548 | return -EINVAL;
|
| 549 |
|
| 550 |
|
| 551 | opts = ctx->rdma_opts;
|
| 552 |
|
| 553 |
|
| 554 | rdma = alloc_rdma(&opts);
|
| 555 | if (!rdma)
|
| 556 | return -ENOMEM;
|
| 557 |
|
| 558 |
|
| 559 | rdma->cm_id = rdma_create_id(&init_net, p9_cm_event_handler, client,
|
| 560 | RDMA_PS_TCP, IB_QPT_RC);
|
| 561 | if (IS_ERR(rdma->cm_id))
|
| 562 | goto error;
|
| 563 |
|
| 564 |
|
| 565 | client->trans = rdma;
|
| 566 |
|
| 567 |
|
| 568 | if (opts.privport) {
|
| 569 | err = p9_rdma_bind_privport(rdma);
|
| 570 | if (err < 0) {
|
| 571 | pr_err("%s (%d): problem binding to privport: %d\n",
|
| 572 | __func__, task_pid_nr(current), -err);
|
| 573 | goto error;
|
| 574 | }
|
| 575 | }
|
| 576 |
|
| 577 |
|
| 578 | rdma->addr.sin_family = AF_INET;
|
| 579 | rdma->addr.sin_addr.s_addr = in_aton(addr);
|
| 580 | rdma->addr.sin_port = htons(opts.port);
|
| 581 | err = rdma_resolve_addr(rdma->cm_id, NULL,
|
| 582 | (struct sockaddr *)&rdma->addr,
|
| 583 | rdma->timeout);
|
| 584 | if (err)
|
| 585 | goto error;
|
| 586 | err = wait_for_completion_interruptible(&rdma->cm_done);
|
| 587 | if (err || (rdma->state != P9_RDMA_ADDR_RESOLVED))
|
| 588 | goto error;
|
| 589 |
|
| 590 |
|
| 591 | err = rdma_resolve_route(rdma->cm_id, rdma->timeout);
|
| 592 | if (err)
|
| 593 | goto error;
|
| 594 | err = wait_for_completion_interruptible(&rdma->cm_done);
|
| 595 | if (err || (rdma->state != P9_RDMA_ROUTE_RESOLVED))
|
| 596 | goto error;
|
| 597 |
|
| 598 |
|
| 599 | rdma->cq = ib_alloc_cq_any(rdma->cm_id->device, client,
|
| 600 | opts.sq_depth + opts.rq_depth + 1,
|
| 601 | IB_POLL_SOFTIRQ);
|
| 602 | if (IS_ERR(rdma->cq))
|
| 603 | goto error;
|
| 604 |
|
| 605 |
|
| 606 | rdma->pd = ib_alloc_pd(rdma->cm_id->device, 0);
|
| 607 | if (IS_ERR(rdma->pd))
|
| 608 | goto error;
|
| 609 |
|
| 610 |
|
| 611 | memset(&qp_attr, 0, sizeof qp_attr);
|
| 612 | qp_attr.event_handler = qp_event_handler;
|
| 613 | qp_attr.qp_context = client;
|
| 614 | qp_attr.cap.max_send_wr = opts.sq_depth;
|
| 615 | qp_attr.cap.max_recv_wr = opts.rq_depth;
|
| 616 | qp_attr.cap.max_send_sge = P9_RDMA_SEND_SGE;
|
| 617 | qp_attr.cap.max_recv_sge = P9_RDMA_RECV_SGE;
|
| 618 | qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
|
| 619 | qp_attr.qp_type = IB_QPT_RC;
|
| 620 | qp_attr.send_cq = rdma->cq;
|
| 621 | qp_attr.recv_cq = rdma->cq;
|
| 622 | err = rdma_create_qp(rdma->cm_id, rdma->pd, &qp_attr);
|
| 623 | if (err)
|
| 624 | goto error;
|
| 625 | rdma->qp = rdma->cm_id->qp;
|
| 626 |
|
| 627 |
|
| 628 | memset(&conn_param, 0, sizeof(conn_param));
|
| 629 | conn_param.private_data = NULL;
|
| 630 | conn_param.private_data_len = 0;
|
| 631 | conn_param.responder_resources = P9_RDMA_IRD;
|
| 632 | conn_param.initiator_depth = P9_RDMA_ORD;
|
| 633 | err = rdma_connect(rdma->cm_id, &conn_param);
|
| 634 | if (err)
|
| 635 | goto error;
|
| 636 | err = wait_for_completion_interruptible(&rdma->cm_done);
|
| 637 | if (err || (rdma->state != P9_RDMA_CONNECTED))
|
| 638 | goto error;
|
| 639 |
|
| 640 | client->status = Connected;
|
| 641 |
|
| 642 | return 0;
|
| 643 |
|
| 644 | error:
|
| 645 | rdma_destroy_trans(rdma);
|
| 646 | return -ENOTCONN;
|
| 647 | }
|
| 648 |
|
| 649 | static struct p9_trans_module p9_rdma_trans = {
|
| 650 | .name = "rdma",
|
| 651 | .maxsize = P9_RDMA_MAXSIZE,
|
| 652 | .pooled_rbuffers = true,
|
| 653 | .def = false,
|
| 654 | .supports_vmalloc = false,
|
| 655 | .owner = THIS_MODULE,
|
| 656 | .create = rdma_create_trans,
|
| 657 | .close = rdma_close,
|
| 658 | .request = rdma_request,
|
| 659 | .cancel = rdma_cancel,
|
| 660 | .cancelled = rdma_cancelled,
|
| 661 | .show_options = p9_rdma_show_options,
|
| 662 | };
|
| 663 |
|
| 664 | |
| 665 | * p9_trans_rdma_init - Register the 9P RDMA transport driver
|
| 666 |
|
| 667 | static int __init p9_trans_rdma_init(void)
|
| 668 | {
|
| 669 | v9fs_register_trans(&p9_rdma_trans);
|
| 670 | return 0;
|
| 671 | }
|
| 672 |
|
| 673 | static void __exit p9_trans_rdma_exit(void)
|
| 674 | {
|
| 675 | v9fs_unregister_trans(&p9_rdma_trans);
|
| 676 | }
|
| 677 |
|
| 678 | module_init(p9_trans_rdma_init);
|
| 679 | module_exit(p9_trans_rdma_exit);
|
| 680 | MODULE_ALIAS_9P("rdma");
|
| 681 |
|
| 682 | MODULE_AUTHOR("Tom Tucker <[email protected]>");
|
| 683 | MODULE_DESCRIPTION("RDMA Transport for 9P");
|
| 684 | MODULE_LICENSE("Dual BSD/GPL");
|