lib/vhost: use user_dev's lock to protect vhost sessions

`spdk_vhost_dev` is created|deleted via RPC or APIs, and
we use a global `spdk_vhost_lock` to protect it, but for
some other places such as: vhost-user message processing,
we also use the global lock for now, actually we don't
need to use this lock, because these vhost-user messages
processing will not delete nor add vhost devices.

While here, we add a `spdk_vhost_user_dev` access lock to
protect vhost-user message processing as an optimization.

Change-Id: Ia9c45b056cebb7b65f458d56ed775a15e386f905
Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15184
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Shuhei Matsumoto <smatsumoto@nvidia.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Feng Li <lifeng1519@gmail.com>
This commit is contained in:
Changpeng Liu 2022-10-28 16:57:47 +08:00 committed by Jim Harris
parent d37a07f7c0
commit 376c25ed0c
6 changed files with 124 additions and 73 deletions

View File

@ -53,13 +53,6 @@ struct vhost_session_fn_ctx {
void *user_ctx; void *user_ctx;
}; };
static struct spdk_vhost_user_dev *
to_user_dev(struct spdk_vhost_dev *vdev)
{
assert(vdev != NULL);
return vdev->ctxt;
}
static void static void
__attribute__((constructor)) __attribute__((constructor))
_vhost_user_sem_init(void) _vhost_user_sem_init(void)
@ -919,21 +912,23 @@ new_connection(int vid)
return -1; return -1;
} }
spdk_vhost_lock();
ctrlr_name = &ifname[0]; ctrlr_name = &ifname[0];
dev_dirname_len = strlen(g_vhost_user_dev_dirname); dev_dirname_len = strlen(g_vhost_user_dev_dirname);
if (strncmp(ctrlr_name, g_vhost_user_dev_dirname, dev_dirname_len) == 0) { if (strncmp(ctrlr_name, g_vhost_user_dev_dirname, dev_dirname_len) == 0) {
ctrlr_name += dev_dirname_len; ctrlr_name += dev_dirname_len;
} }
spdk_vhost_lock();
vdev = spdk_vhost_dev_find(ctrlr_name); vdev = spdk_vhost_dev_find(ctrlr_name);
if (vdev == NULL) { if (vdev == NULL) {
SPDK_ERRLOG("Couldn't find device with vid %d to create connection for.\n", vid); SPDK_ERRLOG("Couldn't find device with vid %d to create connection for.\n", vid);
spdk_vhost_unlock(); spdk_vhost_unlock();
return -1; return -1;
} }
spdk_vhost_unlock();
user_dev = to_user_dev(vdev); user_dev = to_user_dev(vdev);
pthread_mutex_lock(&user_dev->lock);
/* We expect sessions inside user_dev->vsessions to be sorted in ascending /* We expect sessions inside user_dev->vsessions to be sorted in ascending
* order in regard of vsession->id. For now we always set id = vsessions_cnt++ * order in regard of vsession->id. For now we always set id = vsessions_cnt++
@ -941,6 +936,7 @@ new_connection(int vid)
* This is required for vhost_user_dev_foreach_session() to work. * This is required for vhost_user_dev_foreach_session() to work.
*/ */
if (user_dev->vsessions_num == UINT_MAX) { if (user_dev->vsessions_num == UINT_MAX) {
pthread_mutex_unlock(&user_dev->lock);
assert(false); assert(false);
return -EINVAL; return -EINVAL;
} }
@ -948,7 +944,7 @@ new_connection(int vid)
if (posix_memalign((void **)&vsession, SPDK_CACHE_LINE_SIZE, sizeof(*vsession) + if (posix_memalign((void **)&vsession, SPDK_CACHE_LINE_SIZE, sizeof(*vsession) +
user_dev->user_backend->session_ctx_size)) { user_dev->user_backend->session_ctx_size)) {
SPDK_ERRLOG("vsession alloc failed\n"); SPDK_ERRLOG("vsession alloc failed\n");
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
return -1; return -1;
} }
memset(vsession, 0, sizeof(*vsession) + user_dev->user_backend->session_ctx_size); memset(vsession, 0, sizeof(*vsession) + user_dev->user_backend->session_ctx_size);
@ -959,8 +955,8 @@ new_connection(int vid)
vsession->name = spdk_sprintf_alloc("%ss%u", vdev->name, vsession->vid); vsession->name = spdk_sprintf_alloc("%ss%u", vdev->name, vsession->vid);
if (vsession->name == NULL) { if (vsession->name == NULL) {
SPDK_ERRLOG("vsession alloc failed\n"); SPDK_ERRLOG("vsession alloc failed\n");
spdk_vhost_unlock();
free(vsession); free(vsession);
pthread_mutex_unlock(&user_dev->lock);
return -1; return -1;
} }
vsession->started = false; vsession->started = false;
@ -968,9 +964,9 @@ new_connection(int vid)
vsession->stats_check_interval = SPDK_VHOST_STATS_CHECK_INTERVAL_MS * vsession->stats_check_interval = SPDK_VHOST_STATS_CHECK_INTERVAL_MS *
spdk_get_ticks_hz() / 1000UL; spdk_get_ticks_hz() / 1000UL;
TAILQ_INSERT_TAIL(&user_dev->vsessions, vsession, tailq); TAILQ_INSERT_TAIL(&user_dev->vsessions, vsession, tailq);
vhost_session_install_rte_compat_hooks(vsession); vhost_session_install_rte_compat_hooks(vsession);
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
return 0; return 0;
} }
@ -983,6 +979,7 @@ vhost_user_session_start(void *arg1)
const struct spdk_vhost_user_dev_backend *backend; const struct spdk_vhost_user_dev_backend *backend;
int rc; int rc;
pthread_mutex_lock(&user_dev->lock);
backend = user_dev->user_backend; backend = user_dev->user_backend;
rc = backend->start_session(vdev, vsession, NULL); rc = backend->start_session(vdev, vsession, NULL);
if (rc == 0) { if (rc == 0) {
@ -991,6 +988,7 @@ vhost_user_session_start(void *arg1)
assert(user_dev->active_session_num < UINT32_MAX); assert(user_dev->active_session_num < UINT32_MAX);
user_dev->active_session_num++; user_dev->active_session_num++;
} }
pthread_mutex_unlock(&user_dev->lock);
} }
static int static int
@ -1119,17 +1117,18 @@ start_device(int vid)
{ {
struct spdk_vhost_dev *vdev; struct spdk_vhost_dev *vdev;
struct spdk_vhost_session *vsession; struct spdk_vhost_session *vsession;
struct spdk_vhost_user_dev *user_dev;
int rc = 0; int rc = 0;
spdk_vhost_lock();
vsession = vhost_session_find_by_vid(vid); vsession = vhost_session_find_by_vid(vid);
if (vsession == NULL) { if (vsession == NULL) {
rc = -1;
SPDK_ERRLOG("Couldn't find session with vid %d.\n", vid); SPDK_ERRLOG("Couldn't find session with vid %d.\n", vid);
goto out; return -1;
} }
vdev = vsession->vdev;
user_dev = to_user_dev(vdev);
pthread_mutex_lock(&user_dev->lock);
if (vsession->started) { if (vsession->started) {
/* already started, nothing to do */ /* already started, nothing to do */
goto out; goto out;
@ -1141,12 +1140,11 @@ start_device(int vid)
goto out; goto out;
} }
vdev = vsession->vdev;
vhost_user_session_set_coalescing(vdev, vsession, NULL); vhost_user_session_set_coalescing(vdev, vsession, NULL);
spdk_thread_send_msg(vdev->thread, vhost_user_session_start, vsession); spdk_thread_send_msg(vdev->thread, vhost_user_session_start, vsession);
out: out:
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
return rc; return rc;
} }
@ -1154,43 +1152,43 @@ static void
stop_device(int vid) stop_device(int vid)
{ {
struct spdk_vhost_session *vsession; struct spdk_vhost_session *vsession;
struct spdk_vhost_user_dev *user_dev;
spdk_vhost_lock();
vsession = vhost_session_find_by_vid(vid); vsession = vhost_session_find_by_vid(vid);
if (vsession == NULL) { if (vsession == NULL) {
SPDK_ERRLOG("Couldn't find session with vid %d.\n", vid); SPDK_ERRLOG("Couldn't find session with vid %d.\n", vid);
spdk_vhost_unlock();
return; return;
} }
user_dev = to_user_dev(vsession->vdev);
pthread_mutex_lock(&user_dev->lock);
if (!vsession->started) { if (!vsession->started) {
pthread_mutex_unlock(&user_dev->lock);
/* already stopped, nothing to do */ /* already stopped, nothing to do */
spdk_vhost_unlock();
return; return;
} }
_stop_session(vsession); _stop_session(vsession);
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
return;
} }
static void static void
destroy_connection(int vid) destroy_connection(int vid)
{ {
struct spdk_vhost_session *vsession; struct spdk_vhost_session *vsession;
struct spdk_vhost_user_dev *user_dev;
spdk_vhost_lock();
vsession = vhost_session_find_by_vid(vid); vsession = vhost_session_find_by_vid(vid);
if (vsession == NULL) { if (vsession == NULL) {
SPDK_ERRLOG("Couldn't find session with vid %d.\n", vid); SPDK_ERRLOG("Couldn't find session with vid %d.\n", vid);
spdk_vhost_unlock();
return; return;
} }
user_dev = to_user_dev(vsession->vdev);
pthread_mutex_lock(&user_dev->lock);
if (vsession->started) { if (vsession->started) {
if (_stop_session(vsession) != 0) { if (_stop_session(vsession) != 0) {
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
return; return;
} }
} }
@ -1203,7 +1201,7 @@ destroy_connection(int vid)
TAILQ_REMOVE(&to_user_dev(vsession->vdev)->vsessions, vsession, tailq); TAILQ_REMOVE(&to_user_dev(vsession->vdev)->vsessions, vsession, tailq);
free(vsession->name); free(vsession->name);
free(vsession); free(vsession);
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
} }
#if RTE_VERSION >= RTE_VERSION_NUM(21, 11, 0, 0) #if RTE_VERSION >= RTE_VERSION_NUM(21, 11, 0, 0)
@ -1236,15 +1234,24 @@ vhost_session_find_by_vid(int vid)
{ {
struct spdk_vhost_dev *vdev; struct spdk_vhost_dev *vdev;
struct spdk_vhost_session *vsession; struct spdk_vhost_session *vsession;
struct spdk_vhost_user_dev *user_dev;
spdk_vhost_lock();
for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL; for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL;
vdev = spdk_vhost_dev_next(vdev)) { vdev = spdk_vhost_dev_next(vdev)) {
TAILQ_FOREACH(vsession, &to_user_dev(vdev)->vsessions, tailq) { user_dev = to_user_dev(vdev);
pthread_mutex_lock(&user_dev->lock);
TAILQ_FOREACH(vsession, &user_dev->vsessions, tailq) {
if (vsession->vid == vid) { if (vsession->vid == vid) {
pthread_mutex_unlock(&user_dev->lock);
spdk_vhost_unlock();
return vsession; return vsession;
} }
} }
pthread_mutex_unlock(&user_dev->lock);
} }
spdk_vhost_unlock();
return NULL; return NULL;
} }
@ -1290,16 +1297,18 @@ static void
vhost_event_cb(void *arg1) vhost_event_cb(void *arg1)
{ {
struct vhost_session_fn_ctx *ctx = arg1; struct vhost_session_fn_ctx *ctx = arg1;
struct spdk_vhost_dev *vdev = ctx->vdev;
struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev);
struct spdk_vhost_session *vsession; struct spdk_vhost_session *vsession;
if (spdk_vhost_trylock() != 0) { if (pthread_mutex_trylock(&user_dev->lock) != 0) {
spdk_thread_send_msg(spdk_get_thread(), vhost_event_cb, arg1); spdk_thread_send_msg(spdk_get_thread(), vhost_event_cb, arg1);
return; return;
} }
vsession = vhost_session_find_by_id(ctx->vdev, ctx->vsession_id); vsession = vhost_session_find_by_id(vdev, ctx->vsession_id);
ctx->cb_fn(ctx->vdev, vsession, NULL); ctx->cb_fn(vdev, vsession, NULL);
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
} }
int int
@ -1309,6 +1318,7 @@ vhost_user_session_send_event(struct spdk_vhost_session *vsession,
{ {
struct vhost_session_fn_ctx ev_ctx = {0}; struct vhost_session_fn_ctx ev_ctx = {0};
struct spdk_vhost_dev *vdev = vsession->vdev; struct spdk_vhost_dev *vdev = vsession->vdev;
struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev);
ev_ctx.vdev = vdev; ev_ctx.vdev = vdev;
ev_ctx.vsession_id = vsession->id; ev_ctx.vsession_id = vsession->id;
@ -1316,9 +1326,9 @@ vhost_user_session_send_event(struct spdk_vhost_session *vsession,
spdk_thread_send_msg(vdev->thread, vhost_event_cb, &ev_ctx); spdk_thread_send_msg(vdev->thread, vhost_event_cb, &ev_ctx);
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
wait_for_semaphore(timeout_sec, errmsg); wait_for_semaphore(timeout_sec, errmsg);
spdk_vhost_lock(); pthread_mutex_lock(&user_dev->lock);
return g_dpdk_response; return g_dpdk_response;
} }
@ -1330,7 +1340,7 @@ foreach_session_finish_cb(void *arg1)
struct spdk_vhost_dev *vdev = ev_ctx->vdev; struct spdk_vhost_dev *vdev = ev_ctx->vdev;
struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev); struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev);
if (spdk_vhost_trylock() != 0) { if (pthread_mutex_trylock(&user_dev->lock) != 0) {
spdk_thread_send_msg(spdk_get_thread(), spdk_thread_send_msg(spdk_get_thread(),
foreach_session_finish_cb, arg1); foreach_session_finish_cb, arg1);
return; return;
@ -1342,7 +1352,7 @@ foreach_session_finish_cb(void *arg1)
ev_ctx->cpl_fn(vdev, ev_ctx->user_ctx); ev_ctx->cpl_fn(vdev, ev_ctx->user_ctx);
} }
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
free(ev_ctx); free(ev_ctx);
} }
@ -1350,16 +1360,17 @@ static void
foreach_session(void *arg1) foreach_session(void *arg1)
{ {
struct vhost_session_fn_ctx *ev_ctx = arg1; struct vhost_session_fn_ctx *ev_ctx = arg1;
struct spdk_vhost_session *vsession;
struct spdk_vhost_dev *vdev = ev_ctx->vdev; struct spdk_vhost_dev *vdev = ev_ctx->vdev;
struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev);
struct spdk_vhost_session *vsession;
int rc; int rc;
if (spdk_vhost_trylock() != 0) { if (pthread_mutex_trylock(&user_dev->lock) != 0) {
spdk_thread_send_msg(spdk_get_thread(), foreach_session, arg1); spdk_thread_send_msg(spdk_get_thread(), foreach_session, arg1);
return; return;
} }
TAILQ_FOREACH(vsession, &to_user_dev(vdev)->vsessions, tailq) { TAILQ_FOREACH(vsession, &user_dev->vsessions, tailq) {
rc = ev_ctx->cb_fn(vdev, vsession, ev_ctx->user_ctx); rc = ev_ctx->cb_fn(vdev, vsession, ev_ctx->user_ctx);
if (rc < 0) { if (rc < 0) {
goto out; goto out;
@ -1367,8 +1378,7 @@ foreach_session(void *arg1)
} }
out: out:
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
spdk_thread_send_msg(g_vhost_user_init_thread, foreach_session_finish_cb, arg1); spdk_thread_send_msg(g_vhost_user_init_thread, foreach_session_finish_cb, arg1);
} }
@ -1393,8 +1403,10 @@ vhost_user_dev_foreach_session(struct spdk_vhost_dev *vdev,
ev_ctx->cpl_fn = cpl_fn; ev_ctx->cpl_fn = cpl_fn;
ev_ctx->user_ctx = arg; ev_ctx->user_ctx = arg;
pthread_mutex_lock(&user_dev->lock);
assert(user_dev->pending_async_op_num < UINT32_MAX); assert(user_dev->pending_async_op_num < UINT32_MAX);
user_dev->pending_async_op_num++; user_dev->pending_async_op_num++;
pthread_mutex_unlock(&user_dev->lock);
spdk_thread_send_msg(vdev->thread, foreach_session, ev_ctx); spdk_thread_send_msg(vdev->thread, foreach_session, ev_ctx);
} }
@ -1453,6 +1465,7 @@ extern_vhost_pre_msg_handler(int vid, void *_msg)
{ {
struct vhost_user_msg *msg = _msg; struct vhost_user_msg *msg = _msg;
struct spdk_vhost_session *vsession; struct spdk_vhost_session *vsession;
struct spdk_vhost_user_dev *user_dev;
vsession = vhost_session_find_by_vid(vid); vsession = vhost_session_find_by_vid(vid);
if (vsession == NULL) { if (vsession == NULL) {
@ -1460,17 +1473,24 @@ extern_vhost_pre_msg_handler(int vid, void *_msg)
assert(false); assert(false);
return RTE_VHOST_MSG_RESULT_ERR; return RTE_VHOST_MSG_RESULT_ERR;
} }
user_dev = to_user_dev(vsession->vdev);
switch (msg->request) { switch (msg->request) {
case VHOST_USER_GET_VRING_BASE: case VHOST_USER_GET_VRING_BASE:
pthread_mutex_lock(&user_dev->lock);
if (vsession->started) { if (vsession->started) {
pthread_mutex_unlock(&user_dev->lock);
/* `stop_device` is running in synchronous, it
* will hold this lock again before exiting.
*/
g_spdk_vhost_ops.destroy_device(vid); g_spdk_vhost_ops.destroy_device(vid);
} }
pthread_mutex_unlock(&user_dev->lock);
break; break;
case VHOST_USER_GET_CONFIG: { case VHOST_USER_GET_CONFIG: {
int rc = 0; int rc = 0;
spdk_vhost_lock(); pthread_mutex_lock(&user_dev->lock);
if (vsession->vdev->backend->vhost_get_config) { if (vsession->vdev->backend->vhost_get_config) {
rc = vsession->vdev->backend->vhost_get_config(vsession->vdev, rc = vsession->vdev->backend->vhost_get_config(vsession->vdev,
msg->payload.cfg.region, msg->payload.cfg.size); msg->payload.cfg.region, msg->payload.cfg.size);
@ -1478,20 +1498,20 @@ extern_vhost_pre_msg_handler(int vid, void *_msg)
msg->size = 0; msg->size = 0;
} }
} }
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
return RTE_VHOST_MSG_RESULT_REPLY; return RTE_VHOST_MSG_RESULT_REPLY;
} }
case VHOST_USER_SET_CONFIG: { case VHOST_USER_SET_CONFIG: {
int rc = 0; int rc = 0;
spdk_vhost_lock(); pthread_mutex_lock(&user_dev->lock);
if (vsession->vdev->backend->vhost_set_config) { if (vsession->vdev->backend->vhost_set_config) {
rc = vsession->vdev->backend->vhost_set_config(vsession->vdev, rc = vsession->vdev->backend->vhost_set_config(vsession->vdev,
msg->payload.cfg.region, msg->payload.cfg.offset, msg->payload.cfg.region, msg->payload.cfg.offset,
msg->payload.cfg.size, msg->payload.cfg.flags); msg->payload.cfg.size, msg->payload.cfg.flags);
} }
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
return rc == 0 ? RTE_VHOST_MSG_RESULT_OK : RTE_VHOST_MSG_RESULT_ERR; return rc == 0 ? RTE_VHOST_MSG_RESULT_OK : RTE_VHOST_MSG_RESULT_ERR;
} }
@ -1507,6 +1527,7 @@ extern_vhost_post_msg_handler(int vid, void *_msg)
{ {
struct vhost_user_msg *msg = _msg; struct vhost_user_msg *msg = _msg;
struct spdk_vhost_session *vsession; struct spdk_vhost_session *vsession;
struct spdk_vhost_user_dev *user_dev;
uint16_t qid; uint16_t qid;
int rc; int rc;
@ -1516,6 +1537,7 @@ extern_vhost_post_msg_handler(int vid, void *_msg)
assert(false); assert(false);
return RTE_VHOST_MSG_RESULT_ERR; return RTE_VHOST_MSG_RESULT_ERR;
} }
user_dev = to_user_dev(vsession->vdev);
if (msg->request == VHOST_USER_SET_MEM_TABLE) { if (msg->request == VHOST_USER_SET_MEM_TABLE) {
vhost_register_memtable_if_required(vsession, vid); vhost_register_memtable_if_required(vsession, vid);
@ -1546,9 +1568,13 @@ extern_vhost_post_msg_handler(int vid, void *_msg)
/* vhost-user spec tells us to start polling a queue after receiving /* vhost-user spec tells us to start polling a queue after receiving
* its SET_VRING_KICK message. Let's do it! * its SET_VRING_KICK message. Let's do it!
*/ */
pthread_mutex_lock(&user_dev->lock);
if (!vsession->started) { if (!vsession->started) {
pthread_mutex_unlock(&user_dev->lock);
g_spdk_vhost_ops.new_device(vid); g_spdk_vhost_ops.new_device(vid);
return RTE_VHOST_MSG_RESULT_NOT_HANDLED;
} }
pthread_mutex_unlock(&user_dev->lock);
break; break;
default: default:
break; break;
@ -1782,6 +1808,7 @@ vhost_user_dev_register(struct spdk_vhost_dev *vdev, const char *name, struct sp
user_dev->vdev = vdev; user_dev->vdev = vdev;
user_dev->registered = true; user_dev->registered = true;
TAILQ_INIT(&user_dev->vsessions); TAILQ_INIT(&user_dev->vsessions);
pthread_mutex_init(&user_dev->lock, NULL);
vhost_user_dev_set_coalescing(user_dev, SPDK_VHOST_COALESCING_DELAY_BASE_US, vhost_user_dev_set_coalescing(user_dev, SPDK_VHOST_COALESCING_DELAY_BASE_US,
SPDK_VHOST_VQ_IOPS_COALESCING_THRESHOLD); SPDK_VHOST_VQ_IOPS_COALESCING_THRESHOLD);
@ -1789,6 +1816,7 @@ vhost_user_dev_register(struct spdk_vhost_dev *vdev, const char *name, struct sp
if (vhost_register_unix_socket(path, name, vdev->virtio_features, vdev->disabled_features, if (vhost_register_unix_socket(path, name, vdev->virtio_features, vdev->disabled_features,
vdev->protocol_features)) { vdev->protocol_features)) {
spdk_thread_send_msg(vdev->thread, vhost_dev_thread_exit, NULL); spdk_thread_send_msg(vdev->thread, vhost_dev_thread_exit, NULL);
pthread_mutex_destroy(&user_dev->lock);
free(user_dev); free(user_dev);
free(vdev->path); free(vdev->path);
return -EIO; return -EIO;
@ -1802,14 +1830,18 @@ vhost_user_dev_unregister(struct spdk_vhost_dev *vdev)
{ {
struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev); struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev);
pthread_mutex_lock(&user_dev->lock);
if (user_dev->pending_async_op_num) { if (user_dev->pending_async_op_num) {
pthread_mutex_unlock(&user_dev->lock);
return -EBUSY; return -EBUSY;
} }
if (!TAILQ_EMPTY(&user_dev->vsessions)) { if (!TAILQ_EMPTY(&user_dev->vsessions)) {
SPDK_ERRLOG("Controller %s has still valid connection.\n", vdev->name); SPDK_ERRLOG("Controller %s has still valid connection.\n", vdev->name);
pthread_mutex_unlock(&user_dev->lock);
return -EBUSY; return -EBUSY;
} }
pthread_mutex_unlock(&user_dev->lock);
/* There are no valid connections now, and it's not an error if the domain /* There are no valid connections now, and it's not an error if the domain
* socket was already removed by shutdown thread. * socket was already removed by shutdown thread.
@ -1817,6 +1849,8 @@ vhost_user_dev_unregister(struct spdk_vhost_dev *vdev)
vhost_driver_unregister(vdev->path); vhost_driver_unregister(vdev->path);
spdk_thread_send_msg(vdev->thread, vhost_dev_thread_exit, NULL); spdk_thread_send_msg(vdev->thread, vhost_dev_thread_exit, NULL);
pthread_mutex_destroy(&user_dev->lock);
free(user_dev); free(user_dev);
free(vdev->path); free(vdev->path);
@ -1868,16 +1902,18 @@ vhost_user_session_shutdown(void *vhost_cb)
{ {
struct spdk_vhost_dev *vdev = NULL; struct spdk_vhost_dev *vdev = NULL;
struct spdk_vhost_session *vsession; struct spdk_vhost_session *vsession;
struct spdk_vhost_user_dev *user_dev;
for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL; for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL;
vdev = spdk_vhost_dev_next(vdev)) { vdev = spdk_vhost_dev_next(vdev)) {
spdk_vhost_lock(); user_dev = to_user_dev(vdev);
TAILQ_FOREACH(vsession, &to_user_dev(vdev)->vsessions, tailq) { pthread_mutex_lock(&user_dev->lock);
TAILQ_FOREACH(vsession, &user_dev->vsessions, tailq) {
if (vsession->started) { if (vsession->started) {
_stop_session(vsession); _stop_session(vsession);
} }
} }
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
vhost_driver_unregister(vdev->path); vhost_driver_unregister(vdev->path);
} }

View File

@ -127,13 +127,16 @@ vhost_dev_register(struct spdk_vhost_dev *vdev, const char *name, const char *ma
return -EINVAL; return -EINVAL;
} }
spdk_vhost_lock();
if (spdk_vhost_dev_find(name)) { if (spdk_vhost_dev_find(name)) {
SPDK_ERRLOG("vhost controller %s already exists.\n", name); SPDK_ERRLOG("vhost controller %s already exists.\n", name);
spdk_vhost_unlock();
return -EEXIST; return -EEXIST;
} }
vdev->name = strdup(name); vdev->name = strdup(name);
if (vdev->name == NULL) { if (vdev->name == NULL) {
spdk_vhost_unlock();
return -EIO; return -EIO;
} }
@ -145,10 +148,12 @@ vhost_dev_register(struct spdk_vhost_dev *vdev, const char *name, const char *ma
} }
if (rc != 0) { if (rc != 0) {
free(vdev->name); free(vdev->name);
spdk_vhost_unlock();
return rc; return rc;
} }
TAILQ_INSERT_TAIL(&g_vhost_devices, vdev, tailq); TAILQ_INSERT_TAIL(&g_vhost_devices, vdev, tailq);
spdk_vhost_unlock();
SPDK_INFOLOG(vhost, "Controller %s: new controller added\n", vdev->name); SPDK_INFOLOG(vhost, "Controller %s: new controller added\n", vdev->name);
return 0; return 0;
@ -171,11 +176,13 @@ vhost_dev_unregister(struct spdk_vhost_dev *vdev)
SPDK_INFOLOG(vhost, "Controller %s: removed\n", vdev->name); SPDK_INFOLOG(vhost, "Controller %s: removed\n", vdev->name);
free(vdev->name); free(vdev->name);
TAILQ_REMOVE(&g_vhost_devices, vdev, tailq);
spdk_vhost_lock();
TAILQ_REMOVE(&g_vhost_devices, vdev, tailq);
if (TAILQ_EMPTY(&g_vhost_devices) && g_fini_cb != NULL) { if (TAILQ_EMPTY(&g_vhost_devices) && g_fini_cb != NULL) {
g_fini_cb(); g_fini_cb();
} }
spdk_vhost_unlock();
return 0; return 0;
} }
@ -265,9 +272,7 @@ vhost_fini(void)
{ {
struct spdk_vhost_dev *vdev, *tmp; struct spdk_vhost_dev *vdev, *tmp;
spdk_vhost_lock();
if (spdk_vhost_dev_next(NULL) == NULL) { if (spdk_vhost_dev_next(NULL) == NULL) {
spdk_vhost_unlock();
g_fini_cb(); g_fini_cb();
return; return;
} }
@ -279,7 +284,6 @@ vhost_fini(void)
/* don't care if it fails, there's nothing we can do for now */ /* don't care if it fails, there's nothing we can do for now */
vdev = tmp; vdev = tmp;
} }
spdk_vhost_unlock();
/* g_fini_cb will get called when last device is unregistered. */ /* g_fini_cb will get called when last device is unregistered. */
} }

View File

@ -1141,10 +1141,8 @@ vhost_session_bdev_resize_cb(struct spdk_vhost_dev *vdev,
static void static void
vhost_user_blk_resize_cb(struct spdk_vhost_dev *vdev, bdev_event_cb_complete cb, void *cb_arg) vhost_user_blk_resize_cb(struct spdk_vhost_dev *vdev, bdev_event_cb_complete cb, void *cb_arg)
{ {
spdk_vhost_lock();
vhost_user_dev_foreach_session(vdev, vhost_session_bdev_resize_cb, vhost_user_dev_foreach_session(vdev, vhost_session_bdev_resize_cb,
cb, cb_arg); cb, cb_arg);
spdk_vhost_unlock();
} }
static int static int
@ -1182,10 +1180,8 @@ vhost_user_bdev_remove_cb(struct spdk_vhost_dev *vdev, bdev_event_cb_complete cb
SPDK_WARNLOG("%s: hot-removing bdev - all further requests will fail.\n", SPDK_WARNLOG("%s: hot-removing bdev - all further requests will fail.\n",
vdev->name); vdev->name);
spdk_vhost_lock();
vhost_user_dev_foreach_session(vdev, vhost_user_session_bdev_remove_cb, vhost_user_dev_foreach_session(vdev, vhost_user_session_bdev_remove_cb,
cb, cb_arg); cb, cb_arg);
spdk_vhost_unlock();
} }
static void static void
@ -1363,9 +1359,10 @@ destroy_session_poller_cb(void *arg)
{ {
struct spdk_vhost_blk_session *bvsession = arg; struct spdk_vhost_blk_session *bvsession = arg;
struct spdk_vhost_session *vsession = &bvsession->vsession; struct spdk_vhost_session *vsession = &bvsession->vsession;
struct spdk_vhost_user_dev *user_dev = to_user_dev(vsession->vdev);
int i; int i;
if (vsession->task_cnt > 0 || spdk_vhost_trylock() != 0) { if (vsession->task_cnt > 0 || (pthread_mutex_trylock(&user_dev->lock) != 0)) {
assert(vsession->stop_retry_count > 0); assert(vsession->stop_retry_count > 0);
vsession->stop_retry_count--; vsession->stop_retry_count--;
if (vsession->stop_retry_count == 0) { if (vsession->stop_retry_count == 0) {
@ -1395,7 +1392,7 @@ destroy_session_poller_cb(void *arg)
spdk_poller_unregister(&bvsession->stop_poller); spdk_poller_unregister(&bvsession->stop_poller);
vhost_user_session_stop_done(vsession, 0); vhost_user_session_stop_done(vsession, 0);
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
return SPDK_POLLER_BUSY; return SPDK_POLLER_BUSY;
} }
@ -1599,8 +1596,6 @@ spdk_vhost_blk_construct(const char *name, const char *cpumask, const char *dev_
const char *transport_name = VIRTIO_BLK_DEFAULT_TRANSPORT; const char *transport_name = VIRTIO_BLK_DEFAULT_TRANSPORT;
int ret = 0; int ret = 0;
spdk_vhost_lock();
bvdev = calloc(1, sizeof(*bvdev)); bvdev = calloc(1, sizeof(*bvdev));
if (bvdev == NULL) { if (bvdev == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
@ -1669,7 +1664,6 @@ out:
if (ret != 0 && bvdev) { if (ret != 0 && bvdev) {
free(bvdev); free(bvdev);
} }
spdk_vhost_unlock();
return ret; return ret;
} }

View File

@ -156,6 +156,9 @@ struct spdk_vhost_user_dev {
bool registered; bool registered;
/* Use this lock to protect multiple sessions. */
pthread_mutex_t lock;
/* Current connections to the device */ /* Current connections to the device */
TAILQ_HEAD(, spdk_vhost_session) vsessions; TAILQ_HEAD(, spdk_vhost_session) vsessions;
@ -188,6 +191,13 @@ struct spdk_vhost_dev {
TAILQ_ENTRY(spdk_vhost_dev) tailq; TAILQ_ENTRY(spdk_vhost_dev) tailq;
}; };
static inline struct spdk_vhost_user_dev *
to_user_dev(struct spdk_vhost_dev *vdev)
{
assert(vdev != NULL);
return vdev->ctxt;
}
/** /**
* \param vdev vhost device. * \param vdev vhost device.
* \param vsession vhost session. * \param vsession vhost session.

View File

@ -293,9 +293,9 @@ rpc_vhost_delete_controller(struct spdk_jsonrpc_request *request,
rc = -ENODEV; rc = -ENODEV;
goto invalid; goto invalid;
} }
spdk_vhost_unlock();
rc = spdk_vhost_dev_remove(vdev); rc = spdk_vhost_dev_remove(vdev);
spdk_vhost_unlock();
if (rc < 0) { if (rc < 0) {
goto invalid; goto invalid;
} }

View File

@ -169,6 +169,15 @@ vhost_scsi_task_free_cb(struct spdk_scsi_task *scsi_task)
task->used = false; task->used = false;
} }
static void
vhost_scsi_dev_unregister(void *arg1)
{
struct spdk_vhost_scsi_dev *svdev = arg1;
vhost_dev_unregister(&svdev->vdev);
free(svdev);
}
static void static void
remove_scsi_tgt(struct spdk_vhost_scsi_dev *svdev, remove_scsi_tgt(struct spdk_vhost_scsi_dev *svdev,
unsigned scsi_tgt_num) unsigned scsi_tgt_num)
@ -189,8 +198,10 @@ remove_scsi_tgt(struct spdk_vhost_scsi_dev *svdev,
SPDK_INFOLOG(vhost, "removed target 'Target %u'\n", scsi_tgt_num); SPDK_INFOLOG(vhost, "removed target 'Target %u'\n", scsi_tgt_num);
if (--svdev->ref == 0 && svdev->registered == false) { if (--svdev->ref == 0 && svdev->registered == false) {
vhost_dev_unregister(&svdev->vdev); /* `remove_scsi_tgt` is running under vhost-user lock, so we
free(svdev); * unregister the device in next poll.
*/
spdk_thread_send_msg(spdk_get_thread(), vhost_scsi_dev_unregister, svdev);
} }
} }
@ -248,12 +259,10 @@ process_removed_devs(struct spdk_vhost_scsi_session *svsession)
state->dev = NULL; state->dev = NULL;
state->status = VHOST_SCSI_DEV_REMOVED; state->status = VHOST_SCSI_DEV_REMOVED;
/* try to detach it globally */ /* try to detach it globally */
spdk_vhost_lock();
vhost_user_dev_foreach_session(&svsession->svdev->vdev, vhost_user_dev_foreach_session(&svsession->svdev->vdev,
vhost_scsi_session_process_removed, vhost_scsi_session_process_removed,
vhost_scsi_dev_process_removed_cpl_cb, vhost_scsi_dev_process_removed_cpl_cb,
(void *)(uintptr_t)i); (void *)(uintptr_t)i);
spdk_vhost_unlock();
} }
} }
} }
@ -860,19 +869,16 @@ spdk_vhost_scsi_dev_construct(const char *name, const char *cpumask)
svdev->vdev.disabled_features = SPDK_VHOST_SCSI_DISABLED_FEATURES; svdev->vdev.disabled_features = SPDK_VHOST_SCSI_DISABLED_FEATURES;
svdev->vdev.protocol_features = SPDK_VHOST_SCSI_PROTOCOL_FEATURES; svdev->vdev.protocol_features = SPDK_VHOST_SCSI_PROTOCOL_FEATURES;
spdk_vhost_lock();
rc = vhost_dev_register(&svdev->vdev, name, cpumask, NULL, rc = vhost_dev_register(&svdev->vdev, name, cpumask, NULL,
&spdk_vhost_scsi_device_backend, &spdk_vhost_scsi_device_backend,
&spdk_vhost_scsi_user_device_backend); &spdk_vhost_scsi_user_device_backend);
if (rc) { if (rc) {
free(svdev); free(svdev);
spdk_vhost_unlock();
return rc; return rc;
} }
svdev->registered = true; svdev->registered = true;
spdk_vhost_unlock();
return rc; return rc;
} }
@ -1409,10 +1415,11 @@ destroy_session_poller_cb(void *arg)
{ {
struct spdk_vhost_scsi_session *svsession = arg; struct spdk_vhost_scsi_session *svsession = arg;
struct spdk_vhost_session *vsession = &svsession->vsession; struct spdk_vhost_session *vsession = &svsession->vsession;
struct spdk_vhost_user_dev *user_dev = to_user_dev(vsession->vdev);
struct spdk_scsi_dev_session_state *state; struct spdk_scsi_dev_session_state *state;
uint32_t i; uint32_t i;
if (vsession->task_cnt > 0 || spdk_vhost_trylock() != 0) { if (vsession->task_cnt > 0 || (pthread_mutex_trylock(&user_dev->lock) != 0)) {
assert(vsession->stop_retry_count > 0); assert(vsession->stop_retry_count > 0);
vsession->stop_retry_count--; vsession->stop_retry_count--;
if (vsession->stop_retry_count == 0) { if (vsession->stop_retry_count == 0) {
@ -1461,7 +1468,7 @@ destroy_session_poller_cb(void *arg)
spdk_poller_unregister(&svsession->stop_poller); spdk_poller_unregister(&svsession->stop_poller);
vhost_user_session_stop_done(vsession, 0); vhost_user_session_stop_done(vsession, 0);
spdk_vhost_unlock(); pthread_mutex_unlock(&user_dev->lock);
return SPDK_POLLER_BUSY; return SPDK_POLLER_BUSY;
} }