scsi: fix LUN removal/hotremoval path

bdev hotremove event is send directly to hotremove callback given
during spdk_scsi_dev_construct() call. So any bdev hotremove might be
promoted to whole SCSI device removal (like in vhost) wich will trigger
LUN removal. But after returning from hotremove callback LUN and device
might be still referenced (eg to register poller or by outstanding IO).

Even worse: spdk_scsi_dev object is not dynamicaly allocated but
returned from static array which mean there is no way to detect
use-after-free error on spdk_scsi_dev by any tool. This might lead to
using SCSI device that is freed or assigned to different device.

To fix this:
- always delete LUN using hotremove path
- defer spdk_scsi_dev delete/removal after all LUNS are really
deleted.

Change-Id: I65598bf42cd507f620095dff5d32509a0424d060
Signed-off-by: Pawel Wodkowski <pawelx.wodkowski@intel.com>
Reviewed-on: https://review.gerrithub.io/393674
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Dariusz Stojaczyk <dariuszx.stojaczyk@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Pawel Wodkowski 2018-01-04 21:09:02 +01:00 committed by Jim Harris
parent 5c0c104b81
commit 85a61ea6f0
5 changed files with 73 additions and 66 deletions

View File

@ -64,28 +64,42 @@ allocate_dev(void)
static void
free_dev(struct spdk_scsi_dev *dev)
{
assert(dev->is_allocated == 1);
assert(dev->removed == true);
dev->is_allocated = 0;
}
void
spdk_scsi_dev_destruct(struct spdk_scsi_dev *dev)
{
int lun_cnt;
int i;
if (dev == NULL) {
if (dev == NULL || dev->removed) {
return;
}
dev->removed = true;
lun_cnt = 0;
for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
if (dev->lun[i] == NULL) {
continue;
}
/*
* LUN will remove itself from this dev when all outstanding IO
* is done. When no more LUNs, dev will be deleted.
*/
spdk_scsi_lun_destruct(dev->lun[i]);
dev->lun[i] = NULL;
lun_cnt++;
}
free_dev(dev);
if (lun_cnt == 0) {
free_dev(dev);
return;
}
}
static void
@ -101,12 +115,21 @@ void
spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev,
struct spdk_scsi_lun *lun)
{
int lun_cnt = 0;
int i;
for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
if (dev->lun[i] == lun) {
dev->lun[i] = NULL;
}
if (dev->lun[i]) {
lun_cnt++;
}
}
if (dev->removed == true && lun_cnt == 0) {
free_dev(dev);
}
}

View File

@ -188,7 +188,12 @@ spdk_scsi_lun_hotplug(void *arg)
if (!spdk_scsi_lun_has_pending_tasks(lun)) {
spdk_scsi_lun_free_io_channel(lun);
spdk_scsi_lun_delete(lun);
spdk_bdev_close(lun->bdev_desc);
spdk_poller_unregister(&lun->hotplug_poller);
spdk_scsi_dev_delete_lun(lun->dev, lun);
free(lun);
}
}
@ -197,12 +202,15 @@ _spdk_scsi_lun_hot_remove(void *arg1)
{
struct spdk_scsi_lun *lun = arg1;
lun->removed = true;
if (lun->hotremove_cb) {
lun->hotremove_cb(lun, lun->hotremove_ctx);
}
lun->hotplug_poller = spdk_poller_register(spdk_scsi_lun_hotplug, lun, 0);
if (spdk_scsi_lun_has_pending_tasks(lun)) {
lun->hotplug_poller = spdk_poller_register(spdk_scsi_lun_hotplug, lun, 10);
} else {
spdk_scsi_lun_hotplug(lun);
}
}
static void
@ -211,6 +219,11 @@ spdk_scsi_lun_hot_remove(void *remove_ctx)
struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)remove_ctx;
struct spdk_thread *thread;
if (lun->removed) {
return;
}
lun->removed = true;
if (lun->io_channel == NULL) {
_spdk_scsi_lun_hot_remove(lun);
return;
@ -269,35 +282,10 @@ spdk_scsi_lun_construct(struct spdk_bdev *bdev,
return lun;
}
int
void
spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun)
{
spdk_bdev_close(lun->bdev_desc);
spdk_poller_unregister(&lun->hotplug_poller);
free(lun);
return 0;
}
int
spdk_scsi_lun_delete(struct spdk_scsi_lun *lun)
{
struct spdk_scsi_dev *dev;
pthread_mutex_lock(&g_spdk_scsi.mutex);
dev = lun->dev;
/* Remove the LUN from the device */
if (dev != NULL) {
spdk_scsi_dev_delete_lun(dev, lun);
}
/* Destroy this lun */
spdk_scsi_lun_destruct(lun);
pthread_mutex_unlock(&g_spdk_scsi.mutex);
return 0;
spdk_scsi_lun_hot_remove(lun);
}
int spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun)

View File

