bdev/ocssd: track outstanding admin commands

Make sure a namespace is depopulated only once all outstanding commands
generated by the media manegement poller to that namespace are completed.
It fixes the use-after-free errors reported by asan during shutdown.

Fixes #1251

Change-Id: Iab99b594756cee2d41235c70194bcaa5f5e1fb0b
Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/1199
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
This commit is contained in:
Konrad Sztyber 2020-03-04 08:47:27 +01:00 committed by Tomasz Zawadzki
parent 02385a64b9
commit 211ef924e3

View File

@ -90,6 +90,7 @@ struct bdev_ocssd_ns {
struct bdev_ocssd_lba_offsets lba_offsets; struct bdev_ocssd_lba_offsets lba_offsets;
bool chunk_notify_pending; bool chunk_notify_pending;
uint64_t chunk_notify_count; uint64_t chunk_notify_count;
uint64_t num_outstanding;
#define CHUNK_NOTIFICATION_ENTRY_COUNT 64 #define CHUNK_NOTIFICATION_ENTRY_COUNT 64
struct spdk_ocssd_chunk_notification_entry chunk[CHUNK_NOTIFICATION_ENTRY_COUNT]; struct spdk_ocssd_chunk_notification_entry chunk[CHUNK_NOTIFICATION_ENTRY_COUNT];
}; };
@ -800,6 +801,21 @@ bdev_ocssd_get_io_channel(void *ctx)
return spdk_get_io_channel(ocssd_bdev->nvme_bdev.nvme_bdev_ctrlr); return spdk_get_io_channel(ocssd_bdev->nvme_bdev.nvme_bdev_ctrlr);
} }
static void
bdev_ocssd_free_namespace(struct nvme_bdev_ns *nvme_ns)
{
struct nvme_bdev *bdev, *tmp;
TAILQ_FOREACH_SAFE(bdev, &nvme_ns->bdevs, tailq, tmp) {
spdk_bdev_unregister(&bdev->disk, NULL, NULL);
}
free(nvme_ns->type_ctx);
nvme_ns->type_ctx = NULL;
nvme_ctrlr_depopulate_namespace_done(nvme_ns->ctrlr);
}
static void static void
bdev_ocssd_chunk_notification_cb(void *ctx, const struct spdk_nvme_cpl *cpl) bdev_ocssd_chunk_notification_cb(void *ctx, const struct spdk_nvme_cpl *cpl)
{ {
@ -812,13 +828,19 @@ bdev_ocssd_chunk_notification_cb(void *ctx, const struct spdk_nvme_cpl *cpl)
size_t chunk_id, num_blocks, lba; size_t chunk_id, num_blocks, lba;
int rc; int rc;
if (spdk_nvme_cpl_is_error(cpl)) { ocssd_ns->num_outstanding--;
SPDK_ERRLOG("Failed to retrieve chunk notification log\n");
return;
}
/* The namespace could have been depopulated in the meantime */ /* The namespace could have been depopulated in the meantime */
if (!nvme_ns->populated) { if (!nvme_ns->populated) {
if (ocssd_ns->num_outstanding == 0) {
bdev_ocssd_free_namespace(nvme_ns);
}
return;
}
if (spdk_nvme_cpl_is_error(cpl)) {
SPDK_ERRLOG("Failed to retrieve chunk notification log\n");
return; return;
} }
@ -903,6 +925,7 @@ bdev_ocssd_poll_mm(void *ctx)
ocssd_ns = bdev_ocssd_get_ns_from_nvme(nvme_ns); ocssd_ns = bdev_ocssd_get_ns_from_nvme(nvme_ns);
if (ocssd_ns->chunk_notify_pending) { if (ocssd_ns->chunk_notify_pending) {
ocssd_ns->chunk_notify_pending = false; ocssd_ns->chunk_notify_pending = false;
ocssd_ns->num_outstanding++;
rc = spdk_nvme_ctrlr_cmd_get_log_page(nvme_bdev_ctrlr->ctrlr, rc = spdk_nvme_ctrlr_cmd_get_log_page(nvme_bdev_ctrlr->ctrlr,
SPDK_OCSSD_LOG_CHUNK_NOTIFICATION, SPDK_OCSSD_LOG_CHUNK_NOTIFICATION,
@ -914,6 +937,7 @@ bdev_ocssd_poll_mm(void *ctx)
if (spdk_unlikely(rc != 0)) { if (spdk_unlikely(rc != 0)) {
SPDK_ERRLOG("Failed to get chunk notification log page: %s\n", SPDK_ERRLOG("Failed to get chunk notification log page: %s\n",
spdk_strerror(-rc)); spdk_strerror(-rc));
ocssd_ns->num_outstanding--;
} }
} }
} }
@ -1391,17 +1415,20 @@ bdev_ocssd_populate_namespace(struct nvme_bdev_ctrlr *nvme_bdev_ctrlr,
void void
bdev_ocssd_depopulate_namespace(struct nvme_bdev_ns *ns) bdev_ocssd_depopulate_namespace(struct nvme_bdev_ns *ns)
{ {
struct nvme_bdev *bdev, *tmp; struct bdev_ocssd_ns *ocssd_ns;
TAILQ_FOREACH_SAFE(bdev, &ns->bdevs, tailq, tmp) { ocssd_ns = bdev_ocssd_get_ns_from_nvme(ns);
spdk_bdev_unregister(&bdev->disk, NULL, NULL);
}
free(ns->type_ctx); /* If there are outstanding admin requests, we cannot free the context
* here, as they'd write over deallocated memory. Clear the populated
* flag, so that the completion callback knows that the namespace is
* being depopulated and finish its deallocation once all requests are
* completed.
*/
ns->populated = false; ns->populated = false;
ns->type_ctx = NULL; if (ocssd_ns->num_outstanding == 0) {
bdev_ocssd_free_namespace(ns);
nvme_ctrlr_depopulate_namespace_done(ns->ctrlr); }
} }
int int