Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost

Pull vhost,vdpa,virtio updates from Michael Tsirkin:

 - transport v3 support in virtio-mmio

 - suspend support in vduse

 - fixes, cleanups all over the place

* tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost: (54 commits)
  vduse: Add suspend
  vduse: do not take rwsem at reset work flush
  vduse: add F_QUEUE_READY feature
  vduse: add VDUSE_SET_FEATURES ioctl
  vduse: add VDUSE_GET_FEATURES ioctl
  vduse: store control device pointer
  tools/virtio: Fix control typo in trace agent comment
  tools/virtio: Fix userspace typo in vringh test comment
  vhost: reject zero-size IOTLB INVALIDATE
  vdpa: Remove redundant dev_err()
  virtio_ring: fix infinite loop in virtnet_poll_cleantx when device is broken
  vdpa/mlx5: roll back MR update after VQ setup failure
  MAINTAINERS: remove Gabriel from LiteX and fw-cfg drivers
  virtio_mem: fix typo in comment
  vdpa/solidrun: fix typos in snet_ctrl comments
  virtio: fix article before virtio in dma-buf comment
  vhost: fix inaccurate kdoc in iotlb helpers
  virtio: rtc: time out alarm requests
  vdpa/mlx5: fix wrong MLX5_ADDR_OF struct type in alloc_inout()
  vdpa: octeon_ep: add missing MODULE_DEVICE_TABLE()
  ...
This commit is contained in:
Linus Torvalds
2026-08-20 17:07:50 -07:00
37 changed files with 760 additions and 222 deletions

View File

@@ -15032,7 +15032,6 @@ F: lib/tests/list-test.c
LITEX PLATFORM
M: Karol Gugala <kgugala@antmicro.com>
M: Mateusz Holenko <mholenko@antmicro.com>
M: Gabriel Somlo <gsomlo@gmail.com>
M: Joel Stanley <joel@jms.id.au>
S: Maintained
F: Documentation/devicetree/bindings/*/litex,*.yaml
@@ -21987,7 +21986,6 @@ S: Maintained
F: drivers/net/ipa/
QEMU MACHINE EMULATOR AND VIRTUALIZER SUPPORT
M: Gabriel Somlo <somlo@cmu.edu>
M: "Michael S. Tsirkin" <mst@redhat.com>
L: qemu-devel@nongnu.org
S: Maintained

View File

@@ -88,7 +88,8 @@ static void virtio_crypto_dataq_akcipher_callback(struct virtio_crypto_request *
}
/* actual length may be less than dst buffer */
akcipher_req->dst_len = len - sizeof(vc_req->status);
akcipher_req->dst_len = min_t(unsigned int, len - sizeof(vc_req->status),
akcipher_req->dst_len);
sg_copy_from_buffer(akcipher_req->dst, sg_nents(akcipher_req->dst),
vc_akcipher_req->dst_buf, akcipher_req->dst_len);
virtio_crypto_akcipher_finalize_req(vc_akcipher_req, akcipher_req, error);
@@ -194,7 +195,8 @@ static int virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
if (ctrl_status->status != VIRTIO_CRYPTO_OK) {
pr_err("virtio_crypto: Close session failed status: %u, session_id: 0x%llx\n",
ctrl_status->status, destroy_session->session_id);
ctrl_status->status,
le64_to_cpu(destroy_session->session_id));
err = -EINVAL;
goto out;
}

View File