@ -60,6 +60,7 @@ struct spdk_scsi_port {
struct spdk_scsi_dev {
int id;
int is_allocated;
bool removed;
char name[SPDK_SCSI_DEV_MAX_NAME + 1];
@ -125,13 +126,12 @@ typedef struct spdk_scsi_lun _spdk_scsi_lun;
_spdk_scsi_lun *spdk_scsi_lun_construct(struct spdk_bdev *bdev,
void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
void *hotremove_ctx);
int spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun);
void spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun);
void spdk_scsi_lun_execute_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task);
int spdk_scsi_lun_task_mgmt_execute(struct spdk_scsi_task *task, enum spdk_scsi_task_func func);
void spdk_scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task);
void spdk_scsi_lun_complete_mgmt_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task);
int spdk_scsi_lun_delete(struct spdk_scsi_lun *lun);
int spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun);
void spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun *lun);
bool spdk_scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun);

View File

@ -91,11 +91,10 @@ spdk_scsi_lun_construct(struct spdk_bdev *bdev,
return lun;
}
int
void
spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun)
{
free(lun);
return 0;
}
struct spdk_bdev *
@ -150,7 +149,7 @@ dev_destruct_null_dev(void)
static void
dev_destruct_zero_luns(void)
{
struct spdk_scsi_dev dev = { 0 };
struct spdk_scsi_dev dev = { .is_allocated = 1 };
/* No luns attached to the dev */
@ -161,7 +160,7 @@ dev_destruct_zero_luns(void)
static void
dev_destruct_null_lun(void)
{
struct spdk_scsi_dev dev = { 0 };
struct spdk_scsi_dev dev = { .is_allocated = 1 };
/* pass null for the lun */
dev.lun[0] = NULL;
@ -173,13 +172,13 @@ dev_destruct_null_lun(void)
static void
dev_destruct_success(void)
{
struct spdk_scsi_dev dev = { 0 };
struct spdk_scsi_dev dev = { .is_allocated = 1 };
struct spdk_scsi_lun *lun;
lun = calloc(1, sizeof(struct spdk_scsi_lun));
/* dev with a single lun */
dev.lun[0] = lun;
spdk_scsi_dev_add_lun(&dev, lun, 0);
/* free the dev */
spdk_scsi_dev_destruct(&dev);

View File

@ -215,6 +215,9 @@ lun_construct(void)
static void
lun_destruct(struct spdk_scsi_lun *lun)
{
/* LUN will defer its removal if there are any unfinished tasks */
SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&lun->tasks));
spdk_scsi_lun_destruct(lun);
}
@ -282,11 +285,13 @@ lun_task_mgmt_execute_abort_task_not_supported(void)
CU_ASSERT_TRUE(rc < 0);
CU_ASSERT(mgmt_task.response == SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED);
lun_destruct(lun);
/* task is still on the tasks list */
CU_ASSERT_EQUAL(g_task_count, 1);
g_task_count = 0;
spdk_scsi_lun_complete_task(lun, &task);
CU_ASSERT_EQUAL(g_task_count, 0);
lun_destruct(lun);
}
static void
@ -343,11 +348,14 @@ lun_task_mgmt_execute_abort_task_all_not_supported(void)
CU_ASSERT_TRUE(rc < 0);
CU_ASSERT(mgmt_task.response == SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED);
lun_destruct(lun);
/* task is still on the tasks list */
CU_ASSERT_EQUAL(g_task_count, 1);
g_task_count = 0;
spdk_scsi_lun_complete_task(lun, &task);
CU_ASSERT_EQUAL(g_task_count, 0);
lun_destruct(lun);
}
static void
@ -511,11 +519,15 @@ lun_execute_scsi_task_pending(void)
/* Assert the task has been successfully added to the tasks queue */
CU_ASSERT(!TAILQ_EMPTY(&lun->tasks));
lun_destruct(lun);
/* task is still on the tasks list */
CU_ASSERT_EQUAL(g_task_count, 1);
g_task_count = 0;
/* Need to complete task so LUN might be removed right now */
spdk_scsi_lun_complete_task(lun, &task);
CU_ASSERT_EQUAL(g_task_count, 0);
lun_destruct(lun);
}
static void
@ -553,13 +565,11 @@ static void
lun_destruct_success(void)
{
struct spdk_scsi_lun *lun;
int rc;
lun = lun_construct();
rc = spdk_scsi_lun_destruct(lun);
spdk_scsi_lun_destruct(lun);
CU_ASSERT_EQUAL(rc, 0);
CU_ASSERT_EQUAL(g_task_count, 0);
}
@ -585,18 +595,6 @@ lun_construct_success(void)
CU_ASSERT_EQUAL(g_task_count, 0);
}
static void
lun_delete(void)
{
struct spdk_scsi_lun *lun;
int rc;
lun = lun_construct();
rc = spdk_scsi_lun_delete(lun);
CU_ASSERT_EQUAL(rc, 0);
}
int
main(int argc, char **argv)
{
@ -644,7 +642,6 @@ main(int argc, char **argv)
|| CU_add_test(suite, "destruct task - success", lun_destruct_success) == NULL
|| CU_add_test(suite, "construct - null ctx", lun_construct_null_ctx) == NULL
|| CU_add_test(suite, "construct - success", lun_construct_success) == NULL
|| CU_add_test(suite, "lun_delete", lun_delete) == NULL
) {
CU_cleanup_registry();
return CU_get_error();