Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
vdpa: move CVQ isolation check to net_init_vhost_vdpa
Evaluating it at start time instead of initialization time may make the
guest capable of dynamically adding or removing migration blockers.

Also, moving to initialization reduces the number of ioctls in the
migration, reducing failure possibilities.

As a drawback we need to check for CVQ isolation twice: one time with no
MQ negotiated and another one acking it, as long as the device supports
it.  This is because Vring ASID / group management is based on vq
indexes, but we don't know the index of CVQ before negotiating MQ.

Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
Message-Id: <20230526153143.470745-3-eperezma@redhat.com>
Tested-by: Lei Yang <leiyang@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Jason Wang <jasowang@redhat.com>
  • Loading branch information
eugpermar authored and mstsirkin committed Jun 23, 2023
1 parent 0f2bb0b commit 152128d
Showing 1 changed file with 112 additions and 43 deletions.
155 changes: 112 additions & 43 deletions net/vhost-vdpa.c
Expand Up @@ -43,6 +43,10 @@ typedef struct VhostVDPAState {

/* The device always have SVQ enabled */
bool always_svq;

/* The device can isolate CVQ in its own ASID */
bool cvq_isolated;

bool started;
} VhostVDPAState;

Expand Down Expand Up @@ -362,15 +366,8 @@ static NetClientInfo net_vhost_vdpa_info = {
.check_peer_type = vhost_vdpa_check_peer_type,
};