@@ -232,7 +232,8 @@ static int virtio_crypto_alg_skcipher_close_session(
if (ctrl_status->status != VIRTIO_CRYPTO_OK) {
pr_err("virtio_crypto: Close session failed status: %u, session_id: 0x%llx\n",
ctrl_status->status, destroy_session->session_id);
ctrl_status->status,
le64_to_cpu(destroy_session->session_id));
err = -EINVAL;
goto out;

View File

@@ -9,26 +9,130 @@
#include "virtio_pmem.h"
#include "nd.h"
struct virtio_pmem_flush_work {
struct work_struct work;
struct nd_region *nd_region;
struct bio *bio;
};
static void virtio_pmem_req_release(struct kref *kref)
{
struct virtio_pmem_request *req;
req = container_of(kref, struct virtio_pmem_request, kref);
kfree(req);
}
static void virtio_pmem_signal_done(struct virtio_pmem_request *req)
{
/* Pairs with smp_load_acquire() in virtio_pmem_req_done(). */
smp_store_release(&req->done, true);
wake_up(&req->host_acked);
}
static bool virtio_pmem_req_done(struct virtio_pmem_request *req)
{
/* Pairs with smp_store_release() in virtio_pmem_signal_done(). */
return smp_load_acquire(&req->done);
}
static void virtio_pmem_complete_err(struct virtio_pmem_request *req)
{
req->resp.ret = cpu_to_le32(1);
virtio_pmem_signal_done(req);
}
static void virtio_pmem_wake_one_waiter(struct virtio_pmem *vpmem)
{
struct virtio_pmem_request *req_buf;
if (list_empty(&vpmem->req_list))
return;
req_buf = list_first_entry(&vpmem->req_list,
struct virtio_pmem_request, list);
list_del_init(&req_buf->list);
WRITE_ONCE(req_buf->wq_buf_avail, true);
wake_up(&req_buf->wq_buf);
}
static void virtio_pmem_wake_all_waiters(struct virtio_pmem *vpmem)
{
struct virtio_pmem_request *req, *tmp;
list_for_each_entry_safe(req, tmp, &vpmem->req_list, list) {
list_del_init(&req->list);
WRITE_ONCE(req->wq_buf_avail, true);
wake_up(&req->wq_buf);
}
}
static void virtio_pmem_clear_inflight(struct virtio_pmem *vpmem,
struct virtio_pmem_request *req)
{
if (vpmem->req_inflight == req)
vpmem->req_inflight = NULL;
}
static void virtio_pmem_wake_inflight(struct virtio_pmem *vpmem)
{
struct virtio_pmem_request *req = vpmem->req_inflight;
if (req)
wake_up(&req->host_acked);
}
void virtio_pmem_mark_broken(struct virtio_pmem *vpmem)
{
if (!READ_ONCE(vpmem->broken)) {
WRITE_ONCE(vpmem->broken, true);
dev_err_once(&vpmem->vdev->dev, "virtqueue is broken\n");
}
virtio_pmem_wake_inflight(vpmem);
virtio_pmem_wake_all_waiters(vpmem);
}
EXPORT_SYMBOL_GPL(virtio_pmem_mark_broken);
void virtio_pmem_drain(struct virtio_pmem *vpmem)
{
struct virtio_pmem_request *req;
unsigned int len;
if (!vpmem->req_vq)
return;
while ((req = virtqueue_get_buf(vpmem->req_vq, &len)) != NULL) {
virtio_pmem_clear_inflight(vpmem, req);
virtio_pmem_complete_err(req);
kref_put(&req->kref, virtio_pmem_req_release);
}
while ((req = virtqueue_detach_unused_buf(vpmem->req_vq)) != NULL) {
virtio_pmem_clear_inflight(vpmem, req);
virtio_pmem_complete_err(req);
kref_put(&req->kref, virtio_pmem_req_release);
}
}
EXPORT_SYMBOL_GPL(virtio_pmem_drain);
/* The interrupt handler */
void virtio_pmem_host_ack(struct virtqueue *vq)
{
struct virtio_pmem *vpmem = vq->vdev->priv;
struct virtio_pmem_request *req_data, *req_buf;
struct virtio_pmem_request *req_data;
unsigned long flags;
unsigned int len;
spin_lock_irqsave(&vpmem->pmem_lock, flags);
while ((req_data = virtqueue_get_buf(vq, &len)) != NULL) {
req_data->done = true;
wake_up(&req_data->host_acked);
if (!list_empty(&vpmem->req_list)) {
req_buf = list_first_entry(&vpmem->req_list,
struct virtio_pmem_request, list);
req_buf->wq_buf_avail = true;
wake_up(&req_buf->wq_buf);
list_del(&req_buf->list);
}
virtio_pmem_clear_inflight(vpmem, req_data);
virtio_pmem_wake_one_waiter(vpmem);
if (READ_ONCE(vpmem->broken))
virtio_pmem_complete_err(req_data);
else
virtio_pmem_signal_done(req_data);
kref_put(&req_data->kref, virtio_pmem_req_release);
}
spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
}
@@ -55,11 +159,15 @@ static int virtio_pmem_flush(struct nd_region *nd_region)
return -EIO;
}
req_data = kmalloc_obj(*req_data);
if (READ_ONCE(vpmem->broken))
return -EIO;
req_data = kmalloc_obj(*req_data, GFP_NOIO);
if (!req_data)
return -ENOMEM;
req_data->done = false;
kref_init(&req_data->kref);
WRITE_ONCE(req_data->done, false);
init_waitqueue_head(&req_data->host_acked);
init_waitqueue_head(&req_data->wq_buf);
INIT_LIST_HEAD(&req_data->list);
@@ -70,67 +178,132 @@ static int virtio_pmem_flush(struct nd_region *nd_region)
sgs[1] = &ret;
spin_lock_irqsave(&vpmem->pmem_lock, flags);
/*
* If virtqueue_add_sgs returns -ENOSPC then req_vq virtual
* queue does not have free descriptor. We add the request
* to req_list and wait for host_ack to wake us up when free
* slots are available.
*/
while ((err = virtqueue_add_sgs(vpmem->req_vq, sgs, 1, 1, req_data,
GFP_ATOMIC)) == -ENOSPC) {
/*
* If virtqueue_add_sgs returns -ENOSPC then req_vq virtual
* queue does not have free descriptor. We add the request
* to req_list and wait for host_ack to wake us up when free
* slots are available.
*/
for (;;) {
if (READ_ONCE(vpmem->broken)) {
err = -EIO;
break;
}
dev_info(&vdev->dev, "failed to send command to virtio pmem device, no free slots in the virtqueue\n");
req_data->wq_buf_avail = false;
err = virtqueue_add_sgs(vpmem->req_vq, sgs, 1, 1, req_data,
GFP_ATOMIC);
if (!err) {
/*
* Take the virtqueue reference while @pmem_lock is
* held so completion cannot run concurrently.
*/
kref_get(&req_data->kref);
vpmem->req_inflight = req_data;
break;
}
if (err != -ENOSPC)
break;
dev_info_ratelimited(&vdev->dev,
"failed to send command to virtio pmem device, no free slots in the virtqueue\n");
WRITE_ONCE(req_data->wq_buf_avail, false);
list_add_tail(&req_data->list, &vpmem->req_list);
spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
/* A host response results in "host_ack" getting called */
wait_event(req_data->wq_buf, req_data->wq_buf_avail);
wait_event(req_data->wq_buf,
READ_ONCE(req_data->wq_buf_avail) ||
READ_ONCE(vpmem->broken));
spin_lock_irqsave(&vpmem->pmem_lock, flags);
if (READ_ONCE(vpmem->broken))
break;
}
if (READ_ONCE(vpmem->broken))
err = -EIO;
if (err == -EIO || virtqueue_is_broken(vpmem->req_vq))
virtio_pmem_mark_broken(vpmem);
err1 = true;
if (!err && !READ_ONCE(vpmem->broken)) {
err1 = virtqueue_kick(vpmem->req_vq);
if (!err1)
virtio_pmem_mark_broken(vpmem);
}
err1 = virtqueue_kick(vpmem->req_vq);
spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
/*
* virtqueue_add_sgs failed with error different than -ENOSPC, we can't
* do anything about that.
*/
if (err || !err1) {
if (READ_ONCE(vpmem->broken) || err || !err1) {
dev_info(&vdev->dev, "failed to send command to virtio pmem device\n");
err = -EIO;
} else {
/* A host response results in "host_ack" getting called */
wait_event(req_data->host_acked, req_data->done);
err = le32_to_cpu(req_data->resp.ret);
wait_event(req_data->host_acked,
virtio_pmem_req_done(req_data) ||
READ_ONCE(vpmem->broken));
if (virtio_pmem_req_done(req_data))
err = le32_to_cpu(req_data->resp.ret);
else
err = -EIO;
}
kfree(req_data);
kref_put(&req_data->kref, virtio_pmem_req_release);
return err;
};
static void virtio_pmem_flush_work(struct work_struct *work)
{
struct virtio_pmem_flush_work *flush;
int err;
flush = container_of(work, struct virtio_pmem_flush_work, work);
err = virtio_pmem_flush(flush->nd_region);
if (err > 0)
err = -EIO;
if (err)
flush->bio->bi_status = errno_to_blk_status(err);
bio_endio(flush->bio);
kfree(flush);
}
/* The asynchronous flush callback function */
int async_pmem_flush(struct nd_region *nd_region, struct bio *bio)
{
/*
* Create child bio for asynchronous flush and chain with
* parent bio. Otherwise directly call nd_region flush.
*/
if (bio && bio->bi_iter.bi_sector != -1) {
struct bio *child = bio_alloc(bio->bi_bdev, 0,
REQ_OP_WRITE | REQ_PREFLUSH,
GFP_ATOMIC);
struct virtio_device *vdev = nd_region->provider_data;
struct virtio_pmem *vpmem = vdev->priv;
struct virtio_pmem_flush_work *flush;
unsigned long flags;
int err;
if (!child)
if (bio && bio->bi_iter.bi_sector != -1) {
flush = kmalloc_obj(*flush, GFP_NOIO);
if (!flush)
return -ENOMEM;
bio_clone_blkg_association(child, bio);
child->bi_iter.bi_sector = -1;
bio_chain(child, bio);
submit_bio(child);
return 0;
INIT_WORK(&flush->work, virtio_pmem_flush_work);
flush->nd_region = nd_region;
flush->bio = bio;
spin_lock_irqsave(&vpmem->pmem_lock, flags);
if (READ_ONCE(vpmem->broken)) {
spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
kfree(flush);
return -EIO;
}
queue_work(vpmem->flush_wq, &flush->work);
spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
return NVDIMM_FLUSH_ASYNC;
}
if (virtio_pmem_flush(nd_region))
err = virtio_pmem_flush(nd_region);
if (err > 0)
return -EIO;
return 0;
return err;
};
EXPORT_SYMBOL_GPL(async_pmem_flush);
MODULE_DESCRIPTION("Virtio Persistent Memory Driver");

View File

@@ -208,29 +208,44 @@ static void pmem_submit_bio(struct bio *bio)
struct pmem_device *pmem = bio->bi_bdev->bd_disk->private_data;
struct nd_region *nd_region = to_region(pmem);
if (bio->bi_opf & REQ_PREFLUSH)
ret = nvdimm_flush(nd_region, bio);
do_acct = blk_queue_io_stat(bio->bi_bdev->bd_disk->queue);
if (do_acct)
start = bio_start_io_acct(bio);
bio_for_each_segment(bvec, bio, iter) {
if (op_is_write(bio_op(bio)))
rc = pmem_do_write(pmem, bvec.bv_page, bvec.bv_offset,
iter.bi_sector, bvec.bv_len);
else
rc = pmem_do_read(pmem, bvec.bv_page, bvec.bv_offset,
iter.bi_sector, bvec.bv_len);
if (rc) {
bio->bi_status = rc;
break;
if (bio->bi_opf & REQ_PREFLUSH) {
ret = nvdimm_flush(nd_region, NULL);
if (ret) {
bio->bi_status = errno_to_blk_status(ret);
bio_endio(bio);
return;
}
}
if (do_acct)
bio_end_io_acct(bio, start);
if (bio->bi_opf & REQ_FUA)
if (bio_has_data(bio)) {
do_acct = blk_queue_io_stat(bio->bi_bdev->bd_disk->queue);
if (do_acct)
start = bio_start_io_acct(bio);
bio_for_each_segment(bvec, bio, iter) {
if (op_is_write(bio_op(bio)))
rc = pmem_do_write(pmem, bvec.bv_page,
bvec.bv_offset,
iter.bi_sector,
bvec.bv_len);
else
rc = pmem_do_read(pmem, bvec.bv_page,
bvec.bv_offset,
iter.bi_sector,
bvec.bv_len);
if (rc) {
bio->bi_status = rc;
break;
}
}
if (do_acct)
bio_end_io_acct(bio, start);
}
if ((bio->bi_opf & REQ_FUA) && !bio->bi_status) {
ret = nvdimm_flush(nd_region, bio);
if (ret == NVDIMM_FLUSH_ASYNC)
return;
}
if (ret)
bio->bi_status = errno_to_blk_status(ret);

View File

@@ -1093,7 +1093,10 @@ int nvdimm_flush(struct nd_region *nd_region, struct bio *bio)
if (!nd_region->flush)
rc = generic_nvdimm_flush(nd_region);
else {
if (nd_region->flush(nd_region, bio))
rc = nd_region->flush(nd_region, bio);
if (rc > 0)
return rc;
if (rc && rc != -ENOMEM)
rc = -EIO;
}

View File

@@ -17,18 +17,34 @@ static struct virtio_device_id id_table[] = {
/* Initialize virt queue */
static int init_vq(struct virtio_pmem *vpmem)
{
int err;
/* single vq */
vpmem->req_vq = virtio_find_single_vq(vpmem->vdev,
virtio_pmem_host_ack, "flush_queue");
if (IS_ERR(vpmem->req_vq))
return PTR_ERR(vpmem->req_vq);
if (IS_ERR(vpmem->req_vq)) {
err = PTR_ERR(vpmem->req_vq);
vpmem->req_vq = NULL;
return err;
}
spin_lock_init(&vpmem->pmem_lock);
INIT_LIST_HEAD(&vpmem->req_list);
vpmem->req_inflight = NULL;
WRITE_ONCE(vpmem->broken, false);
return 0;
};
static void virtio_pmem_del_vqs(struct virtio_pmem *vpmem)
{
if (!vpmem->req_vq)
return;
vpmem->vdev->config->del_vqs(vpmem->vdev);
vpmem->req_vq = NULL;
}
static int virtio_pmem_validate(struct virtio_device *vdev)
{
struct virtio_shm_region shm_reg;
@@ -67,10 +83,17 @@ static int virtio_pmem_probe(struct virtio_device *vdev)
mutex_init(&vpmem->flush_lock);
vpmem->vdev = vdev;
vdev->priv = vpmem;
vpmem->flush_wq = alloc_ordered_workqueue("virtio-pmem-flush",
WQ_MEM_RECLAIM);
if (!vpmem->flush_wq) {
err = -ENOMEM;
goto out_err;
}
err = init_vq(vpmem);
if (err) {
dev_err(&vdev->dev, "failed to initialize virtio pmem vq's\n");
goto out_err;
goto out_wq;
}
if (virtio_has_feature(vdev, VIRTIO_PMEM_F_SHMEM_REGION)) {
@@ -130,7 +153,9 @@ static int virtio_pmem_probe(struct virtio_device *vdev)
virtio_reset_device(vdev);
nvdimm_bus_unregister(vpmem->nvdimm_bus);
out_vq:
vdev->config->del_vqs(vdev);
virtio_pmem_del_vqs(vpmem);
out_wq:
destroy_workqueue(vpmem->flush_wq);
out_err:
return err;
}
@@ -138,17 +163,43 @@ static int virtio_pmem_probe(struct virtio_device *vdev)
static void virtio_pmem_remove(struct virtio_device *vdev)
{
struct nvdimm_bus *nvdimm_bus = dev_get_drvdata(&vdev->dev);
struct virtio_pmem *vpmem = vdev->priv;
unsigned long flags;
spin_lock_irqsave(&vpmem->pmem_lock, flags);
virtio_pmem_mark_broken(vpmem);
spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
drain_workqueue(vpmem->flush_wq);
virtio_reset_device(vdev);
spin_lock_irqsave(&vpmem->pmem_lock, flags);
virtio_pmem_drain(vpmem);
spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
nvdimm_bus_unregister(nvdimm_bus);
vdev->config->del_vqs(vdev);
virtio_reset_device(vdev);
virtio_pmem_del_vqs(vpmem);
destroy_workqueue(vpmem->flush_wq);
}
static int virtio_pmem_freeze(struct virtio_device *vdev)
{
vdev->config->del_vqs(vdev);
struct virtio_pmem *vpmem = vdev->priv;
unsigned long flags;
spin_lock_irqsave(&vpmem->pmem_lock, flags);
virtio_pmem_mark_broken(vpmem);
spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
drain_workqueue(vpmem->flush_wq);
virtio_reset_device(vdev);
spin_lock_irqsave(&vpmem->pmem_lock, flags);
virtio_pmem_drain(vpmem);
spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
virtio_pmem_del_vqs(vpmem);
return 0;
}

View File

@@ -10,15 +10,17 @@
#ifndef _LINUX_VIRTIO_PMEM_H
#define _LINUX_VIRTIO_PMEM_H
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <uapi/linux/virtio_pmem.h>
#include <linux/kref.h>
#include <linux/libnvdimm.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
struct virtio_pmem_request {
struct virtio_pmem_req req;
struct virtio_pmem_resp resp;
struct kref kref;
/* Wait queue to process deferred work after ack from host */
wait_queue_head_t host_acked;
@@ -28,6 +30,11 @@ struct virtio_pmem_request {
wait_queue_head_t wq_buf;
bool wq_buf_avail;
struct list_head list;
struct virtio_pmem_req req;
__dma_from_device_group_begin(resp);
struct virtio_pmem_resp resp;
__dma_from_device_group_end(resp);
};
struct virtio_pmem {
@@ -39,6 +46,9 @@ struct virtio_pmem {
/* Serialize flush requests to the device. */
struct mutex flush_lock;
/* Complete asynchronous FUA flushes outside the submit path. */
struct workqueue_struct *flush_wq;
/* nvdimm bus registers virtio pmem device */
struct nvdimm_bus *nvdimm_bus;
struct nvdimm_bus_descriptor nd_desc;
@@ -46,6 +56,12 @@ struct virtio_pmem {
/* List to store deferred work if virtqueue is full */
struct list_head req_list;
/* Request currently owned by the virtqueue. */
struct virtio_pmem_request *req_inflight;
/* Fail fast and wake waiters if the request virtqueue is broken. */
bool broken;
/* Synchronize virtqueue data */
spinlock_t pmem_lock;
@@ -55,5 +71,7 @@ struct virtio_pmem {
};
void virtio_pmem_host_ack(struct virtqueue *vq);
void virtio_pmem_mark_broken(struct virtio_pmem *vpmem);
void virtio_pmem_drain(struct virtio_pmem *vpmem);
int async_pmem_flush(struct nd_region *nd_region, struct bio *bio);
#endif

View File

@@ -545,6 +545,7 @@ static struct pci_device_id eni_pci_ids[] = {
VIRTIO_ID_NET) },
{ 0 },
};
MODULE_DEVICE_TABLE(pci, eni_pci_ids);
static struct pci_driver eni_vdpa_driver = {
.name = "alibaba-eni-vdpa",

View File

@@ -481,7 +481,7 @@ static int add_direct_chain(struct mlx5_vdpa_dev *mvdev,
return 0;
err_alloc:
list_for_each_entry_safe(dmr, n, &mr->head, list) {
list_for_each_entry_safe(dmr, n, &tmp, list) {
list_del_init(&dmr->list);
unmap_direct_mr(mvdev, dmr);
kfree(dmr);

View File

@@ -1080,7 +1080,7 @@ static void alloc_inout(struct mlx5_vdpa_net *ndev, int cmd, void **in, int *inl
MLX5_SET(init2rtr_qp_in, *in, opcode, cmd);
MLX5_SET(init2rtr_qp_in, *in, uid, ndev->mvdev.res.uid);
MLX5_SET(init2rtr_qp_in, *in, qpn, qpn);
qpc = MLX5_ADDR_OF(rst2init_qp_in, *in, qpc);
qpc = MLX5_ADDR_OF(init2rtr_qp_in, *in, qpc);
MLX5_SET(qpc, qpc, mtu, MLX5_QPC_MTU_256_BYTES);
MLX5_SET(qpc, qpc, log_msg_max, 30);
MLX5_SET(qpc, qpc, remote_qpn, rqpn);
@@ -1098,7 +1098,7 @@ static void alloc_inout(struct mlx5_vdpa_net *ndev, int cmd, void **in, int *inl
MLX5_SET(rtr2rts_qp_in, *in, opcode, cmd);
MLX5_SET(rtr2rts_qp_in, *in, uid, ndev->mvdev.res.uid);
MLX5_SET(rtr2rts_qp_in, *in, qpn, qpn);
qpc = MLX5_ADDR_OF(rst2init_qp_in, *in, qpc);
qpc = MLX5_ADDR_OF(rtr2rts_qp_in, *in, qpc);
pp = MLX5_ADDR_OF(qpc, qpc, primary_address_path);
MLX5_SET(ads, pp, ack_timeout, 14);
MLX5_SET(qpc, qpc, retry_count, 7);
@@ -3055,18 +3055,24 @@ static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev,
unsigned int asid)
{
struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
struct mlx5_vdpa_mr *old_mr;
bool teardown = !is_resumable(ndev);
int err;
suspend_vqs(ndev, 0, ndev->cur_num_vqs);
if (teardown) {
err = save_channels_info(ndev);
if (err)
if (err) {
mlx5_vdpa_put_mr(mvdev, new_mr);
return err;
}
teardown_vq_resources(ndev);
}
/* Keep the old MR alive in case rebuilding the VQs fails. */
old_mr = mvdev->mres.mr[asid];
mlx5_vdpa_get_mr(mvdev, old_mr);
mlx5_vdpa_update_mr(mvdev, new_mr, asid);
for (int i = 0; i < mvdev->max_vqs; i++)
@@ -3074,17 +3080,22 @@ static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev,
MLX5_VIRTQ_MODIFY_MASK_DESC_GROUP_MKEY;
if (!(mvdev->status & VIRTIO_CONFIG_S_DRIVER_OK) || mvdev->suspended)
return 0;
goto out;
if (teardown) {
restore_channels_info(ndev);
err = setup_vq_resources(ndev, true);
if (err)
if (err) {
/* The saved reference becomes the restored map reference. */
mlx5_vdpa_update_mr(mvdev, old_mr, asid);
return err;
}
}
resume_vqs(ndev, 0, ndev->cur_num_vqs);
out:
mlx5_vdpa_put_mr(mvdev, old_mr);
return 0;
}
@@ -3368,15 +3379,11 @@ static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
err = mlx5_vdpa_change_map(mvdev, new_mr, asid);
if (err) {
mlx5_vdpa_err(mvdev, "change map failed(%d)\n", err);
goto out_err;
return err;
}
}
return mlx5_vdpa_update_cvq_iotlb(mvdev, iotlb, asid);
out_err:
mlx5_vdpa_put_mr(mvdev, new_mr);
return err;
}
static int mlx5_vdpa_set_map(struct vdpa_device *vdev, unsigned int asid,

View File

@@ -170,10 +170,8 @@ static int octep_request_irqs(struct octep_hw *oct_hw, irqreturn_t (*irq_handler
irq = pci_irq_vector(pdev, idx);
ret = devm_request_irq(&pdev->dev, irq, irq_handler, 0, dev_name(&pdev->dev),
oct_hw);
if (ret) {
dev_err(&pdev->dev, "Failed to register interrupt handler\n");
if (ret)
goto free_irqs;
}
oct_hw->irqs[idx] = irq;
}
oct_hw->requested_irqs = nb_irqs;
@@ -979,6 +977,7 @@ static struct pci_device_id octep_pci_vdpa_map[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_VDPA_DEVID_CN103K_VF) },
{ 0 },
};
MODULE_DEVICE_TABLE(pci, octep_pci_vdpa_map);
static struct pci_driver octep_pci_vdpa = {
.name = OCTEP_VDPA_DRIVER_NAME,

View File

@@ -124,10 +124,10 @@ static int snet_wait_for_dpu_completion(struct snet_ctrl_regs __iomem *ctrl_regs
* reading the in_process and error bits in the control register.
* (2) Write the request opcode and the VQ idx in the opcode register
* and write the buffer size in the control register.
* (3) Start readind chunks of data, chunk_ready bit indicates that a
* (3) Start reading chunks of data, chunk_ready bit indicates that a
* data chunk is available, we signal that we read the data by clearing the bit.
* (4) Detect that the transfer is completed when the in_process bit
* in the control register is cleared or when the an error appears.
* in the control register is cleared or when an error appears.
*/
static int snet_ctrl_read_from_dpu(struct snet *snet, u16 opcode, u16 vq_idx, void *buffer,
u32 buf_size)

View File

@@ -161,6 +161,7 @@ static void vdpasim_do_reset(struct vdpasim *vdpasim, u32 flags)
}
vdpasim->running = false;
vdpasim->pending_kick = false;
spin_unlock(&vdpasim->iommu_lock);
vdpasim->features = 0;
@@ -233,8 +234,11 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr,
kthread_init_work(&vdpasim->work, vdpasim_work_fn);
vdpasim->worker = kthread_run_worker(0, "vDPA sim worker: %s",
dev_attr->name);
if (IS_ERR(vdpasim->worker))
if (IS_ERR(vdpasim->worker)) {
ret = PTR_ERR(vdpasim->worker);
vdpasim->worker = NULL;
goto err_iommu;
}
mutex_init(&vdpasim->mutex);
spin_lock_init(&vdpasim->iommu_lock);
@@ -729,12 +733,11 @@ static int vdpasim_dma_unmap(struct vdpa_device *vdpa, unsigned int asid,
if (asid >= vdpasim->dev_attr.nas)
return -EINVAL;
spin_lock(&vdpasim->iommu_lock);
if (vdpasim->iommu_pt[asid]) {
vhost_iotlb_reset(&vdpasim->iommu[asid]);
vdpasim->iommu_pt[asid] = false;
}
spin_lock(&vdpasim->iommu_lock);
vhost_iotlb_del_range(&vdpasim->iommu[asid], iova, iova + size - 1);
spin_unlock(&vdpasim->iommu_lock);
@@ -746,18 +749,24 @@ static void vdpasim_free(struct vdpa_device *vdpa)
struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
int i;
kthread_cancel_work_sync(&vdpasim->work);
kthread_destroy_worker(vdpasim->worker);
if (vdpasim->worker) {
kthread_cancel_work_sync(&vdpasim->work);
kthread_destroy_worker(vdpasim->worker);
}
for (i = 0; i < vdpasim->dev_attr.nvqs; i++) {
vringh_kiov_cleanup(&vdpasim->vqs[i].out_iov);
vringh_kiov_cleanup(&vdpasim->vqs[i].in_iov);
if (vdpasim->vqs) {
for (i = 0; i < vdpasim->dev_attr.nvqs; i++) {
vringh_kiov_cleanup(&vdpasim->vqs[i].out_iov);
vringh_kiov_cleanup(&vdpasim->vqs[i].in_iov);
}
}
vdpasim->dev_attr.free(vdpasim);
for (i = 0; i < vdpasim->dev_attr.nas; i++)
vhost_iotlb_reset(&vdpasim->iommu[i]);
if (vdpasim->iommu) {
for (i = 0; i < vdpasim->dev_attr.nas; i++)
vhost_iotlb_reset(&vdpasim->iommu[i]);
}
kfree(vdpasim->iommu);
kfree(vdpasim->iommu_pt);
kfree(vdpasim->vqs);

View File

@@ -9,6 +9,7 @@
*/
#include "linux/virtio_net.h"
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -51,6 +52,10 @@
#define IRQ_UNBOUND -1
/* Supported VDUSE features */
static const uint64_t vduse_features = BIT_U64(VDUSE_F_QUEUE_READY) |
BIT_U64(VDUSE_F_SUSPEND);
/*
* VDUSE instance have not asked the vduse API version, so assume 0.
*
@@ -73,6 +78,7 @@ struct vduse_virtqueue {
u32 group;
spinlock_t kick_lock;
spinlock_t irq_lock;
spinlock_t ready_lock;
struct eventfd_ctx *kickfd;
struct vdpa_callback cb;
struct work_struct inject;
@@ -80,6 +86,7 @@ struct vduse_virtqueue {
int irq_effective_cpu;
struct cpumask irq_affinity;
struct kobject kobj;
struct vduse_dev *dev;
};
struct vduse_dev;
@@ -116,6 +123,7 @@ struct vduse_dev {
char *name;
struct mutex lock;
spinlock_t msg_lock;
u64 vduse_features;
u64 msg_unique;
u32 msg_timeout;
wait_queue_head_t waitq;
@@ -128,6 +136,7 @@ struct vduse_dev {
int minor;
bool broken;
bool connected;
bool suspended;
u64 api_version;
u64 device_features;
u64 driver_features;
@@ -156,6 +165,7 @@ struct vduse_dev_msg {
struct vduse_control {
u64 api_version;
u64 vduse_features;
};
static DEFINE_MUTEX(vduse_lock);
@@ -163,6 +173,7 @@ static DEFINE_IDR(vduse_idr);
static dev_t vduse_major;
static struct cdev vduse_ctrl_cdev;
static const struct device *vduse_ctrl_dev;
static struct cdev vduse_cdev;
static struct workqueue_struct *vduse_irq_wq;
static struct workqueue_struct *vduse_irq_bound_wq;
@@ -494,44 +505,50 @@ static void vduse_dev_reset(struct vduse_dev *dev)
vduse_domain_reset_bounce_map(domain);
}
down_write(&dev->rwsem);
scoped_guard(rwsem_write, &dev->rwsem) {
dev->suspended = false;
dev->status = 0;
dev->driver_features = 0;
dev->generation++;
spin_lock(&dev->irq_lock);
dev->config_cb.callback = NULL;
dev->config_cb.private = NULL;
spin_unlock(&dev->irq_lock);
for (i = 0; i < dev->vq_num; i++) {
struct vduse_virtqueue *vq = dev->vqs[i];
scoped_guard(spinlock_bh, &vq->ready_lock) {
vq->ready = false;
}
vq->desc_addr = 0;
vq->driver_addr = 0;
vq->device_addr = 0;
vq->num = 0;
memset(&vq->state, 0, sizeof(vq->state));
spin_lock(&vq->kick_lock);
vq->kicked = false;
if (vq->kickfd)
eventfd_ctx_put(vq->kickfd);
vq->kickfd = NULL;
spin_unlock(&vq->kick_lock);
spin_lock(&vq->irq_lock);
vq->cb.callback = NULL;
vq->cb.private = NULL;
vq->cb.trigger = NULL;
spin_unlock(&vq->irq_lock);
}
}
dev->status = 0;
dev->driver_features = 0;
dev->generation++;
spin_lock(&dev->irq_lock);
dev->config_cb.callback = NULL;
dev->config_cb.private = NULL;
spin_unlock(&dev->irq_lock);
flush_work(&dev->inject);
for (i = 0; i < dev->vq_num; i++) {
struct vduse_virtqueue *vq = dev->vqs[i];
vq->ready = false;
vq->desc_addr = 0;
vq->driver_addr = 0;
vq->device_addr = 0;
vq->num = 0;
memset(&vq->state, 0, sizeof(vq->state));
spin_lock(&vq->kick_lock);
vq->kicked = false;
if (vq->kickfd)
eventfd_ctx_put(vq->kickfd);
vq->kickfd = NULL;
spin_unlock(&vq->kick_lock);
spin_lock(&vq->irq_lock);
vq->cb.callback = NULL;
vq->cb.private = NULL;
vq->cb.trigger = NULL;
spin_unlock(&vq->irq_lock);
flush_work(&vq->inject);
flush_work(&vq->kick);
}
up_write(&dev->rwsem);
}
static int vduse_vdpa_set_vq_address(struct vdpa_device *vdpa, u16 idx,
@@ -550,16 +567,19 @@ static int vduse_vdpa_set_vq_address(struct vdpa_device *vdpa, u16 idx,
static void vduse_vq_kick(struct vduse_virtqueue *vq)
{
spin_lock(&vq->kick_lock);
if (!vq->ready)
goto unlock;
guard(rwsem_read)(&vq->dev->rwsem);
if (vq->dev->suspended)
return;
guard(spinlock)(&vq->kick_lock);
scoped_guard(spinlock_bh, &vq->ready_lock)
if (!vq->ready)
return;
if (vq->kickfd)
eventfd_signal(vq->kickfd);
else
vq->kicked = true;
unlock:
spin_unlock(&vq->kick_lock);
}
static void vduse_vq_kick_work(struct work_struct *work)
@@ -619,7 +639,30 @@ static void vduse_vdpa_set_vq_ready(struct vdpa_device *vdpa,
{
struct vduse_dev *dev = vdpa_to_vduse(vdpa);
struct vduse_virtqueue *vq = dev->vqs[idx];
struct vduse_dev_msg msg = { 0 };
int r;
if (dev->vduse_features & BIT_U64(VDUSE_F_QUEUE_READY)) {
msg.req.type = VDUSE_SET_VQ_READY;
msg.req.vq_ready.num = idx;
msg.req.vq_ready.ready = !!ready;
r = vduse_dev_msg_sync(dev, &msg);
if (r < 0) {
dev_dbg(&vdpa->dev, "device refuses to set vq %u ready %u",
idx, ready);
/* We can't do better than break the device in this case */
spin_lock(&dev->msg_lock);
vduse_dev_broken(dev);
spin_unlock(&dev->msg_lock);
return;
}
}
guard(spinlock_bh)(&vq->ready_lock);
vq->ready = ready;
}
@@ -628,6 +671,7 @@ static bool vduse_vdpa_get_vq_ready(struct vdpa_device *vdpa, u16 idx)
struct vduse_dev *dev = vdpa_to_vduse(vdpa);
struct vduse_virtqueue *vq = dev->vqs[idx];
guard(spinlock_bh)(&vq->ready_lock);
return vq->ready;
}
@@ -891,6 +935,27 @@ static int vduse_vdpa_set_map(struct vdpa_device *vdpa,
return 0;
}
static int vduse_vdpa_suspend(struct vdpa_device *vdpa)
{
struct vduse_dev *dev = vdpa_to_vduse(vdpa);
struct vduse_dev_msg msg = { 0 };
int ret;
msg.req.type = VDUSE_SUSPEND;
ret = vduse_dev_msg_sync(dev, &msg);
if (ret == 0) {
scoped_guard(rwsem_write, &dev->rwsem)
dev->suspended = true;
cancel_work_sync(&dev->inject);
for (u32 i = 0; i < dev->vq_num; i++)
cancel_work_sync(&dev->vqs[i]->inject);
}
return ret;
}
static void vduse_vdpa_free(struct vdpa_device *vdpa)
{
struct vduse_dev *dev = vdpa_to_vduse(vdpa);
@@ -932,6 +997,41 @@ static const struct vdpa_config_ops vduse_vdpa_config_ops = {
.free = vduse_vdpa_free,
};
static const struct vdpa_config_ops vduse_vdpa_config_ops_with_suspend = {
.set_vq_address = vduse_vdpa_set_vq_address,
.kick_vq = vduse_vdpa_kick_vq,
.set_vq_cb = vduse_vdpa_set_vq_cb,
.set_vq_num = vduse_vdpa_set_vq_num,
.get_vq_size = vduse_vdpa_get_vq_size,
.get_vq_group = vduse_get_vq_group,
.set_vq_ready = vduse_vdpa_set_vq_ready,
.get_vq_ready = vduse_vdpa_get_vq_ready,
.set_vq_state = vduse_vdpa_set_vq_state,
.get_vq_state = vduse_vdpa_get_vq_state,
.get_vq_align = vduse_vdpa_get_vq_align,
.get_device_features = vduse_vdpa_get_device_features,
.set_driver_features = vduse_vdpa_set_driver_features,
.get_driver_features = vduse_vdpa_get_driver_features,
.set_config_cb = vduse_vdpa_set_config_cb,
.get_vq_num_max = vduse_vdpa_get_vq_num_max,
.get_device_id = vduse_vdpa_get_device_id,
.get_vendor_id = vduse_vdpa_get_vendor_id,
.get_status = vduse_vdpa_get_status,
.set_status = vduse_vdpa_set_status,
.get_config_size = vduse_vdpa_get_config_size,
.get_config = vduse_vdpa_get_config,
.set_config = vduse_vdpa_set_config,
.get_generation = vduse_vdpa_get_generation,
.set_vq_affinity = vduse_vdpa_set_vq_affinity,
.get_vq_affinity = vduse_vdpa_get_vq_affinity,
.reset = vduse_vdpa_reset,
.set_map = vduse_vdpa_set_map,
.set_group_asid = vduse_set_group_asid,
.get_vq_map = vduse_get_vq_map,
.suspend = vduse_vdpa_suspend,
.free = vduse_vdpa_free,
};
static void vduse_dev_sync_single_for_device(union virtio_map token,
dma_addr_t dma_addr, size_t size,
enum dma_data_direction dir)
@@ -1115,15 +1215,16 @@ static int vduse_kickfd_setup(struct vduse_dev *dev,
} else if (eventfd->fd != VDUSE_EVENTFD_DEASSIGN)
return 0;
spin_lock(&vq->kick_lock);
guard(spinlock)(&vq->kick_lock);
if (vq->kickfd)
eventfd_ctx_put(vq->kickfd);
vq->kickfd = ctx;
guard(spinlock_bh)(&vq->ready_lock);
if (vq->ready && vq->kicked && vq->kickfd) {
eventfd_signal(vq->kickfd);
vq->kicked = false;
}
spin_unlock(&vq->kick_lock);
return 0;
}
@@ -1143,6 +1244,10 @@ static void vduse_dev_irq_inject(struct work_struct *work)
{
struct vduse_dev *dev = container_of(work, struct vduse_dev, inject);
guard(rwsem_read)(&dev->rwsem);
if (dev->suspended)
return;
spin_lock_bh(&dev->irq_lock);
if (dev->config_cb.callback)
dev->config_cb.callback(dev->config_cb.private);
@@ -1154,25 +1259,33 @@ static void vduse_vq_irq_inject(struct work_struct *work)
struct vduse_virtqueue *vq = container_of(work,
struct vduse_virtqueue, inject);
spin_lock_bh(&vq->irq_lock);
guard(rwsem_read)(&vq->dev->rwsem);
if (vq->dev->suspended)
return;
guard(spinlock_bh)(&vq->irq_lock);
guard(spinlock_bh)(&vq->ready_lock);
if (vq->ready && vq->cb.callback)
vq->cb.callback(vq->cb.private);
spin_unlock_bh(&vq->irq_lock);
}
static bool vduse_vq_signal_irqfd(struct vduse_virtqueue *vq)
{
bool signal = false;
guard(rwsem_read)(&vq->dev->rwsem);
if (vq->dev->suspended)
return false;
if (!vq->cb.trigger)
return false;
spin_lock_irq(&vq->irq_lock);
guard(spinlock_irq)(&vq->irq_lock);
guard(spinlock_irq)(&vq->ready_lock);
if (vq->ready && vq->cb.trigger) {
eventfd_signal(vq->cb.trigger);
signal = true;
}
spin_unlock_irq(&vq->irq_lock);
return signal;
}
@@ -1183,9 +1296,9 @@ static int vduse_dev_queue_irq_work(struct vduse_dev *dev,
{
int ret = -EINVAL;
down_read(&dev->rwsem);
if (!(dev->status & VIRTIO_CONFIG_S_DRIVER_OK))
goto unlock;
guard(rwsem_read)(&dev->rwsem);
if (dev->suspended || !(dev->status & VIRTIO_CONFIG_S_DRIVER_OK))
return ret;
ret = 0;
if (irq_effective_cpu == IRQ_UNBOUND)
@@ -1193,8 +1306,6 @@ static int vduse_dev_queue_irq_work(struct vduse_dev *dev,
else
queue_work_on(irq_effective_cpu,
vduse_irq_bound_wq, irq_work);
unlock:
up_read(&dev->rwsem);
return ret;
}
@@ -1510,7 +1621,9 @@ static long vduse_dev_ioctl(struct file *file, unsigned int cmd,
vq_info.split.avail_index =
vq->state.split.avail_index;
vq_info.ready = vq->ready;
scoped_guard(spinlock_bh, &vq->ready_lock) {
vq_info.ready = vq->ready;
}
ret = -EFAULT;
if (copy_to_user(argp, &vq_info, sizeof(vq_info)))
@@ -1740,7 +1853,9 @@ static long vduse_dev_compat_ioctl(struct file *file, unsigned int cmd,
vq_info.split.avail_index =
vq->state.split.avail_index;
vq_info.ready = vq->ready;
scoped_guard(spinlock_bh, &vq->ready_lock) {
vq_info.ready = vq->ready;
}
ret = -EFAULT;
if (copy_to_user(argp, &vq_info,
@@ -1948,11 +2063,13 @@ static int vduse_dev_init_vqs(struct vduse_dev *dev, u32 vq_align, u32 vq_num)
}
dev->vqs[i]->index = i;
dev->vqs[i]->dev = dev;
dev->vqs[i]->irq_effective_cpu = IRQ_UNBOUND;
INIT_WORK(&dev->vqs[i]->inject, vduse_vq_irq_inject);
INIT_WORK(&dev->vqs[i]->kick, vduse_vq_kick_work);
spin_lock_init(&dev->vqs[i]->kick_lock);
spin_lock_init(&dev->vqs[i]->irq_lock);
spin_lock_init(&dev->vqs[i]->ready_lock);
cpumask_setall(&dev->vqs[i]->irq_affinity);
kobject_init(&dev->vqs[i]->kobj, &vq_type);
@@ -2188,7 +2305,8 @@ static struct attribute *vduse_dev_attrs[] = {
ATTRIBUTE_GROUPS(vduse_dev);
static int vduse_create_dev(struct vduse_dev_config *config,
void *config_buf, u64 api_version)
void *config_buf, u64 api_version,
uint64_t vduse_features)
{
int ret;
struct vduse_dev *dev;
@@ -2210,6 +2328,9 @@ static int vduse_create_dev(struct vduse_dev_config *config,
dev->device_features = config->features;
dev->device_id = config->device_id;
dev->vendor_id = config->vendor_id;
dev->vduse_features = vduse_features;
dev_dbg(vduse_ctrl_dev, "Creating device %s with features 0x%llx",
config->name, vduse_features);
dev->nas = (dev->api_version < VDUSE_API_VERSION_1) ? 1 : config->nas;
dev->as = kzalloc_objs(dev->as[0], dev->nas);
@@ -2325,7 +2446,8 @@ static long vduse_ioctl(struct file *file, unsigned int cmd,
break;
}
config.name[VDUSE_NAME_MAX - 1] = '\0';
ret = vduse_create_dev(&config, buf, control->api_version);
ret = vduse_create_dev(&config, buf, control->api_version,
control->vduse_features);
if (ret)
kvfree(buf);
break;
@@ -2341,6 +2463,32 @@ static long vduse_ioctl(struct file *file, unsigned int cmd,
ret = vduse_destroy_dev(name);
break;
}
case VDUSE_GET_FEATURES:
ret = put_user(vduse_features, (u64 __user *)argp);
break;
case VDUSE_SET_FEATURES: {
u64 features;
ret = -EFAULT;
if (get_user(features, (u64 __user *)argp)) {
dev_dbg(vduse_ctrl_dev, "Could not get vduse features");
break;
}
ret = -EINVAL;
if (features & ~vduse_features) {
dev_dbg(vduse_ctrl_dev,
"Invalid features in %llx, expected %llx",
features, vduse_features);
break;
}
ret = 0;
control->vduse_features = features;
dev_dbg(vduse_ctrl_dev, "Set features %llx", features);
break;
}
default:
ret = -EINVAL;
break;
@@ -2367,6 +2515,7 @@ static int vduse_open(struct inode *inode, struct file *file)
return -ENOMEM;
control->api_version = VDUSE_API_VERSION_NOT_ASKED;
control->vduse_features = 0;
file->private_data = control;
return 0;
@@ -2391,12 +2540,18 @@ static struct vduse_mgmt_dev *vduse_mgmt;
static int vduse_dev_init_vdpa(struct vduse_dev *dev, const char *name)
{
struct vduse_vdpa *vdev;
const struct vdpa_config_ops *ops;
if (dev->vdev)
return -EEXIST;
if (dev->vduse_features & BIT_U64(VDUSE_F_SUSPEND))
ops = &vduse_vdpa_config_ops_with_suspend;
else
ops = &vduse_vdpa_config_ops;
vdev = vdpa_alloc_device(struct vduse_vdpa, vdpa, dev->dev,
&vduse_vdpa_config_ops, &vduse_map_ops,
ops, &vduse_map_ops,
dev->ngroups, dev->nas, name, true);
if (IS_ERR(vdev))
return PTR_ERR(vdev);
@@ -2531,7 +2686,6 @@ static void vduse_mgmtdev_exit(void)
static int vduse_init(void)
{
int ret;
struct device *dev;
ret = class_register(&vduse_class);
if (ret)
@@ -2548,9 +2702,10 @@ static int vduse_init(void)
if (ret)
goto err_ctrl_cdev;
dev = device_create(&vduse_class, NULL, vduse_major, NULL, "control");
if (IS_ERR(dev)) {
ret = PTR_ERR(dev);
vduse_ctrl_dev = device_create(&vduse_class, NULL, vduse_major, NULL, "control");
if (IS_ERR(vduse_ctrl_dev)) {
ret = PTR_ERR(vduse_ctrl_dev);
vduse_ctrl_dev = NULL;
goto err_device;
}

View File

@@ -189,11 +189,8 @@ static int vp_vdpa_request_irq(struct vp_vdpa *vp_vdpa)
vp_vdpa_vq_handler,
0, vp_vdpa->vring[i].msix_name,
&vp_vdpa->vring[i]);
if (ret) {
dev_err(&pdev->dev,
"vp_vdpa: fail to request irq for vq %d\n", i);
if (ret)
goto err;
}
vp_modern_queue_vector(mdev, i, msix_vec);
vp_vdpa->vring[i].irq = irq;
msix_vec++;
@@ -204,11 +201,8 @@ static int vp_vdpa_request_irq(struct vp_vdpa *vp_vdpa)
irq = pci_irq_vector(pdev, msix_vec);
ret = devm_request_irq(&pdev->dev, irq, vp_vdpa_config_handler, 0,
vp_vdpa->msix_name, vp_vdpa);
if (ret) {
dev_err(&pdev->dev,
"vp_vdpa: fail to request irq for config: %d\n", ret);
goto err;
}
if (ret)
goto err;
vp_modern_config_vector(mdev, msix_vec);
vp_vdpa->config_irq = irq;

View File

@@ -50,7 +50,7 @@ EXPORT_SYMBOL_GPL(vhost_iotlb_map_free);
* @perm: access permission of this range
* @opaque: the opaque pointer for the new mapping
*
* Returns an error last is smaller than start or memory allocation
* Returns an error if last is smaller than start or memory allocation
* fails
*/
int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb,
@@ -162,11 +162,11 @@ void vhost_iotlb_init(struct vhost_iotlb *iotlb, unsigned int limit,
EXPORT_SYMBOL_GPL(vhost_iotlb_init);
/**
* vhost_iotlb_alloc - add a new vhost IOTLB
* vhost_iotlb_alloc - allocate a new vhost IOTLB
* @limit: maximum number of IOTLB entries
* @flags: VHOST_IOTLB_FLAG_XXX
*
* Returns an error is memory allocation fails
* Returns NULL if memory allocation fails
*/
struct vhost_iotlb *vhost_iotlb_alloc(unsigned int limit, unsigned int flags)
{

View File

@@ -1786,7 +1786,8 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
return -EFAULT;
/* Zero the trailing space provided by user-space, if any */
if (clear_user(argp, size_mul(count - copied, sizeof(u64))))
if (clear_user(argp + size_mul(copied, sizeof(u64)),
size_mul(count - copied, sizeof(u64))))
return -EFAULT;
return 0;
case VHOST_SET_FEATURES_ARRAY:

View File

@@ -1660,6 +1660,10 @@ static int vhost_process_iotlb_msg(struct vhost_dev *dev, u32 asid,
ret = -EFAULT;
break;
}
if (!msg->size) {
ret = -EINVAL;
break;
}
vhost_vq_meta_reset(dev);
vhost_iotlb_del_range(dev->iotlb, msg->iova,
msg->iova + msg->size - 1);

View File

@@ -401,6 +401,32 @@ static const struct cpumask *virtio_irq_get_affinity(struct device *_d,
return dev->config->get_vq_affinity(dev, irq_vec);
}
/**
* virtio_device_shutdown - break and reset a device on shutdown
* @dev: the device
*
* Drivers with their own .shutdown method should quiesce their activity and
* then call this to stop the device the way the generic shutdown path does.
*/
void virtio_device_shutdown(struct virtio_device *dev)
{
/*
* Some devices get wedged if you kick them after they are
* reset. Mark all vqs as broken to make sure we don't.
*/
virtio_break_device(dev);
/*
* Guarantee that any callback will see vq->broken as true.
*/
virtio_synchronize_cbs(dev);
/*
* As IOMMUs are reset on shutdown, this will block device access to memory.
* Some devices get wedged if this happens, so reset to make sure it does not.
*/
dev->config->reset(dev);
}
EXPORT_SYMBOL_GPL(virtio_device_shutdown);
static void virtio_dev_shutdown(struct device *_d)
{
struct virtio_device *dev = dev_to_virtio(_d);
@@ -419,20 +445,7 @@ static void virtio_dev_shutdown(struct device *_d)
return;
}
/*
* Some devices get wedged if you kick them after they are
* reset. Mark all vqs as broken to make sure we don't.
*/
virtio_break_device(dev);
/*
* Guarantee that any callback will see vq->broken as true.
*/
virtio_synchronize_cbs(dev);
/*
* As IOMMUs are reset on shutdown, this will block device access to memory.
* Some devices get wedged if this happens, so reset to make sure it does not.
*/
dev->config->reset(dev);
virtio_device_shutdown(dev);
}
static int virtio_dev_num_vf(struct device *dev)

View File

@@ -7,6 +7,7 @@
*/
#include <linux/virtio.h>
#include <uapi/linux/virtio_ring.h>
#include <linux/virtio_balloon.h>
#include <linux/swap.h>
#include <linux/workqueue.h>
@@ -184,16 +185,18 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq)
{
struct scatterlist sg;
unsigned int len;
int err;
sg_init_one(&sg, vb->pfns, sizeof(vb->pfns[0]) * vb->num_pfns);
/* We should always be able to add one buffer to an empty queue. */
virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL);
err = virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL);
if (WARN_ON_ONCE(err))
return;
virtqueue_kick(vq);
/* When host has read buffer, this completes via balloon_ack */
wait_event(vb->acked, virtqueue_get_buf(vq, &len));
}
static int virtballoon_free_page_report(struct page_reporting_dev_info *pr_dev_info,
@@ -443,6 +446,7 @@ static void stats_handle_request(struct virtio_balloon *vb)
struct virtqueue *vq;
struct scatterlist sg;
unsigned int len, num_stats;
int err;
num_stats = update_balloon_stats(vb);
@@ -450,7 +454,9 @@ static void stats_handle_request(struct virtio_balloon *vb)
if (!virtqueue_get_buf(vq, &len))
return;
sg_init_one(&sg, vb->stats, sizeof(vb->stats[0]) * num_stats);
virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL);
err = virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL);
if (WARN_ON_ONCE(err))
return;
virtqueue_kick(vq);
}
@@ -1095,32 +1101,51 @@ static void remove_common(struct virtio_balloon *vb)
vb->vdev->config->del_vqs(vb->vdev);
}
static void virtballoon_remove(struct virtio_device *vdev)
/*
* Stop all asynchronous balloon work. The device must still be alive so that
* in-flight requests can drain via the host before it is reset or freed.
*/
static void virtballoon_quiesce(struct virtio_balloon *vb)
{
struct virtio_balloon *vb = vdev->priv;
struct virtio_device *vdev = vb->vdev;
if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING))
if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_REPORTING))
page_reporting_unregister(&vb->pr_dev_info);
if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
unregister_oom_notifier(&vb->oom_nb);
if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
virtio_balloon_unregister_shrinker(vb);
spin_lock_irq(&vb->stop_update_lock);
vb->stop_update = true;
spin_unlock_irq(&vb->stop_update_lock);
cancel_work_sync(&vb->update_balloon_size_work);
cancel_work_sync(&vb->update_balloon_stats_work);
if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT)) {
if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
cancel_work_sync(&vb->report_free_page_work);
}
static void virtballoon_remove(struct virtio_device *vdev)
{
struct virtio_balloon *vb = vdev->priv;
virtballoon_quiesce(vb);
if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
destroy_workqueue(vb->balloon_wq);
}
remove_common(vb);
mutex_destroy(&vb->balloon_lock);
kfree(vb);
}
static void virtballoon_shutdown(struct virtio_device *vdev)
{
virtballoon_quiesce(vdev->priv);
virtio_device_shutdown(vdev);
}
#ifdef CONFIG_PM_SLEEP
static int virtballoon_freeze(struct virtio_device *vdev)
{
@@ -1165,6 +1190,11 @@ static int virtballoon_validate(struct virtio_device *vdev)
else if (!virtio_has_feature(vdev, VIRTIO_BALLOON_F_PAGE_POISON))
__virtio_clear_bit(vdev, VIRTIO_BALLOON_F_REPORTING);
/*
* Disable indirect descriptors to avoid memory allocation in
* virtqueue_add during page reporting.
*/
__virtio_clear_bit(vdev, VIRTIO_RING_F_INDIRECT_DESC);
__virtio_clear_bit(vdev, VIRTIO_F_ACCESS_PLATFORM);
return 0;
}
@@ -1186,6 +1216,7 @@ static struct virtio_driver virtio_balloon_driver = {
.validate = virtballoon_validate,
.probe = virtballoon_probe,
.remove = virtballoon_remove,
.shutdown = virtballoon_shutdown,
.config_changed = virtballoon_changed,
#ifdef CONFIG_PM_SLEEP
.freeze = virtballoon_freeze,

View File

@@ -14,7 +14,7 @@
* struct embedded in a virtio_dma_buf_ops.
*
* This wraps dma_buf_export() to allow virtio drivers to create a dma-buf
* for an virtio exported object that can be queried by other virtio drivers
* for a virtio exported object that can be queried by other virtio drivers
* for the object's UUID.
*/
struct dma_buf *virtio_dma_buf_export

View File

@@ -423,14 +423,14 @@ static int virtio_mem_bbm_bb_states_prepare_next_bb(struct virtio_mem *vm)
}
#define virtio_mem_bbm_for_each_bb(_vm, _bb_id, _state) \
for (_bb_id = vm->bbm.first_bb_id; \
_bb_id < vm->bbm.next_bb_id && _vm->bbm.bb_count[_state]; \
for (_bb_id = (_vm)->bbm.first_bb_id; \
_bb_id < (_vm)->bbm.next_bb_id && (_vm)->bbm.bb_count[_state]; \
_bb_id++) \
if (virtio_mem_bbm_get_bb_state(_vm, _bb_id) == _state)
#define virtio_mem_bbm_for_each_bb_rev(_vm, _bb_id, _state) \
for (_bb_id = vm->bbm.next_bb_id - 1; \
_bb_id >= vm->bbm.first_bb_id && _vm->bbm.bb_count[_state]; \
for (_bb_id = (_vm)->bbm.next_bb_id - 1; \
_bb_id >= (_vm)->bbm.first_bb_id && (_vm)->bbm.bb_count[_state]; \
_bb_id--) \
if (virtio_mem_bbm_get_bb_state(_vm, _bb_id) == _state)
@@ -1080,7 +1080,7 @@ static int virtio_mem_memory_notifier_cb(struct notifier_block *nb,
atomic64_sub(size, &vm->offline_size);
/*
* Start adding more memory once we onlined half of our
* threshold. Don't trigger if it's possibly due to our actipn
* threshold. Don't trigger if it's possibly due to our action
* (e.g., us adding memory which gets onlined immediately from
* the core).
*/

View File

@@ -55,6 +55,7 @@
#define pr_fmt(fmt) "virtio-mmio: " fmt
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/highmem.h>
#include <linux/interrupt.h>
@@ -114,9 +115,9 @@ static int vm_finalize_features(struct virtio_device *vdev)
vring_transport_features(vdev);
/* Make sure there are no mixed devices */
if (vm_dev->version == 2 &&
if (vm_dev->version >= 2 &&
!__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) {
dev_err(&vdev->dev, "New virtio-mmio devices (version 2) must provide VIRTIO_F_VERSION_1 feature!\n");
dev_err(&vdev->dev, "New virtio-mmio devices (version >= 2) must provide VIRTIO_F_VERSION_1 feature!\n");
return -EINVAL;
}
@@ -254,6 +255,12 @@ static void vm_reset(struct virtio_device *vdev)
/* 0 status means a reset. */
writel(0, vm_dev->base + VIRTIO_MMIO_STATUS);
if (vm_dev->version >= 3) {
/* Wait for reset to complete. */
while (vm_get_status(vdev))
fsleep(1000);
}
}
@@ -600,7 +607,7 @@ static int virtio_mmio_probe(struct platform_device *pdev)
/* Check device version */
vm_dev->version = readl(vm_dev->base + VIRTIO_MMIO_VERSION);
if (vm_dev->version < 1 || vm_dev->version > 2) {
if (vm_dev->version < 1 || vm_dev->version > 3) {
dev_err(&pdev->dev, "Version %ld not supported!\n",
vm_dev->version);
rc = -ENXIO;

View File

@@ -499,7 +499,7 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs,
if (!avq_num)
return 0;
sprintf(avq->name, "avq.%u", avq->vq_index);
vq = vp_setup_vq(vdev, queue_idx++, vp_modern_avq_done, avq->name,
vq = vp_setup_vq(vdev, avq->vq_index, vp_modern_avq_done, avq->name,
false, VIRTIO_MSI_NO_VECTOR,
&vp_dev->admin_vq.info);
if (IS_ERR(vq)) {

View File

@@ -3233,6 +3233,14 @@ bool virtqueue_enable_cb_delayed(struct virtqueue *_vq)
{
struct vring_virtqueue *vq = to_vvq(_vq);
/*
* When the device is broken there is no point in polling used->idx,
* the backend will never update it. Return true to let callers
* exit their cleanup loops instead of spinning forever.
*/
if (unlikely(vq->broken))
return true;
if (vq->event_triggered)
data_race(vq->event_triggered = false);

View File

@@ -574,8 +574,8 @@ static int viortc_msg_xfer(struct viortc_vq *vq, struct viortc_msg *msg,
* read requests
*/
/** timeout for clock readings, where timeouts are considered non-fatal */
#define VIORTC_MSG_READ_TIMEOUT secs_to_jiffies(60)
/** timeout for runtime requests, where timeouts are considered non-fatal */
#define VIORTC_MSG_TIMEOUT secs_to_jiffies(60)
/**
* viortc_read() - VIRTIO_RTC_REQ_READ wrapper
@@ -600,7 +600,7 @@ int viortc_read(struct viortc_dev *viortc, u16 vio_clk_id, u64 *reading)
VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id);
ret = viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl),
VIORTC_MSG_READ_TIMEOUT);
VIORTC_MSG_TIMEOUT);
if (ret) {
dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__,
ret);
@@ -642,7 +642,7 @@ int viortc_read_cross(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_counter,
VIORTC_MSG_WRITE(hdl, hw_counter, &hw_counter);
ret = viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl),
VIORTC_MSG_READ_TIMEOUT);
VIORTC_MSG_TIMEOUT);
if (ret) {
dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__,
ret);
@@ -809,7 +809,7 @@ int viortc_read_alarm(struct viortc_dev *viortc, u16 vio_clk_id,
VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id);
ret = viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl),
0);
VIORTC_MSG_TIMEOUT);
if (ret) {
dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__,
ret);
@@ -858,7 +858,7 @@ int viortc_set_alarm(struct viortc_dev *viortc, u16 vio_clk_id, u64 alarm_time,
VIORTC_MSG_WRITE(hdl, flags, &flags);
ret = viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl),
0);
VIORTC_MSG_TIMEOUT);
if (ret) {
dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__,
ret);
@@ -900,7 +900,7 @@ int viortc_set_alarm_enabled(struct viortc_dev *viortc, u16 vio_clk_id,
VIORTC_MSG_WRITE(hdl, flags, &flags);
ret = viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl),
0);
VIORTC_MSG_TIMEOUT);
if (ret) {
dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__,
ret);

View File

@@ -126,6 +126,15 @@ struct nd_mapping_desc {
struct bio;
struct resource;
struct nd_region;
/*
* Provider flush callback return values:
* 0: flush completed synchronously
* <0: flush failed
* >0: flush completion was queued and @bio will be completed later
*/
#define NVDIMM_FLUSH_ASYNC 1
struct nd_region_desc {
struct resource *res;
struct nd_mapping_desc *mapping;

View File

@@ -213,6 +213,7 @@ int virtio_device_freeze(struct virtio_device *dev);
int virtio_device_restore(struct virtio_device *dev);
#endif
void virtio_reset_device(struct virtio_device *dev);
void virtio_device_shutdown(struct virtio_device *dev);
int virtio_device_reset_prepare(struct virtio_device *dev);
int virtio_device_reset_done(struct virtio_device *dev);

View File

@@ -17,7 +17,7 @@
* @ops: the base dma_buf_ops. ops.attach MUST be virtio_dma_buf_attach.
* @device_attach: [optional] callback invoked by virtio_dma_buf_attach during
* all attach operations.
* @get_uid: [required] callback to get the uuid of the exported object.
* @get_uuid: [required] callback to get the uuid of the exported object.
*/
struct virtio_dma_buf_ops {
struct dma_buf_ops ops;

View File

@@ -14,6 +14,12 @@
#define VDUSE_API_VERSION_1 1
/* The VDUSE instance expects a request for vq ready */
#define VDUSE_F_QUEUE_READY 0
/* The VDUSE instance expects a request for suspend */
#define VDUSE_F_SUSPEND 1
/*
* Get the version of VDUSE API that kernel supported (VDUSE_API_VERSION).
* This is used for future extension.
@@ -63,6 +69,12 @@ struct vduse_dev_config {
*/
#define VDUSE_DESTROY_DEV _IOW(VDUSE_BASE, 0x03, char[VDUSE_NAME_MAX])
/* Get the VDUSE supported features */
#define VDUSE_GET_FEATURES _IOR(VDUSE_BASE, 0x04, __u64)
/* Set the VDUSE features */
#define VDUSE_SET_FEATURES _IOW(VDUSE_BASE, 0x05, __u64)
/* The ioctls for VDUSE device (/dev/vduse/$NAME) */
/**
@@ -325,6 +337,8 @@ enum vduse_req_type {
VDUSE_SET_STATUS,
VDUSE_UPDATE_IOTLB,
VDUSE_SET_VQ_GROUP_ASID,
VDUSE_SET_VQ_READY,
VDUSE_SUSPEND,
};
/**
@@ -372,6 +386,15 @@ struct vduse_iova_range_v2 {
__u32 padding;
};
/**
* struct vduse_vq_ready - Virtqueue ready request message
* @num: Virtqueue number
*/
struct vduse_vq_ready {
__u32 num;
__u32 ready;
};
/**
* struct vduse_dev_request - control request
* @type: request type
@@ -382,6 +405,7 @@ struct vduse_iova_range_v2 {
* @iova: IOVA range for updating
* @iova_v2: IOVA range for updating if API_VERSION >= 1
* @vq_group_asid: ASID of a virtqueue group
* @vq_ready: Virtqueue ready request
* @padding: padding
*
* Structure used by read(2) on /dev/vduse/$NAME.
@@ -399,6 +423,10 @@ struct vduse_dev_request {
*/
struct vduse_iova_range_v2 iova_v2;
struct vduse_vq_group_asid vq_group_asid;
/* Only if VDUSE_F_QUEUE_READY is negotiated */
struct vduse_vq_ready vq_ready;
__u32 padding[32];
};
};

View File

@@ -69,6 +69,7 @@
#define VIRTIO_ID_BT 40 /* virtio bluetooth */
#define VIRTIO_ID_GPIO 41 /* virtio gpio */
#define VIRTIO_ID_SPI 45 /* virtio spi */
#define VIRTIO_ID_MEDIA 48 /* virtio media */
/*
* Virtio Transitional IDs

View File

@@ -1491,6 +1491,7 @@ void iov_iter_restore(struct iov_iter *i, struct iov_iter_state *state)
i->__iov -= state->nr_segs - i->nr_segs;
i->nr_segs = state->nr_segs;
}
EXPORT_SYMBOL_FOR_MODULES(iov_iter_restore, "vmw_vsock_virtio_transport_common");
/*
* Extract a list of contiguous pages from an ITER_FOLIOQ iterator. This does

View File

@@ -302,6 +302,7 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
u32 max_skb_len = VIRTIO_VSOCK_MAX_PKT_BUF_SIZE;
u32 src_cid, src_port, dst_cid, dst_port;
const struct virtio_transport *t_ops;
struct iov_iter_state msg_iter_state;
struct virtio_vsock_sock *vvs;
struct ubuf_info *uarg = NULL;
u32 pkt_len = info->pkt_len;
@@ -372,8 +373,17 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
struct sk_buff *skb;
size_t skb_len;
/* Save iterator state in case allocation or transmission fails
* so we can restore it and retry.
*/
if (info->msg)
iov_iter_save_state(&info->msg->msg_iter, &msg_iter_state);
skb_len = min(max_skb_len, rest_len);
/* Note: virtio_transport_alloc_skb() can advance info->msg->msg_iter
* even if it fails (e.g. partial GUP success).
*/
skb = virtio_transport_alloc_skb(info, skb_len, can_zcopy,
uarg,
src_cid, src_port,
@@ -403,6 +413,9 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
break;
} while (rest_len);
if (info->msg && ret < 0)
iov_iter_restore(&info->msg->msg_iter, &msg_iter_state);
virtio_transport_put_credit(vvs, rest_len);
/* msg_zerocopy_realloc() initializes the ubuf_info refcnt to 1.

View File

@@ -450,11 +450,6 @@ static const struct option longopts[] = {
.val = 'n',
.has_arg = required_argument,
},
{
.name = "batch",
.val = 'b',
.has_arg = required_argument,
},
{
}
};

View File

@@ -84,7 +84,7 @@ static int wait_order(int ctl_fd)
}
/*
* contol read/write threads by handling global_run_operation
* control read/write threads by handling global_run_operation
*/
void *rw_ctl_loop(int ctl_fd)
{

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/* Simple test of virtio code, entirely in userpsace. */
/* Simple test of virtio code, entirely in userspace. */
#define _GNU_SOURCE
#include <sched.h>
#include <err.h>