mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-27 14:04:47 -04:00
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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -450,11 +450,6 @@ static const struct option longopts[] = {
|
||||
.val = 'n',
|
||||
.has_arg = required_argument,
|
||||
},
|
||||
{
|
||||
.name = "batch",
|
||||
.val = 'b',
|
||||
.has_arg = required_argument,
|
||||
},
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user