diff options
Diffstat (limited to 'drivers/nvme/target/rdma.c')
-rw-r--r-- | drivers/nvme/target/rdma.c | 100 |
1 files changed, 74 insertions, 26 deletions
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index e06d504bdf0c..b4d648536c3e 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c | |||
@@ -77,6 +77,7 @@ enum nvmet_rdma_queue_state { | |||
77 | NVMET_RDMA_Q_CONNECTING, | 77 | NVMET_RDMA_Q_CONNECTING, |
78 | NVMET_RDMA_Q_LIVE, | 78 | NVMET_RDMA_Q_LIVE, |
79 | NVMET_RDMA_Q_DISCONNECTING, | 79 | NVMET_RDMA_Q_DISCONNECTING, |
80 | NVMET_RDMA_IN_DEVICE_REMOVAL, | ||
80 | }; | 81 | }; |
81 | 82 | ||
82 | struct nvmet_rdma_queue { | 83 | struct nvmet_rdma_queue { |
@@ -615,15 +616,10 @@ static u16 nvmet_rdma_map_sgl_keyed(struct nvmet_rdma_rsp *rsp, | |||
615 | if (!len) | 616 | if (!len) |
616 | return 0; | 617 | return 0; |
617 | 618 | ||
618 | /* use the already allocated data buffer if possible */ | 619 | status = nvmet_rdma_alloc_sgl(&rsp->req.sg, &rsp->req.sg_cnt, |
619 | if (len <= NVMET_RDMA_INLINE_DATA_SIZE && rsp->queue->host_qid) { | 620 | len); |
620 | nvmet_rdma_use_inline_sg(rsp, len, 0); | 621 | if (status) |
621 | } else { | 622 | return status; |
622 | status = nvmet_rdma_alloc_sgl(&rsp->req.sg, &rsp->req.sg_cnt, | ||
623 | len); | ||
624 | if (status) | ||
625 | return status; | ||
626 | } | ||
627 | 623 | ||
628 | ret = rdma_rw_ctx_init(&rsp->rw, cm_id->qp, cm_id->port_num, | 624 | ret = rdma_rw_ctx_init(&rsp->rw, cm_id->qp, cm_id->port_num, |
629 | rsp->req.sg, rsp->req.sg_cnt, 0, addr, key, | 625 | rsp->req.sg, rsp->req.sg_cnt, 0, addr, key, |
@@ -984,7 +980,10 @@ static void nvmet_rdma_release_queue_work(struct work_struct *w) | |||
984 | struct nvmet_rdma_device *dev = queue->dev; | 980 | struct nvmet_rdma_device *dev = queue->dev; |
985 | 981 | ||
986 | nvmet_rdma_free_queue(queue); | 982 | nvmet_rdma_free_queue(queue); |
987 | rdma_destroy_id(cm_id); | 983 | |
984 | if (queue->state != NVMET_RDMA_IN_DEVICE_REMOVAL) | ||
985 | rdma_destroy_id(cm_id); | ||
986 | |||
988 | kref_put(&dev->ref, nvmet_rdma_free_dev); | 987 | kref_put(&dev->ref, nvmet_rdma_free_dev); |
989 | } | 988 | } |
990 | 989 | ||
@@ -1233,8 +1232,9 @@ static void __nvmet_rdma_queue_disconnect(struct nvmet_rdma_queue *queue) | |||
1233 | switch (queue->state) { | 1232 | switch (queue->state) { |
1234 | case NVMET_RDMA_Q_CONNECTING: | 1233 | case NVMET_RDMA_Q_CONNECTING: |
1235 | case NVMET_RDMA_Q_LIVE: | 1234 | case NVMET_RDMA_Q_LIVE: |
1236 | disconnect = true; | ||
1237 | queue->state = NVMET_RDMA_Q_DISCONNECTING; | 1235 | queue->state = NVMET_RDMA_Q_DISCONNECTING; |
1236 | case NVMET_RDMA_IN_DEVICE_REMOVAL: | ||
1237 | disconnect = true; | ||
1238 | break; | 1238 | break; |
1239 | case NVMET_RDMA_Q_DISCONNECTING: | 1239 | case NVMET_RDMA_Q_DISCONNECTING: |
1240 | break; | 1240 | break; |
@@ -1272,6 +1272,62 @@ static void nvmet_rdma_queue_connect_fail(struct rdma_cm_id *cm_id, | |||
1272 | schedule_work(&queue->release_work); | 1272 | schedule_work(&queue->release_work); |
1273 | } | 1273 | } |
1274 | 1274 | ||
1275 | /** | ||
1276 | * nvme_rdma_device_removal() - Handle RDMA device removal | ||
1277 | * @queue: nvmet rdma queue (cm id qp_context) | ||
1278 | * @addr: nvmet address (cm_id context) | ||
1279 | * | ||
1280 | * DEVICE_REMOVAL event notifies us that the RDMA device is about | ||
1281 | * to unplug so we should take care of destroying our RDMA resources. | ||
1282 | * This event will be generated for each allocated cm_id. | ||
1283 | * | ||
1284 | * Note that this event can be generated on a normal queue cm_id | ||
1285 | * and/or a device bound listener cm_id (where in this case | ||
1286 | * queue will be null). | ||
1287 | * | ||
1288 | * we claim ownership on destroying the cm_id. For queues we move | ||
1289 | * the queue state to NVMET_RDMA_IN_DEVICE_REMOVAL and for port | ||
1290 | * we nullify the priv to prevent double cm_id destruction and destroying | ||
1291 | * the cm_id implicitely by returning a non-zero rc to the callout. | ||
1292 | */ | ||
1293 | static int nvmet_rdma_device_removal(struct rdma_cm_id *cm_id, | ||
1294 | struct nvmet_rdma_queue *queue) | ||
1295 | { | ||
1296 | unsigned long flags; | ||
1297 | |||
1298 | if (!queue) { | ||
1299 | struct nvmet_port *port = cm_id->context; | ||
1300 | |||
1301 | /* | ||
1302 | * This is a listener cm_id. Make sure that | ||
1303 | * future remove_port won't invoke a double | ||
1304 | * cm_id destroy. use atomic xchg to make sure | ||
1305 | * we don't compete with remove_port. | ||
1306 | */ | ||
1307 | if (xchg(&port->priv, NULL) != cm_id) | ||
1308 | return 0; | ||
1309 | } else { | ||
1310 | /* | ||
1311 | * This is a queue cm_id. Make sure that | ||
1312 | * release queue will not destroy the cm_id | ||
1313 | * and schedule all ctrl queues removal (only | ||
1314 | * if the queue is not disconnecting already). | ||
1315 | */ | ||
1316 | spin_lock_irqsave(&queue->state_lock, flags); | ||
1317 | if (queue->state != NVMET_RDMA_Q_DISCONNECTING) | ||
1318 | queue->state = NVMET_RDMA_IN_DEVICE_REMOVAL; | ||
1319 | spin_unlock_irqrestore(&queue->state_lock, flags); | ||
1320 | nvmet_rdma_queue_disconnect(queue); | ||
1321 | flush_scheduled_work(); | ||
1322 | } | ||
1323 | |||
1324 | /* | ||
1325 | * We need to return 1 so that the core will destroy | ||
1326 | * it's own ID. What a great API design.. | ||
1327 | */ | ||
1328 | return 1; | ||
1329 | } | ||
1330 | |||
1275 | static int nvmet_rdma_cm_handler(struct rdma_cm_id *cm_id, | 1331 | static int nvmet_rdma_cm_handler(struct rdma_cm_id *cm_id, |
1276 | struct rdma_cm_event *event) | 1332 | struct rdma_cm_event *event) |
1277 | { | 1333 | { |
@@ -1294,20 +1350,11 @@ static int nvmet_rdma_cm_handler(struct rdma_cm_id *cm_id, | |||
1294 | break; | 1350 | break; |
1295 | case RDMA_CM_EVENT_ADDR_CHANGE: | 1351 | case RDMA_CM_EVENT_ADDR_CHANGE: |
1296 | case RDMA_CM_EVENT_DISCONNECTED: | 1352 | case RDMA_CM_EVENT_DISCONNECTED: |
1297 | case RDMA_CM_EVENT_DEVICE_REMOVAL: | ||
1298 | case RDMA_CM_EVENT_TIMEWAIT_EXIT: | 1353 | case RDMA_CM_EVENT_TIMEWAIT_EXIT: |
1299 | /* | 1354 | nvmet_rdma_queue_disconnect(queue); |
1300 | * We can get the device removal callback even for a | 1355 | break; |
1301 | * CM ID that we aren't actually using. In that case | 1356 | case RDMA_CM_EVENT_DEVICE_REMOVAL: |
1302 | * the context pointer is NULL, so we shouldn't try | 1357 | ret = nvmet_rdma_device_removal(cm_id, queue); |
1303 | * to disconnect a non-existing queue. But we also | ||
1304 | * need to return 1 so that the core will destroy | ||
1305 | * it's own ID. What a great API design.. | ||
1306 | */ | ||
1307 | if (queue) | ||
1308 | nvmet_rdma_queue_disconnect(queue); | ||
1309 | else | ||
1310 | ret = 1; | ||
1311 | break; | 1358 | break; |
1312 | case RDMA_CM_EVENT_REJECTED: | 1359 | case RDMA_CM_EVENT_REJECTED: |
1313 | case RDMA_CM_EVENT_UNREACHABLE: | 1360 | case RDMA_CM_EVENT_UNREACHABLE: |
@@ -1396,9 +1443,10 @@ out_destroy_id: | |||
1396 | 1443 | ||
1397 | static void nvmet_rdma_remove_port(struct nvmet_port *port) | 1444 | static void nvmet_rdma_remove_port(struct nvmet_port *port) |
1398 | { | 1445 | { |
1399 | struct rdma_cm_id *cm_id = port->priv; | 1446 | struct rdma_cm_id *cm_id = xchg(&port->priv, NULL); |
1400 | 1447 | ||
1401 | rdma_destroy_id(cm_id); | 1448 | if (cm_id) |
1449 | rdma_destroy_id(cm_id); | ||
1402 | } | 1450 | } |
1403 | 1451 | ||
1404 | static struct nvmet_fabrics_ops nvmet_rdma_ops = { | 1452 | static struct nvmet_fabrics_ops nvmet_rdma_ops = { |