lib/vhost: separate out rte_vhost code from vhost
This patch separates out rte_vhost code responsible for vhost init/fini and vdev register/unregister from vhost. Signed-off-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com> Change-Id: Ie69ecd3b2659c805c9c0b0a0076996ef85c8fe71 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/9535 Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com> Community-CI: Mellanox Build Bot Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
This commit is contained in:
parent
19d1b5f965
commit
9dda3d60b9
@ -117,6 +117,49 @@ vhost_session_mem_unregister(struct rte_vhost_memory *mem)
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
_stop_session(struct spdk_vhost_session *vsession)
|
||||
{
|
||||
struct spdk_vhost_dev *vdev = vsession->vdev;
|
||||
struct spdk_vhost_virtqueue *q;
|
||||
int rc;
|
||||
uint16_t i;
|
||||
|
||||
rc = vdev->backend->stop_session(vsession);
|
||||
if (rc != 0) {
|
||||
SPDK_ERRLOG("Couldn't stop device with vid %d.\n", vsession->vid);
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (i = 0; i < vsession->max_queues; i++) {
|
||||
q = &vsession->virtqueue[i];
|
||||
|
||||
/* vring.desc and vring.desc_packed are in a union struct
|
||||
* so q->vring.desc can replace q->vring.desc_packed.
|
||||
*/
|
||||
if (q->vring.desc == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Packed virtqueues support up to 2^15 entries each
|
||||
* so left one bit can be used as wrap counter.
|
||||
*/
|
||||
if (q->packed.packed_ring) {
|
||||
q->last_avail_idx = q->last_avail_idx |
|
||||
((uint16_t)q->packed.avail_phase << 15);
|
||||
q->last_used_idx = q->last_used_idx |
|
||||
((uint16_t)q->packed.used_phase << 15);
|
||||
}
|
||||
|
||||
rte_vhost_set_vring_base(vsession->vid, i, q->last_avail_idx, q->last_used_idx);
|
||||
}
|
||||
|
||||
vhost_session_mem_unregister(vsession->mem);
|
||||
free(vsession->mem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
new_connection(int vid)
|
||||
{
|
||||
@ -488,3 +531,150 @@ spdk_vhost_set_socket_path(const char *basename)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
vhost_dev_thread_exit(void *arg1)
|
||||
{
|
||||
spdk_thread_exit(spdk_get_thread());
|
||||
}
|
||||
|
||||
int
|
||||
vhost_user_dev_register(struct spdk_vhost_dev *vdev, const char *name, struct spdk_cpuset *cpumask,
|
||||
const struct spdk_vhost_dev_backend *backend)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (snprintf(path, sizeof(path), "%s%s", g_vhost_user_dev_dirname, name) >= (int)sizeof(path)) {
|
||||
SPDK_ERRLOG("Resulting socket path for controller %s is too long: %s%s\n",
|
||||
name,g_vhost_user_dev_dirname, name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vdev->path = strdup(path);
|
||||
if (vdev->path == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
vdev->thread = spdk_thread_create(vdev->name, cpumask);
|
||||
if (vdev->thread == NULL) {
|
||||
free(vdev->path);
|
||||
SPDK_ERRLOG("Failed to create thread for vhost controller %s.\n", name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
vdev->registered = true;
|
||||
vdev->backend = backend;
|
||||
TAILQ_INIT(&vdev->vsessions);
|
||||
|
||||
vhost_user_dev_set_coalescing(vdev, SPDK_VHOST_COALESCING_DELAY_BASE_US,
|
||||
SPDK_VHOST_VQ_IOPS_COALESCING_THRESHOLD);
|
||||
|
||||
if (vhost_register_unix_socket(path, name, vdev->virtio_features, vdev->disabled_features,
|
||||
vdev->protocol_features)) {
|
||||
spdk_thread_send_msg(vdev->thread, vhost_dev_thread_exit, NULL);
|
||||
free(vdev->path);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
vhost_user_dev_unregister(struct spdk_vhost_dev *vdev)
|
||||
{
|
||||
if (!TAILQ_EMPTY(&vdev->vsessions)) {
|
||||
SPDK_ERRLOG("Controller %s has still valid connection.\n", vdev->name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (vdev->registered && vhost_driver_unregister(vdev->path) != 0) {
|
||||
SPDK_ERRLOG("Could not unregister controller %s with vhost library\n"
|
||||
"Check if domain socket %s still exists\n",
|
||||
vdev->name, vdev->path);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spdk_thread_send_msg(vdev->thread, vhost_dev_thread_exit, NULL);
|
||||
free(vdev->path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool g_vhost_user_started = false;
|
||||
|
||||
int
|
||||
vhost_user_init(void)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (g_vhost_user_started) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (g_vhost_user_dev_dirname[0] == '\0') {
|
||||
if (getcwd(g_vhost_user_dev_dirname, sizeof(g_vhost_user_dev_dirname) - 1) == NULL) {
|
||||
SPDK_ERRLOG("getcwd failed (%d): %s\n", errno, spdk_strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = strlen(g_vhost_user_dev_dirname);
|
||||
if (g_vhost_user_dev_dirname[len - 1] != '/') {
|
||||
g_vhost_user_dev_dirname[len] = '/';
|
||||
g_vhost_user_dev_dirname[len + 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
g_vhost_user_started = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
vhost_user_session_shutdown(void *arg)
|
||||
{
|
||||
struct spdk_vhost_dev *vdev = NULL;
|
||||
struct spdk_vhost_session *vsession;
|
||||
vhost_fini_cb vhost_cb = arg;
|
||||
|
||||
for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL;
|
||||
vdev = spdk_vhost_dev_next(vdev)) {
|
||||
spdk_vhost_lock();
|
||||
TAILQ_FOREACH(vsession, &vdev->vsessions, tailq) {
|
||||
if (vsession->started) {
|
||||
_stop_session(vsession);
|
||||
}
|
||||
}
|
||||
spdk_vhost_unlock();
|
||||
vhost_driver_unregister(vdev->path);
|
||||
vdev->registered = false;
|
||||
}
|
||||
|
||||
SPDK_INFOLOG(vhost, "Exiting\n");
|
||||
spdk_thread_send_msg(g_vhost_init_thread, vhost_cb, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
vhost_user_fini(vhost_fini_cb vhost_cb)
|
||||
{
|
||||
pthread_t tid;
|
||||
int rc;
|
||||
|
||||
if (!g_vhost_user_started) {
|
||||
vhost_cb(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
g_vhost_user_started = false;
|
||||
|
||||
/* rte_vhost API for removing sockets is not asynchronous. Since it may call SPDK
|
||||
* ops for stopping a device or removing a connection, we need to call it from
|
||||
* a separate thread to avoid deadlock.
|
||||
*/
|
||||
rc = pthread_create(&tid, NULL, &vhost_user_session_shutdown, vhost_cb);
|
||||
if (rc < 0) {
|
||||
SPDK_ERRLOG("Failed to start session shutdown thread (%d): %s\n", rc, spdk_strerror(rc));
|
||||
abort();
|
||||
}
|
||||
pthread_detach(tid);
|
||||
}
|
||||
|
@ -47,9 +47,7 @@ bool g_packed_ring_recovery = false;
|
||||
static struct spdk_cpuset g_vhost_core_mask;
|
||||
|
||||
/* Thread performing all vhost management operations */
|
||||
static struct spdk_thread *g_vhost_init_thread;
|
||||
|
||||
static spdk_vhost_fini_cb g_fini_cpl_cb;
|
||||
struct spdk_thread *g_vhost_init_thread = NULL;
|
||||
|
||||
/** Return code for the current DPDK callback */
|
||||
static int g_dpdk_response;
|
||||
@ -852,17 +850,10 @@ vhost_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
vhost_dev_thread_exit(void *arg1)
|
||||
{
|
||||
spdk_thread_exit(spdk_get_thread());
|
||||
}
|
||||
|
||||
int
|
||||
vhost_dev_register(struct spdk_vhost_dev *vdev, const char *name, const char *mask_str,
|
||||
const struct spdk_vhost_dev_backend *backend)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
struct spdk_cpuset cpumask = {};
|
||||
int rc;
|
||||
|
||||
@ -883,72 +874,36 @@ vhost_dev_register(struct spdk_vhost_dev *vdev, const char *name, const char *ma
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if (snprintf(path, sizeof(path), "%s%s", g_vhost_user_dev_dirname, name) >= (int)sizeof(path)) {
|
||||
SPDK_ERRLOG("Resulting socket path for controller %s is too long: %s%s\n",
|
||||
name, g_vhost_user_dev_dirname, name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vdev->name = strdup(name);
|
||||
vdev->path = strdup(path);
|
||||
if (vdev->name == NULL || vdev->path == NULL) {
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
if (vdev->name == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
vdev->thread = spdk_thread_create(vdev->name, &cpumask);
|
||||
if (vdev->thread == NULL) {
|
||||
SPDK_ERRLOG("Failed to create thread for vhost controller %s.\n", name);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vdev->registered = true;
|
||||
vdev->backend = backend;
|
||||
TAILQ_INIT(&vdev->vsessions);
|
||||
|
||||
vhost_user_dev_set_coalescing(vdev, SPDK_VHOST_COALESCING_DELAY_BASE_US,
|
||||
SPDK_VHOST_VQ_IOPS_COALESCING_THRESHOLD);
|
||||
|
||||
if (vhost_register_unix_socket(path, name, vdev->virtio_features, vdev->disabled_features,
|
||||
vdev->protocol_features)) {
|
||||
spdk_thread_send_msg(vdev->thread, vhost_dev_thread_exit, NULL);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
rc = vhost_user_dev_register(vdev, name, &cpumask, backend);
|
||||
if (rc != 0) {
|
||||
free(vdev->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
TAILQ_INSERT_TAIL(&g_vhost_devices, vdev, tailq);
|
||||
|
||||
SPDK_INFOLOG(vhost, "Controller %s: new controller added\n", vdev->name);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
free(vdev->name);
|
||||
free(vdev->path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
vhost_dev_unregister(struct spdk_vhost_dev *vdev)
|
||||
{
|
||||
if (!TAILQ_EMPTY(&vdev->vsessions)) {
|
||||
SPDK_ERRLOG("Controller %s has still valid connection.\n", vdev->name);
|
||||
return -EBUSY;
|
||||
}
|
||||
int rc;
|
||||
|
||||
if (vdev->registered && vhost_driver_unregister(vdev->path) != 0) {
|
||||
SPDK_ERRLOG("Could not unregister controller %s with vhost library\n"
|
||||
"Check if domain socket %s still exists\n",
|
||||
vdev->name, vdev->path);
|
||||
return -EIO;
|
||||
rc = vhost_user_dev_unregister(vdev);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
SPDK_INFOLOG(vhost, "Controller %s: removed\n", vdev->name);
|
||||
|
||||
spdk_thread_send_msg(vdev->thread, vhost_dev_thread_exit, NULL);
|
||||
|
||||
free(vdev->name);
|
||||
free(vdev->path);
|
||||
TAILQ_REMOVE(&g_vhost_devices, vdev, tailq);
|
||||
return 0;
|
||||
}
|
||||
@ -1128,49 +1083,6 @@ vhost_dev_foreach_session(struct spdk_vhost_dev *vdev,
|
||||
spdk_thread_send_msg(vdev->thread, foreach_session, ev_ctx);
|
||||
}
|
||||
|
||||
static int
|
||||
_stop_session(struct spdk_vhost_session *vsession)
|
||||
{
|
||||
struct spdk_vhost_dev *vdev = vsession->vdev;
|
||||
struct spdk_vhost_virtqueue *q;
|
||||
int rc;
|
||||
uint16_t i;
|
||||
|
||||
rc = vdev->backend->stop_session(vsession);
|
||||
if (rc != 0) {
|
||||
SPDK_ERRLOG("Couldn't stop device with vid %d.\n", vsession->vid);
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (i = 0; i < vsession->max_queues; i++) {
|
||||
q = &vsession->virtqueue[i];
|
||||
|
||||
/* vring.desc and vring.desc_packed are in a union struct
|
||||
* so q->vring.desc can replace q->vring.desc_packed.
|
||||
*/
|
||||
if (q->vring.desc == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Packed virtqueues support up to 2^15 entries each
|
||||
* so left one bit can be used as wrap counter.
|
||||
*/
|
||||
if (q->packed.packed_ring) {
|
||||
q->last_avail_idx = q->last_avail_idx |
|
||||
((uint16_t)q->packed.avail_phase << 15);
|
||||
q->last_used_idx = q->last_used_idx |
|
||||
((uint16_t)q->packed.used_phase << 15);
|
||||
}
|
||||
|
||||
rte_vhost_set_vring_base(vsession->vid, i, q->last_avail_idx, q->last_used_idx);
|
||||
}
|
||||
|
||||
vhost_session_mem_unregister(vsession->mem);
|
||||
free(vsession->mem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
vhost_stop_device_cb(int vid)
|
||||
{
|
||||
@ -1507,25 +1419,16 @@ spdk_vhost_unlock(void)
|
||||
void
|
||||
spdk_vhost_init(spdk_vhost_init_cb init_cb)
|
||||
{
|
||||
size_t len;
|
||||
uint32_t i;
|
||||
int ret = 0;
|
||||
|
||||
g_vhost_init_thread = spdk_get_thread();
|
||||
assert(g_vhost_init_thread != NULL);
|
||||
|
||||
if (g_vhost_user_dev_dirname[0] == '\0') {
|
||||
if (getcwd(g_vhost_user_dev_dirname, sizeof(g_vhost_user_dev_dirname) - 1) == NULL) {
|
||||
SPDK_ERRLOG("getcwd failed (%d): %s\n", errno, spdk_strerror(errno));
|
||||
init_cb(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
len = strlen(g_vhost_user_dev_dirname);
|
||||
if (g_vhost_user_dev_dirname[len - 1] != '/') {
|
||||
g_vhost_user_dev_dirname[len] = '/';
|
||||
g_vhost_user_dev_dirname[len + 1] = '\0';
|
||||
}
|
||||
ret = vhost_user_init();
|
||||
if (ret != 0) {
|
||||
init_cb(ret);
|
||||
return;
|
||||
}
|
||||
|
||||
spdk_cpuset_zero(&g_vhost_core_mask);
|
||||
@ -1535,6 +1438,8 @@ spdk_vhost_init(spdk_vhost_init_cb init_cb)
|
||||
init_cb(ret);
|
||||
}
|
||||
|
||||
static spdk_vhost_fini_cb g_fini_cb;
|
||||
|
||||
static void
|
||||
vhost_fini(void *arg1)
|
||||
{
|
||||
@ -1550,52 +1455,17 @@ vhost_fini(void *arg1)
|
||||
}
|
||||
spdk_vhost_unlock();
|
||||
|
||||
g_fini_cpl_cb();
|
||||
}
|
||||
|
||||
static void *
|
||||
session_shutdown(void *arg)
|
||||
{
|
||||
struct spdk_vhost_dev *vdev = NULL;
|
||||
struct spdk_vhost_session *vsession;
|
||||
|
||||
for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL;
|
||||
vdev = spdk_vhost_dev_next(vdev)) {
|
||||
spdk_vhost_lock();
|
||||
TAILQ_FOREACH(vsession, &vdev->vsessions, tailq) {
|
||||
if (vsession->started) {
|
||||
_stop_session(vsession);
|
||||
}
|
||||
}
|
||||
spdk_vhost_unlock();
|
||||
vhost_driver_unregister(vdev->path);
|
||||
vdev->registered = false;
|
||||
}
|
||||
|
||||
SPDK_INFOLOG(vhost, "Exiting\n");
|
||||
spdk_thread_send_msg(g_vhost_init_thread, vhost_fini, NULL);
|
||||
return NULL;
|
||||
g_fini_cb();
|
||||
}
|
||||
|
||||
void
|
||||
spdk_vhost_fini(spdk_vhost_fini_cb fini_cb)
|
||||
{
|
||||
pthread_t tid;
|
||||
int rc;
|
||||
|
||||
assert(spdk_get_thread() == g_vhost_init_thread);
|
||||
g_fini_cpl_cb = fini_cb;
|
||||
|
||||
/* rte_vhost API for removing sockets is not asynchronous. Since it may call SPDK
|
||||
* ops for stopping a device or removing a connection, we need to call it from
|
||||
* a separate thread to avoid deadlock.
|
||||
*/
|
||||
rc = pthread_create(&tid, NULL, &session_shutdown, NULL);
|
||||
if (rc < 0) {
|
||||
SPDK_ERRLOG("Failed to start session shutdown thread (%d): %s\n", rc, spdk_strerror(rc));
|
||||
abort();
|
||||
}
|
||||
pthread_detach(tid);
|
||||
g_fini_cb = fini_cb;
|
||||
|
||||
vhost_user_fini(vhost_fini);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -47,6 +47,9 @@
|
||||
|
||||
extern bool g_packed_ring_recovery;
|
||||
|
||||
/* Thread performing all vhost management operations */
|
||||
extern struct spdk_thread *g_vhost_init_thread;
|
||||
|
||||
/**
|
||||
* DPDK calls our callbacks synchronously but the work those callbacks
|
||||
* perform needs to be async. Luckily, all DPDK callbacks are called on
|
||||
@ -522,5 +525,12 @@ int vhost_user_session_set_coalescing(struct spdk_vhost_dev *vdev,
|
||||
struct spdk_vhost_session *vsession, void *ctx);
|
||||
int vhost_user_dev_set_coalescing(struct spdk_vhost_dev *vdev, uint32_t delay_base_us,
|
||||
uint32_t iops_threshold);
|
||||
int vhost_user_dev_register(struct spdk_vhost_dev *vdev, const char *name,
|
||||
struct spdk_cpuset *cpumask, const struct spdk_vhost_dev_backend *backend);
|
||||
int vhost_user_dev_unregister(struct spdk_vhost_dev *vdev);
|
||||
int vhost_user_init(void);
|
||||
typedef void (*vhost_fini_cb)(void *ctx);
|
||||
void vhost_user_fini(vhost_fini_cb vhost_cb);
|
||||
int _stop_session(struct spdk_vhost_session *vsession);
|
||||
|
||||
#endif /* SPDK_VHOST_INTERNAL_H */
|
||||
|
Loading…
Reference in New Issue
Block a user