/**
* Get vring virtqueue group
*
* @device_fd vdpa device fd
* @vq_index Virtqueue index
*
* Return -errno in case of error, or vq group if success.
*/
static int64_t vhost_vdpa_get_vring_group(int device_fd, unsigned vq_index)
static int64_t vhost_vdpa_get_vring_group(int device_fd, unsigned vq_index,
Error **errp)
{
struct vhost_vring_state state = {
.index = vq_index,
Expand All @@ -379,8 +376,7 @@ static int64_t vhost_vdpa_get_vring_group(int device_fd, unsigned vq_index)

if (unlikely(r < 0)) {
r = -errno;
error_report("Cannot get VQ %u group: %s", vq_index,
g_strerror(errno));
error_setg_errno(errp, errno, "Cannot get VQ %u group", vq_index);
return r;
}

Expand Down Expand Up @@ -480,9 +476,9 @@ static int vhost_vdpa_net_cvq_start(NetClientState *nc)
{
VhostVDPAState *s, *s0;
struct vhost_vdpa *v;
uint64_t backend_features;
int64_t cvq_group;
int cvq_index, r;
int r;
Error *err = NULL;

assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);

Expand All @@ -502,41 +498,22 @@ static int vhost_vdpa_net_cvq_start(NetClientState *nc)
/*
* If we early return in these cases SVQ will not be enabled. The migration
* will be blocked as long as vhost-vdpa backends will not offer _F_LOG.
*
* Calling VHOST_GET_BACKEND_FEATURES as they are not available in v->dev
* yet.
*/
r = ioctl(v->device_fd, VHOST_GET_BACKEND_FEATURES, &backend_features);
if (unlikely(r < 0)) {
error_report("Cannot get vdpa backend_features: %s(%d)",
g_strerror(errno), errno);
return -1;
if (!vhost_vdpa_net_valid_svq_features(v->dev->features, NULL)) {
return 0;
}
if (!(backend_features & BIT_ULL(VHOST_BACKEND_F_IOTLB_ASID)) ||
!vhost_vdpa_net_valid_svq_features(v->dev->features, NULL)) {

if (!s->cvq_isolated) {
return 0;
}

/*
* Check if all the virtqueues of the virtio device are in a different vq
* than the last vq. VQ group of last group passed in cvq_group.
*/
cvq_index = v->dev->vq_index_end - 1;
cvq_group = vhost_vdpa_get_vring_group(v->device_fd, cvq_index);
cvq_group = vhost_vdpa_get_vring_group(v->device_fd,
v->dev->vq_index_end - 1,
&err);
if (unlikely(cvq_group < 0)) {
error_report_err(err);
return cvq_group;
}
for (int i = 0; i < cvq_index; ++i) {
int64_t group = vhost_vdpa_get_vring_group(v->device_fd, i);

if (unlikely(group < 0)) {
return group;
}

if (group == cvq_group) {
return 0;
}
}

r = vhost_vdpa_set_address_space_id(v, cvq_group, VHOST_VDPA_NET_CVQ_ASID);
if (unlikely(r < 0)) {
Expand Down Expand Up @@ -799,6 +776,87 @@ static const VhostShadowVirtqueueOps vhost_vdpa_net_svq_ops = {
.avail_handler = vhost_vdpa_net_handle_ctrl_avail,
};

/**
* Probe if CVQ is isolated
*
* @device_fd The vdpa device fd
* @features Features offered by the device.
* @cvq_index The control vq pair index
*
* Returns <0 in case of failure, 0 if false and 1 if true.
*/
static int vhost_vdpa_probe_cvq_isolation(int device_fd, uint64_t features,
int cvq_index, Error **errp)
{
uint64_t backend_features;
int64_t cvq_group;
uint8_t status = VIRTIO_CONFIG_S_ACKNOWLEDGE |
VIRTIO_CONFIG_S_DRIVER |
VIRTIO_CONFIG_S_FEATURES_OK;
int r;

ERRP_GUARD();

r = ioctl(device_fd, VHOST_GET_BACKEND_FEATURES, &backend_features);
if (unlikely(r < 0)) {
error_setg_errno(errp, errno, "Cannot get vdpa backend_features");
return r;
}

if (!(backend_features & BIT_ULL(VHOST_BACKEND_F_IOTLB_ASID))) {
return 0;
}

r = ioctl(device_fd, VHOST_SET_FEATURES, &features);
if (unlikely(r)) {
error_setg_errno(errp, errno, "Cannot set features");
}

r = ioctl(device_fd, VHOST_VDPA_SET_STATUS, &status);
if (unlikely(r)) {
error_setg_errno(errp, -r, "Cannot set device features");
goto out;
}

cvq_group = vhost_vdpa_get_vring_group(device_fd, cvq_index, errp);
if (unlikely(cvq_group < 0)) {
if (cvq_group != -ENOTSUP) {
r = cvq_group;
goto out;
}

/*
* The kernel report VHOST_BACKEND_F_IOTLB_ASID if the vdpa frontend
* support ASID even if the parent driver does not. The CVQ cannot be
* isolated in this case.
*/
error_free(*errp);
*errp = NULL;
r = 0;
goto out;
}

for (int i = 0; i < cvq_index; ++i) {
int64_t group = vhost_vdpa_get_vring_group(device_fd, i, errp);
if (unlikely(group < 0)) {
r = group;
goto out;
}

if (group == (int64_t)cvq_group) {
r = 0;
goto out;
}
}

r = 1;

out:
status = 0;
ioctl(device_fd, VHOST_VDPA_SET_STATUS, &status);
return r;
}

static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
const char *device,
const char *name,
Expand All @@ -808,16 +866,26 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
bool is_datapath,
bool svq,
struct vhost_vdpa_iova_range iova_range,
uint64_t features)
uint64_t features,
Error **errp)
{
NetClientState *nc = NULL;
VhostVDPAState *s;
int ret = 0;
assert(name);
int cvq_isolated;

if (is_datapath) {
nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device,
name);
} else {
cvq_isolated = vhost_vdpa_probe_cvq_isolation(vdpa_device_fd, features,
queue_pair_index * 2,
errp);
if (unlikely(cvq_isolated < 0)) {
return NULL;
}

nc = qemu_new_net_control_client(&net_vhost_vdpa_cvq_info, peer,
device, name);
}
Expand All @@ -844,6 +912,7 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,

s->vhost_vdpa.shadow_vq_ops = &vhost_vdpa_net_svq_ops;
s->vhost_vdpa.shadow_vq_ops_opaque = s;
s->cvq_isolated = cvq_isolated;

/*
* TODO: We cannot migrate devices with CVQ as there is no way to set
Expand Down Expand Up @@ -972,15 +1041,15 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
for (i = 0; i < queue_pairs; i++) {
ncs[i] = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name,
vdpa_device_fd, i, 2, true, opts->x_svq,
iova_range, features);
iova_range, features, errp);
if (!ncs[i])
goto err;
}

if (has_cvq) {
nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name,
vdpa_device_fd, i, 1, false,
opts->x_svq, iova_range, features);
opts->x_svq, iova_range, features, errp);
if (!nc)
goto err;
}
Expand Down

0 comments on commit 152128d

Please sign in to comment.