Merge fc6d70f40b ("virtio_ring: check desc == NULL when using indirect with packed") into android-mainline
Steps on the way to 5.16-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I7196f49b7264d3f22de3b532aadf389daada8acd
This commit is contained in:
@@ -371,6 +371,7 @@ config XEN_BLKDEV_BACKEND
|
||||
config VIRTIO_BLK
|
||||
tristate "Virtio block driver"
|
||||
depends on VIRTIO
|
||||
select SG_POOL
|
||||
help
|
||||
This is the virtual block driver for virtio. It can be used with
|
||||
QEMU based VMMs (like KVM or Xen). Say Y or M.
|
||||
|
||||
+122
-57
@@ -24,6 +24,31 @@
|
||||
/* The maximum number of sg elements that fit into a virtqueue */
|
||||
#define VIRTIO_BLK_MAX_SG_ELEMS 32768
|
||||
|
||||
#ifdef CONFIG_ARCH_NO_SG_CHAIN
|
||||
#define VIRTIO_BLK_INLINE_SG_CNT 0
|
||||
#else
|
||||
#define VIRTIO_BLK_INLINE_SG_CNT 2
|
||||
#endif
|
||||
|
||||
static int virtblk_queue_count_set(const char *val,
|
||||
const struct kernel_param *kp)
|
||||
{
|
||||
return param_set_uint_minmax(val, kp, 1, nr_cpu_ids);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops queue_count_ops = {
|
||||
.set = virtblk_queue_count_set,
|
||||
.get = param_get_uint,
|
||||
};
|
||||
|
||||
static unsigned int num_request_queues;
|
||||
module_param_cb(num_request_queues, &queue_count_ops, &num_request_queues,
|
||||
0644);
|
||||
MODULE_PARM_DESC(num_request_queues,
|
||||
"Limit the number of request queues to use for blk device. "
|
||||
"0 for no limit. "
|
||||
"Values > nr_cpu_ids truncated to nr_cpu_ids.");
|
||||
|
||||
static int major;
|
||||
static DEFINE_IDA(vd_index_ida);
|
||||
|
||||
@@ -77,6 +102,7 @@ struct virtio_blk {
|
||||
struct virtblk_req {
|
||||
struct virtio_blk_outhdr out_hdr;
|
||||
u8 status;
|
||||
struct sg_table sg_table;
|
||||
struct scatterlist sg[];
|
||||
};
|
||||
|
||||
@@ -162,12 +188,92 @@ static int virtblk_setup_discard_write_zeroes(struct request *req, bool unmap)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtblk_unmap_data(struct request *req, struct virtblk_req *vbr)
|
||||
{
|
||||
if (blk_rq_nr_phys_segments(req))
|
||||
sg_free_table_chained(&vbr->sg_table,
|
||||
VIRTIO_BLK_INLINE_SG_CNT);
|
||||
}
|
||||
|
||||
static int virtblk_map_data(struct blk_mq_hw_ctx *hctx, struct request *req,
|
||||
struct virtblk_req *vbr)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!blk_rq_nr_phys_segments(req))
|
||||
return 0;
|
||||
|
||||
vbr->sg_table.sgl = vbr->sg;
|
||||
err = sg_alloc_table_chained(&vbr->sg_table,
|
||||
blk_rq_nr_phys_segments(req),
|
||||
vbr->sg_table.sgl,
|
||||
VIRTIO_BLK_INLINE_SG_CNT);
|
||||
if (unlikely(err))
|
||||
return -ENOMEM;
|
||||
|
||||
return blk_rq_map_sg(hctx->queue, req, vbr->sg_table.sgl);
|
||||
}
|
||||
|
||||
static void virtblk_cleanup_cmd(struct request *req)
|
||||
{
|
||||
if (req->rq_flags & RQF_SPECIAL_PAYLOAD)
|
||||
kfree(bvec_virt(&req->special_vec));
|
||||
}
|
||||
|
||||
static int virtblk_setup_cmd(struct virtio_device *vdev, struct request *req,
|
||||
struct virtblk_req *vbr)
|
||||
{
|
||||
bool unmap = false;
|
||||
u32 type;
|
||||
|
||||
vbr->out_hdr.sector = 0;
|
||||
|
||||
switch (req_op(req)) {
|
||||
case REQ_OP_READ:
|
||||
type = VIRTIO_BLK_T_IN;
|
||||
vbr->out_hdr.sector = cpu_to_virtio64(vdev,
|
||||
blk_rq_pos(req));
|
||||
break;
|
||||
case REQ_OP_WRITE:
|
||||
type = VIRTIO_BLK_T_OUT;
|
||||
vbr->out_hdr.sector = cpu_to_virtio64(vdev,
|
||||
blk_rq_pos(req));
|
||||
break;
|
||||
case REQ_OP_FLUSH:
|
||||
type = VIRTIO_BLK_T_FLUSH;
|
||||
break;
|
||||
case REQ_OP_DISCARD:
|
||||
type = VIRTIO_BLK_T_DISCARD;
|
||||
break;
|
||||
case REQ_OP_WRITE_ZEROES:
|
||||
type = VIRTIO_BLK_T_WRITE_ZEROES;
|
||||
unmap = !(req->cmd_flags & REQ_NOUNMAP);
|
||||
break;
|
||||
case REQ_OP_DRV_IN:
|
||||
type = VIRTIO_BLK_T_GET_ID;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return BLK_STS_IOERR;
|
||||
}
|
||||
|
||||
vbr->out_hdr.type = cpu_to_virtio32(vdev, type);
|
||||
vbr->out_hdr.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));
|
||||
|
||||
if (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES) {
|
||||
if (virtblk_setup_discard_write_zeroes(req, unmap))
|
||||
return BLK_STS_RESOURCE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void virtblk_request_done(struct request *req)
|
||||
{
|
||||
struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
|
||||
|
||||
if (req->rq_flags & RQF_SPECIAL_PAYLOAD)
|
||||
kfree(bvec_virt(&req->special_vec));
|
||||
virtblk_unmap_data(req, vbr);
|
||||
virtblk_cleanup_cmd(req);
|
||||
blk_mq_end_request(req, virtblk_result(vbr));
|
||||
}
|
||||
|
||||
@@ -225,57 +331,23 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||
int qid = hctx->queue_num;
|
||||
int err;
|
||||
bool notify = false;
|
||||
bool unmap = false;
|
||||
u32 type;
|
||||
|
||||
BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
|
||||
|
||||
switch (req_op(req)) {
|
||||
case REQ_OP_READ:
|
||||
case REQ_OP_WRITE:
|
||||
type = 0;
|
||||
break;
|
||||
case REQ_OP_FLUSH:
|
||||
type = VIRTIO_BLK_T_FLUSH;
|
||||
break;
|
||||
case REQ_OP_DISCARD:
|
||||
type = VIRTIO_BLK_T_DISCARD;
|
||||
break;
|
||||
case REQ_OP_WRITE_ZEROES:
|
||||
type = VIRTIO_BLK_T_WRITE_ZEROES;
|
||||
unmap = !(req->cmd_flags & REQ_NOUNMAP);
|
||||
break;
|
||||
case REQ_OP_DRV_IN:
|
||||
type = VIRTIO_BLK_T_GET_ID;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return BLK_STS_IOERR;
|
||||
}
|
||||
|
||||
vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, type);
|
||||
vbr->out_hdr.sector = type ?
|
||||
0 : cpu_to_virtio64(vblk->vdev, blk_rq_pos(req));
|
||||
vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(req));
|
||||
err = virtblk_setup_cmd(vblk->vdev, req, vbr);
|
||||
if (unlikely(err))
|
||||
return err;
|
||||
|
||||
blk_mq_start_request(req);
|
||||
|
||||
if (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES) {
|
||||
err = virtblk_setup_discard_write_zeroes(req, unmap);
|
||||
if (err)
|
||||
return BLK_STS_RESOURCE;
|
||||
}
|
||||
|
||||
num = blk_rq_map_sg(hctx->queue, req, vbr->sg);
|
||||
if (num) {
|
||||
if (rq_data_dir(req) == WRITE)
|
||||
vbr->out_hdr.type |= cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_OUT);
|
||||
else
|
||||
vbr->out_hdr.type |= cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_IN);
|
||||
num = virtblk_map_data(hctx, req, vbr);
|
||||
if (unlikely(num < 0)) {
|
||||
virtblk_cleanup_cmd(req);
|
||||
return BLK_STS_RESOURCE;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&vblk->vqs[qid].lock, flags);
|
||||
err = virtblk_add_req(vblk->vqs[qid].vq, vbr, vbr->sg, num);
|
||||
err = virtblk_add_req(vblk->vqs[qid].vq, vbr, vbr->sg_table.sgl, num);
|
||||
if (err) {
|
||||
virtqueue_kick(vblk->vqs[qid].vq);
|
||||
/* Don't stop the queue if -ENOMEM: we may have failed to
|
||||
@@ -284,6 +356,8 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||
if (err == -ENOSPC)
|
||||
blk_mq_stop_hw_queue(hctx);
|
||||
spin_unlock_irqrestore(&vblk->vqs[qid].lock, flags);
|
||||
virtblk_unmap_data(req, vbr);
|
||||
virtblk_cleanup_cmd(req);
|
||||
switch (err) {
|
||||
case -ENOSPC:
|
||||
return BLK_STS_DEV_RESOURCE;
|
||||
@@ -498,7 +572,9 @@ static int init_vq(struct virtio_blk *vblk)
|
||||
if (err)
|
||||
num_vqs = 1;
|
||||
|
||||
num_vqs = min_t(unsigned int, nr_cpu_ids, num_vqs);
|
||||
num_vqs = min_t(unsigned int,
|
||||
min_not_zero(num_request_queues, nr_cpu_ids),
|
||||
num_vqs);
|
||||
|
||||
vblk->vqs = kmalloc_array(num_vqs, sizeof(*vblk->vqs), GFP_KERNEL);
|
||||
if (!vblk->vqs)
|
||||
@@ -660,16 +736,6 @@ static const struct attribute_group *virtblk_attr_groups[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int virtblk_init_request(struct blk_mq_tag_set *set, struct request *rq,
|
||||
unsigned int hctx_idx, unsigned int numa_node)
|
||||
{
|
||||
struct virtio_blk *vblk = set->driver_data;
|
||||
struct virtblk_req *vbr = blk_mq_rq_to_pdu(rq);
|
||||
|
||||
sg_init_table(vbr->sg, vblk->sg_elems);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtblk_map_queues(struct blk_mq_tag_set *set)
|
||||
{
|
||||
struct virtio_blk *vblk = set->driver_data;
|
||||
@@ -682,7 +748,6 @@ static const struct blk_mq_ops virtio_mq_ops = {
|
||||
.queue_rq = virtio_queue_rq,
|
||||
.commit_rqs = virtio_commit_rqs,
|
||||
.complete = virtblk_request_done,
|
||||
.init_request = virtblk_init_request,
|
||||
.map_queues = virtblk_map_queues,
|
||||
};
|
||||
|
||||
@@ -762,7 +827,7 @@ static int virtblk_probe(struct virtio_device *vdev)
|
||||
vblk->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
|
||||
vblk->tag_set.cmd_size =
|
||||
sizeof(struct virtblk_req) +
|
||||
sizeof(struct scatterlist) * sg_elems;
|
||||
sizeof(struct scatterlist) * VIRTIO_BLK_INLINE_SG_CNT;
|
||||
vblk->tag_set.driver_data = vblk;
|
||||
vblk->tag_set.nr_hw_queues = vblk->num_vqs;
|
||||
|
||||
|
||||
@@ -18,13 +18,20 @@ static DEFINE_IDA(rng_index_ida);
|
||||
struct virtrng_info {
|
||||
struct hwrng hwrng;
|
||||
struct virtqueue *vq;
|
||||
struct completion have_data;
|
||||
char name[25];
|
||||
unsigned int data_avail;
|
||||
int index;
|
||||
bool busy;
|
||||
bool hwrng_register_done;
|
||||
bool hwrng_removed;
|
||||
/* data transfer */
|
||||
struct completion have_data;
|
||||
unsigned int data_avail;
|
||||
unsigned int data_idx;
|
||||
/* minimal size returned by rng_buffer_size() */
|
||||
#if SMP_CACHE_BYTES < 32
|
||||
u8 data[32];
|
||||
#else
|
||||
u8 data[SMP_CACHE_BYTES];
|
||||
#endif
|
||||
};
|
||||
|
||||
static void random_recv_done(struct virtqueue *vq)
|
||||
@@ -35,54 +42,88 @@ static void random_recv_done(struct virtqueue *vq)
|
||||
if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
|
||||
return;
|
||||
|
||||
vi->data_idx = 0;
|
||||
|
||||
complete(&vi->have_data);
|
||||
}
|
||||
|
||||
/* The host will fill any buffer we give it with sweet, sweet randomness. */
|
||||
static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size)
|
||||
static void request_entropy(struct virtrng_info *vi)
|
||||
{
|
||||
struct scatterlist sg;
|
||||
|
||||
sg_init_one(&sg, buf, size);
|
||||
reinit_completion(&vi->have_data);
|
||||
vi->data_avail = 0;
|
||||
vi->data_idx = 0;
|
||||
|
||||
sg_init_one(&sg, vi->data, sizeof(vi->data));
|
||||
|
||||
/* There should always be room for one buffer. */
|
||||
virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL);
|
||||
virtqueue_add_inbuf(vi->vq, &sg, 1, vi->data, GFP_KERNEL);
|
||||
|
||||
virtqueue_kick(vi->vq);
|
||||
}
|
||||
|
||||
static unsigned int copy_data(struct virtrng_info *vi, void *buf,
|
||||
unsigned int size)
|
||||
{
|
||||
size = min_t(unsigned int, size, vi->data_avail);
|
||||
memcpy(buf, vi->data + vi->data_idx, size);
|
||||
vi->data_idx += size;
|
||||
vi->data_avail -= size;
|
||||
if (vi->data_avail == 0)
|
||||
request_entropy(vi);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
|
||||
{
|
||||
int ret;
|
||||
struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
|
||||
unsigned int chunk;
|
||||
size_t read;
|
||||
|
||||
if (vi->hwrng_removed)
|
||||
return -ENODEV;
|
||||
|
||||
if (!vi->busy) {
|
||||
vi->busy = true;
|
||||
reinit_completion(&vi->have_data);
|
||||
register_buffer(vi, buf, size);
|
||||
read = 0;
|
||||
|
||||
/* copy available data */
|
||||
if (vi->data_avail) {
|
||||
chunk = copy_data(vi, buf, size);
|
||||
size -= chunk;
|
||||
read += chunk;
|
||||
}
|
||||
|
||||
if (!wait)
|
||||
return 0;
|
||||
return read;
|
||||
|
||||
ret = wait_for_completion_killable(&vi->have_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* We have already copied available entropy,
|
||||
* so either size is 0 or data_avail is 0
|
||||
*/
|
||||
while (size != 0) {
|
||||
/* data_avail is 0 but a request is pending */
|
||||
ret = wait_for_completion_killable(&vi->have_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* if vi->data_avail is 0, we have been interrupted
|
||||
* by a cleanup, but buffer stays in the queue
|
||||
*/
|
||||
if (vi->data_avail == 0)
|
||||
return read;
|
||||
|
||||
vi->busy = false;
|
||||
chunk = copy_data(vi, buf + read, size);
|
||||
size -= chunk;
|
||||
read += chunk;
|
||||
}
|
||||
|
||||
return vi->data_avail;
|
||||
return read;
|
||||
}
|
||||
|
||||
static void virtio_cleanup(struct hwrng *rng)
|
||||
{
|
||||
struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
|
||||
|
||||
if (vi->busy)
|
||||
wait_for_completion(&vi->have_data);
|
||||
complete(&vi->have_data);
|
||||
}
|
||||
|
||||
static int probe_common(struct virtio_device *vdev)
|
||||
@@ -118,6 +159,9 @@ static int probe_common(struct virtio_device *vdev)
|
||||
goto err_find;
|
||||
}
|
||||
|
||||
/* we always have a pending entropy request */
|
||||
request_entropy(vi);
|
||||
|
||||
return 0;
|
||||
|
||||
err_find:
|
||||
@@ -133,9 +177,9 @@ static void remove_common(struct virtio_device *vdev)
|
||||
|
||||
vi->hwrng_removed = true;
|
||||
vi->data_avail = 0;
|
||||
vi->data_idx = 0;
|
||||
complete(&vi->have_data);
|
||||
vdev->config->reset(vdev);
|
||||
vi->busy = false;
|
||||
if (vi->hwrng_register_done)
|
||||
hwrng_unregister(&vi->hwrng);
|
||||
vdev->config->del_vqs(vdev);
|
||||
|
||||
@@ -408,12 +408,13 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,
|
||||
* add_recvbuf_mergeable() + get_mergeable_buf_len()
|
||||
*/
|
||||
truesize = headroom ? PAGE_SIZE : truesize;
|
||||
tailroom = truesize - len - headroom - (hdr_padded_len - hdr_len);
|
||||
tailroom = truesize - headroom;
|
||||
buf = p - headroom;
|
||||
|
||||
len -= hdr_len;
|
||||
offset += hdr_padded_len;
|
||||
p += hdr_padded_len;
|
||||
tailroom -= hdr_padded_len + len;
|
||||
|
||||
shinfo_size = SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
||||
|
||||
|
||||
@@ -78,4 +78,12 @@ config VP_VDPA
|
||||
help
|
||||
This kernel module bridges virtio PCI device to vDPA bus.
|
||||
|
||||
config ALIBABA_ENI_VDPA
|
||||
tristate "vDPA driver for Alibaba ENI"
|
||||
select VIRTIO_PCI_LIB_LEGACY
|
||||
depends on PCI_MSI && X86
|
||||
help
|
||||
VDPA driver for Alibaba ENI (Elastic Network Interface) which is built upon
|
||||
virtio 0.9.5 specification.
|
||||
|
||||
endif # VDPA
|
||||
|
||||
@@ -5,3 +5,4 @@ obj-$(CONFIG_VDPA_USER) += vdpa_user/
|
||||
obj-$(CONFIG_IFCVF) += ifcvf/
|
||||
obj-$(CONFIG_MLX5_VDPA) += mlx5/
|
||||
obj-$(CONFIG_VP_VDPA) += virtio_pci/
|
||||
obj-$(CONFIG_ALIBABA_ENI_VDPA) += alibaba/
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_ALIBABA_ENI_VDPA) += eni_vdpa.o
|
||||
|
||||
@@ -0,0 +1,553 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* vDPA bridge driver for Alibaba ENI(Elastic Network Interface)
|
||||
*
|
||||
* Copyright (c) 2021, Alibaba Inc. All rights reserved.
|
||||
* Author: Wu Zongyong <wuzongyong@linux.alibaba.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "linux/bits.h"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/vdpa.h>
|
||||
#include <linux/virtio.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/virtio_ring.h>
|
||||
#include <linux/virtio_pci.h>
|
||||
#include <linux/virtio_pci_legacy.h>
|
||||
#include <uapi/linux/virtio_net.h>
|
||||
|
||||
#define ENI_MSIX_NAME_SIZE 256
|
||||
|
||||
#define ENI_ERR(pdev, fmt, ...) \
|
||||
dev_err(&pdev->dev, "%s"fmt, "eni_vdpa: ", ##__VA_ARGS__)
|
||||
#define ENI_DBG(pdev, fmt, ...) \
|
||||
dev_dbg(&pdev->dev, "%s"fmt, "eni_vdpa: ", ##__VA_ARGS__)
|
||||
#define ENI_INFO(pdev, fmt, ...) \
|
||||
dev_info(&pdev->dev, "%s"fmt, "eni_vdpa: ", ##__VA_ARGS__)
|
||||
|
||||
struct eni_vring {
|
||||
void __iomem *notify;
|
||||
char msix_name[ENI_MSIX_NAME_SIZE];
|
||||
struct vdpa_callback cb;
|
||||
int irq;
|
||||
};
|
||||
|
||||
struct eni_vdpa {
|
||||
struct vdpa_device vdpa;
|
||||
struct virtio_pci_legacy_device ldev;
|
||||
struct eni_vring *vring;
|
||||
struct vdpa_callback config_cb;
|
||||
char msix_name[ENI_MSIX_NAME_SIZE];
|
||||
int config_irq;
|
||||
int queues;
|
||||
int vectors;
|
||||
};
|
||||
|
||||
static struct eni_vdpa *vdpa_to_eni(struct vdpa_device *vdpa)
|
||||
{
|
||||
return container_of(vdpa, struct eni_vdpa, vdpa);
|
||||
}
|
||||
|
||||
static struct virtio_pci_legacy_device *vdpa_to_ldev(struct vdpa_device *vdpa)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = vdpa_to_eni(vdpa);
|
||||
|
||||
return &eni_vdpa->ldev;
|
||||
}
|
||||
|
||||
static u64 eni_vdpa_get_features(struct vdpa_device *vdpa)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
u64 features = vp_legacy_get_features(ldev);
|
||||
|
||||
features |= BIT_ULL(VIRTIO_F_ACCESS_PLATFORM);
|
||||
features |= BIT_ULL(VIRTIO_F_ORDER_PLATFORM);
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
static int eni_vdpa_set_features(struct vdpa_device *vdpa, u64 features)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
|
||||
if (!(features & BIT_ULL(VIRTIO_NET_F_MRG_RXBUF)) && features) {
|
||||
ENI_ERR(ldev->pci_dev,
|
||||
"VIRTIO_NET_F_MRG_RXBUF is not negotiated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vp_legacy_set_features(ldev, (u32)features);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 eni_vdpa_get_status(struct vdpa_device *vdpa)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
|
||||
return vp_legacy_get_status(ldev);
|
||||
}
|
||||
|
||||
static int eni_vdpa_get_vq_irq(struct vdpa_device *vdpa, u16 idx)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = vdpa_to_eni(vdpa);
|
||||
int irq = eni_vdpa->vring[idx].irq;
|
||||
|
||||
if (irq == VIRTIO_MSI_NO_VECTOR)
|
||||
return -EINVAL;
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static void eni_vdpa_free_irq(struct eni_vdpa *eni_vdpa)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = &eni_vdpa->ldev;
|
||||
struct pci_dev *pdev = ldev->pci_dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < eni_vdpa->queues; i++) {
|
||||
if (eni_vdpa->vring[i].irq != VIRTIO_MSI_NO_VECTOR) {
|
||||
vp_legacy_queue_vector(ldev, i, VIRTIO_MSI_NO_VECTOR);
|
||||
devm_free_irq(&pdev->dev, eni_vdpa->vring[i].irq,
|
||||
&eni_vdpa->vring[i]);
|
||||
eni_vdpa->vring[i].irq = VIRTIO_MSI_NO_VECTOR;
|
||||
}
|
||||
}
|
||||
|
||||
if (eni_vdpa->config_irq != VIRTIO_MSI_NO_VECTOR) {
|
||||
vp_legacy_config_vector(ldev, VIRTIO_MSI_NO_VECTOR);
|
||||
devm_free_irq(&pdev->dev, eni_vdpa->config_irq, eni_vdpa);
|
||||
eni_vdpa->config_irq = VIRTIO_MSI_NO_VECTOR;
|
||||
}
|
||||
|
||||
if (eni_vdpa->vectors) {
|
||||
pci_free_irq_vectors(pdev);
|
||||
eni_vdpa->vectors = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t eni_vdpa_vq_handler(int irq, void *arg)
|
||||
{
|
||||
struct eni_vring *vring = arg;
|
||||
|
||||
if (vring->cb.callback)
|
||||
return vring->cb.callback(vring->cb.private);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t eni_vdpa_config_handler(int irq, void *arg)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = arg;
|
||||
|
||||
if (eni_vdpa->config_cb.callback)
|
||||
return eni_vdpa->config_cb.callback(eni_vdpa->config_cb.private);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int eni_vdpa_request_irq(struct eni_vdpa *eni_vdpa)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = &eni_vdpa->ldev;
|
||||
struct pci_dev *pdev = ldev->pci_dev;
|
||||
int i, ret, irq;
|
||||
int queues = eni_vdpa->queues;
|
||||
int vectors = queues + 1;
|
||||
|
||||
ret = pci_alloc_irq_vectors(pdev, vectors, vectors, PCI_IRQ_MSIX);
|
||||
if (ret != vectors) {
|
||||
ENI_ERR(pdev,
|
||||
"failed to allocate irq vectors want %d but %d\n",
|
||||
vectors, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
eni_vdpa->vectors = vectors;
|
||||
|
||||
for (i = 0; i < queues; i++) {
|
||||
snprintf(eni_vdpa->vring[i].msix_name, ENI_MSIX_NAME_SIZE,
|
||||
"eni-vdpa[%s]-%d\n", pci_name(pdev), i);
|
||||
irq = pci_irq_vector(pdev, i);
|
||||
ret = devm_request_irq(&pdev->dev, irq,
|
||||
eni_vdpa_vq_handler,
|
||||
0, eni_vdpa->vring[i].msix_name,
|
||||
&eni_vdpa->vring[i]);
|
||||
if (ret) {
|
||||
ENI_ERR(pdev, "failed to request irq for vq %d\n", i);
|
||||
goto err;
|
||||
}
|
||||
vp_legacy_queue_vector(ldev, i, i);
|
||||
eni_vdpa->vring[i].irq = irq;
|
||||
}
|
||||
|
||||
snprintf(eni_vdpa->msix_name, ENI_MSIX_NAME_SIZE, "eni-vdpa[%s]-config\n",
|
||||
pci_name(pdev));
|
||||
irq = pci_irq_vector(pdev, queues);
|
||||
ret = devm_request_irq(&pdev->dev, irq, eni_vdpa_config_handler, 0,
|
||||
eni_vdpa->msix_name, eni_vdpa);
|
||||
if (ret) {
|
||||
ENI_ERR(pdev, "failed to request irq for config vq %d\n", i);
|
||||
goto err;
|
||||
}
|
||||
vp_legacy_config_vector(ldev, queues);
|
||||
eni_vdpa->config_irq = irq;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
eni_vdpa_free_irq(eni_vdpa);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void eni_vdpa_set_status(struct vdpa_device *vdpa, u8 status)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = vdpa_to_eni(vdpa);
|
||||
struct virtio_pci_legacy_device *ldev = &eni_vdpa->ldev;
|
||||
u8 s = eni_vdpa_get_status(vdpa);
|
||||
|
||||
if (status & VIRTIO_CONFIG_S_DRIVER_OK &&
|
||||
!(s & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
eni_vdpa_request_irq(eni_vdpa);
|
||||
}
|
||||
|
||||
vp_legacy_set_status(ldev, status);
|
||||
|
||||
if (!(status & VIRTIO_CONFIG_S_DRIVER_OK) &&
|
||||
(s & VIRTIO_CONFIG_S_DRIVER_OK))
|
||||
eni_vdpa_free_irq(eni_vdpa);
|
||||
}
|
||||
|
||||
static int eni_vdpa_reset(struct vdpa_device *vdpa)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = vdpa_to_eni(vdpa);
|
||||
struct virtio_pci_legacy_device *ldev = &eni_vdpa->ldev;
|
||||
u8 s = eni_vdpa_get_status(vdpa);
|
||||
|
||||
vp_legacy_set_status(ldev, 0);
|
||||
|
||||
if (s & VIRTIO_CONFIG_S_DRIVER_OK)
|
||||
eni_vdpa_free_irq(eni_vdpa);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 eni_vdpa_get_vq_num_max(struct vdpa_device *vdpa)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
|
||||
return vp_legacy_get_queue_size(ldev, 0);
|
||||
}
|
||||
|
||||
static u16 eni_vdpa_get_vq_num_min(struct vdpa_device *vdpa)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
|
||||
return vp_legacy_get_queue_size(ldev, 0);
|
||||
}
|
||||
|
||||
static int eni_vdpa_get_vq_state(struct vdpa_device *vdpa, u16 qid,
|
||||
struct vdpa_vq_state *state)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int eni_vdpa_set_vq_state(struct vdpa_device *vdpa, u16 qid,
|
||||
const struct vdpa_vq_state *state)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
const struct vdpa_vq_state_split *split = &state->split;
|
||||
|
||||
/* ENI is build upon virtio-pci specfication which not support
|
||||
* to set state of virtqueue. But if the state is equal to the
|
||||
* device initial state by chance, we can let it go.
|
||||
*/
|
||||
if (!vp_legacy_get_queue_enable(ldev, qid)
|
||||
&& split->avail_index == 0)
|
||||
return 0;
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
||||
static void eni_vdpa_set_vq_cb(struct vdpa_device *vdpa, u16 qid,
|
||||
struct vdpa_callback *cb)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = vdpa_to_eni(vdpa);
|
||||
|
||||
eni_vdpa->vring[qid].cb = *cb;
|
||||
}
|
||||
|
||||
static void eni_vdpa_set_vq_ready(struct vdpa_device *vdpa, u16 qid,
|
||||
bool ready)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
|
||||
/* ENI is a legacy virtio-pci device. This is not supported
|
||||
* by specification. But we can disable virtqueue by setting
|
||||
* address to 0.
|
||||
*/
|
||||
if (!ready)
|
||||
vp_legacy_set_queue_address(ldev, qid, 0);
|
||||
}
|
||||
|
||||
static bool eni_vdpa_get_vq_ready(struct vdpa_device *vdpa, u16 qid)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
|
||||
return vp_legacy_get_queue_enable(ldev, qid);
|
||||
}
|
||||
|
||||
static void eni_vdpa_set_vq_num(struct vdpa_device *vdpa, u16 qid,
|
||||
u32 num)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
struct pci_dev *pdev = ldev->pci_dev;
|
||||
u16 n = vp_legacy_get_queue_size(ldev, qid);
|
||||
|
||||
/* ENI is a legacy virtio-pci device which not allow to change
|
||||
* virtqueue size. Just report a error if someone tries to
|
||||
* change it.
|
||||
*/
|
||||
if (num != n)
|
||||
ENI_ERR(pdev,
|
||||
"not support to set vq %u fixed num %u to %u\n",
|
||||
qid, n, num);
|
||||
}
|
||||
|
||||
static int eni_vdpa_set_vq_address(struct vdpa_device *vdpa, u16 qid,
|
||||
u64 desc_area, u64 driver_area,
|
||||
u64 device_area)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
u32 pfn = desc_area >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
|
||||
|
||||
vp_legacy_set_queue_address(ldev, qid, pfn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eni_vdpa_kick_vq(struct vdpa_device *vdpa, u16 qid)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = vdpa_to_eni(vdpa);
|
||||
|
||||
iowrite16(qid, eni_vdpa->vring[qid].notify);
|
||||
}
|
||||
|
||||
static u32 eni_vdpa_get_device_id(struct vdpa_device *vdpa)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
|
||||
return ldev->id.device;
|
||||
}
|
||||
|
||||
static u32 eni_vdpa_get_vendor_id(struct vdpa_device *vdpa)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = vdpa_to_ldev(vdpa);
|
||||
|
||||
return ldev->id.vendor;
|
||||
}
|
||||
|
||||
static u32 eni_vdpa_get_vq_align(struct vdpa_device *vdpa)
|
||||
{
|
||||
return VIRTIO_PCI_VRING_ALIGN;
|
||||
}
|
||||
|
||||
static size_t eni_vdpa_get_config_size(struct vdpa_device *vdpa)
|
||||
{
|
||||
return sizeof(struct virtio_net_config);
|
||||
}
|
||||
|
||||
|
||||
static void eni_vdpa_get_config(struct vdpa_device *vdpa,
|
||||
unsigned int offset,
|
||||
void *buf, unsigned int len)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = vdpa_to_eni(vdpa);
|
||||
struct virtio_pci_legacy_device *ldev = &eni_vdpa->ldev;
|
||||
void __iomem *ioaddr = ldev->ioaddr +
|
||||
VIRTIO_PCI_CONFIG_OFF(eni_vdpa->vectors) +
|
||||
offset;
|
||||
u8 *p = buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
*p++ = ioread8(ioaddr + i);
|
||||
}
|
||||
|
||||
static void eni_vdpa_set_config(struct vdpa_device *vdpa,
|
||||
unsigned int offset, const void *buf,
|
||||
unsigned int len)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = vdpa_to_eni(vdpa);
|
||||
struct virtio_pci_legacy_device *ldev = &eni_vdpa->ldev;
|
||||
void __iomem *ioaddr = ldev->ioaddr +
|
||||
VIRTIO_PCI_CONFIG_OFF(eni_vdpa->vectors) +
|
||||
offset;
|
||||
const u8 *p = buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
iowrite8(*p++, ioaddr + i);
|
||||
}
|
||||
|
||||
static void eni_vdpa_set_config_cb(struct vdpa_device *vdpa,
|
||||
struct vdpa_callback *cb)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = vdpa_to_eni(vdpa);
|
||||
|
||||
eni_vdpa->config_cb = *cb;
|
||||
}
|
||||
|
||||
static const struct vdpa_config_ops eni_vdpa_ops = {
|
||||
.get_features = eni_vdpa_get_features,
|
||||
.set_features = eni_vdpa_set_features,
|
||||
.get_status = eni_vdpa_get_status,
|
||||
.set_status = eni_vdpa_set_status,
|
||||
.reset = eni_vdpa_reset,
|
||||
.get_vq_num_max = eni_vdpa_get_vq_num_max,
|
||||
.get_vq_num_min = eni_vdpa_get_vq_num_min,
|
||||
.get_vq_state = eni_vdpa_get_vq_state,
|
||||
.set_vq_state = eni_vdpa_set_vq_state,
|
||||
.set_vq_cb = eni_vdpa_set_vq_cb,
|
||||
.set_vq_ready = eni_vdpa_set_vq_ready,
|
||||
.get_vq_ready = eni_vdpa_get_vq_ready,
|
||||
.set_vq_num = eni_vdpa_set_vq_num,
|
||||
.set_vq_address = eni_vdpa_set_vq_address,
|
||||
.kick_vq = eni_vdpa_kick_vq,
|
||||
.get_device_id = eni_vdpa_get_device_id,
|
||||
.get_vendor_id = eni_vdpa_get_vendor_id,
|
||||
.get_vq_align = eni_vdpa_get_vq_align,
|
||||
.get_config_size = eni_vdpa_get_config_size,
|
||||
.get_config = eni_vdpa_get_config,
|
||||
.set_config = eni_vdpa_set_config,
|
||||
.set_config_cb = eni_vdpa_set_config_cb,
|
||||
.get_vq_irq = eni_vdpa_get_vq_irq,
|
||||
};
|
||||
|
||||
|
||||
static u16 eni_vdpa_get_num_queues(struct eni_vdpa *eni_vdpa)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = &eni_vdpa->ldev;
|
||||
u32 features = vp_legacy_get_features(ldev);
|
||||
u16 num = 2;
|
||||
|
||||
if (features & BIT_ULL(VIRTIO_NET_F_MQ)) {
|
||||
__virtio16 max_virtqueue_pairs;
|
||||
|
||||
eni_vdpa_get_config(&eni_vdpa->vdpa,
|
||||
offsetof(struct virtio_net_config, max_virtqueue_pairs),
|
||||
&max_virtqueue_pairs,
|
||||
sizeof(max_virtqueue_pairs));
|
||||
num = 2 * __virtio16_to_cpu(virtio_legacy_is_little_endian(),
|
||||
max_virtqueue_pairs);
|
||||
}
|
||||
|
||||
if (features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ))
|
||||
num += 1;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static void eni_vdpa_free_irq_vectors(void *data)
|
||||
{
|
||||
pci_free_irq_vectors(data);
|
||||
}
|
||||
|
||||
static int eni_vdpa_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct eni_vdpa *eni_vdpa;
|
||||
struct virtio_pci_legacy_device *ldev;
|
||||
int ret, i;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
eni_vdpa = vdpa_alloc_device(struct eni_vdpa, vdpa,
|
||||
dev, &eni_vdpa_ops, NULL, false);
|
||||
if (IS_ERR(eni_vdpa)) {
|
||||
ENI_ERR(pdev, "failed to allocate vDPA structure\n");
|
||||
return PTR_ERR(eni_vdpa);
|
||||
}
|
||||
|
||||
ldev = &eni_vdpa->ldev;
|
||||
ldev->pci_dev = pdev;
|
||||
|
||||
ret = vp_legacy_probe(ldev);
|
||||
if (ret) {
|
||||
ENI_ERR(pdev, "failed to probe legacy PCI device\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_set_drvdata(pdev, eni_vdpa);
|
||||
|
||||
eni_vdpa->vdpa.dma_dev = &pdev->dev;
|
||||
eni_vdpa->queues = eni_vdpa_get_num_queues(eni_vdpa);
|
||||
|
||||
ret = devm_add_action_or_reset(dev, eni_vdpa_free_irq_vectors, pdev);
|
||||
if (ret) {
|
||||
ENI_ERR(pdev,
|
||||
"failed for adding devres for freeing irq vectors\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
eni_vdpa->vring = devm_kcalloc(&pdev->dev, eni_vdpa->queues,
|
||||
sizeof(*eni_vdpa->vring),
|
||||
GFP_KERNEL);
|
||||
if (!eni_vdpa->vring) {
|
||||
ret = -ENOMEM;
|
||||
ENI_ERR(pdev, "failed to allocate virtqueues\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < eni_vdpa->queues; i++) {
|
||||
eni_vdpa->vring[i].irq = VIRTIO_MSI_NO_VECTOR;
|
||||
eni_vdpa->vring[i].notify = ldev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY;
|
||||
}
|
||||
eni_vdpa->config_irq = VIRTIO_MSI_NO_VECTOR;
|
||||
|
||||
ret = vdpa_register_device(&eni_vdpa->vdpa, eni_vdpa->queues);
|
||||
if (ret) {
|
||||
ENI_ERR(pdev, "failed to register to vdpa bus\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
put_device(&eni_vdpa->vdpa.dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void eni_vdpa_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct eni_vdpa *eni_vdpa = pci_get_drvdata(pdev);
|
||||
|
||||
vdpa_unregister_device(&eni_vdpa->vdpa);
|
||||
vp_legacy_remove(&eni_vdpa->ldev);
|
||||
}
|
||||
|
||||
static struct pci_device_id eni_pci_ids[] = {
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_REDHAT_QUMRANET,
|
||||
VIRTIO_TRANS_ID_NET,
|
||||
PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
|
||||
VIRTIO_ID_NET) },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
static struct pci_driver eni_vdpa_driver = {
|
||||
.name = "alibaba-eni-vdpa",
|
||||
.id_table = eni_pci_ids,
|
||||
.probe = eni_vdpa_probe,
|
||||
.remove = eni_vdpa_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(eni_vdpa_driver);
|
||||
|
||||
MODULE_AUTHOR("Wu Zongyong <wuzongyong@linux.alibaba.com>");
|
||||
MODULE_DESCRIPTION("Alibaba ENI vDPA driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -63,7 +63,7 @@ struct mlx5_control_vq {
|
||||
unsigned short head;
|
||||
};
|
||||
|
||||
struct mlx5_ctrl_wq_ent {
|
||||
struct mlx5_vdpa_wq_ent {
|
||||
struct work_struct work;
|
||||
struct mlx5_vdpa_dev *mvdev;
|
||||
};
|
||||
|
||||
@@ -159,8 +159,9 @@ struct mlx5_vdpa_net {
|
||||
struct mlx5_fc *rx_counter;
|
||||
struct mlx5_flow_handle *rx_rule;
|
||||
bool setup;
|
||||
u16 mtu;
|
||||
u32 cur_num_vqs;
|
||||
struct notifier_block nb;
|
||||
struct vdpa_callback config_cb;
|
||||
};
|
||||
|
||||
static void free_resources(struct mlx5_vdpa_net *ndev);
|
||||
@@ -1557,14 +1558,14 @@ static void mlx5_cvq_kick_handler(struct work_struct *work)
|
||||
{
|
||||
virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
|
||||
struct virtio_net_ctrl_hdr ctrl;
|
||||
struct mlx5_ctrl_wq_ent *wqent;
|
||||
struct mlx5_vdpa_wq_ent *wqent;
|
||||
struct mlx5_vdpa_dev *mvdev;
|
||||
struct mlx5_control_vq *cvq;
|
||||
struct mlx5_vdpa_net *ndev;
|
||||
size_t read, write;
|
||||
int err;
|
||||
|
||||
wqent = container_of(work, struct mlx5_ctrl_wq_ent, work);
|
||||
wqent = container_of(work, struct mlx5_vdpa_wq_ent, work);
|
||||
mvdev = wqent->mvdev;
|
||||
ndev = to_mlx5_vdpa_ndev(mvdev);
|
||||
cvq = &mvdev->cvq;
|
||||
@@ -1616,7 +1617,7 @@ static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx)
|
||||
struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
|
||||
struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
|
||||
struct mlx5_vdpa_virtqueue *mvq;
|
||||
struct mlx5_ctrl_wq_ent *wqent;
|
||||
struct mlx5_vdpa_wq_ent *wqent;
|
||||
|
||||
if (!is_index_valid(mvdev, idx))
|
||||
return;
|
||||
@@ -1852,6 +1853,7 @@ static u64 mlx5_vdpa_get_features(struct vdpa_device *vdev)
|
||||
ndev->mvdev.mlx_features |= BIT_ULL(VIRTIO_NET_F_CTRL_VQ);
|
||||
ndev->mvdev.mlx_features |= BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR);
|
||||
ndev->mvdev.mlx_features |= BIT_ULL(VIRTIO_NET_F_MQ);
|
||||
ndev->mvdev.mlx_features |= BIT_ULL(VIRTIO_NET_F_STATUS);
|
||||
|
||||
print_features(mvdev, ndev->mvdev.mlx_features, false);
|
||||
return ndev->mvdev.mlx_features;
|
||||
@@ -1942,16 +1944,16 @@ static int mlx5_vdpa_set_features(struct vdpa_device *vdev, u64 features)
|
||||
return err;
|
||||
|
||||
ndev->mvdev.actual_features = features & ndev->mvdev.mlx_features;
|
||||
ndev->config.mtu = cpu_to_mlx5vdpa16(mvdev, ndev->mtu);
|
||||
ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
|
||||
update_cvq_info(mvdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mlx5_vdpa_set_config_cb(struct vdpa_device *vdev, struct vdpa_callback *cb)
|
||||
{
|
||||
/* not implemented */
|
||||
mlx5_vdpa_warn(to_mvdev(vdev), "set config callback not supported\n");
|
||||
struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
|
||||
struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
|
||||
|
||||
ndev->config_cb = *cb;
|
||||
}
|
||||
|
||||
#define MLX5_VDPA_MAX_VQ_ENTRIES 256
|
||||
@@ -2404,6 +2406,82 @@ struct mlx5_vdpa_mgmtdev {
|
||||
struct mlx5_vdpa_net *ndev;
|
||||
};
|
||||
|
||||
static u8 query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
|
||||
{
|
||||
u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {};
|
||||
u32 in[MLX5_ST_SZ_DW(query_vport_state_in)] = {};
|
||||
int err;
|
||||
|
||||
MLX5_SET(query_vport_state_in, in, opcode, MLX5_CMD_OP_QUERY_VPORT_STATE);
|
||||
MLX5_SET(query_vport_state_in, in, op_mod, opmod);
|
||||
MLX5_SET(query_vport_state_in, in, vport_number, vport);
|
||||
if (vport)
|
||||
MLX5_SET(query_vport_state_in, in, other_vport, 1);
|
||||
|
||||
err = mlx5_cmd_exec_inout(mdev, query_vport_state, in, out);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
return MLX5_GET(query_vport_state_out, out, state);
|
||||
}
|
||||
|
||||
static bool get_link_state(struct mlx5_vdpa_dev *mvdev)
|
||||
{
|
||||
if (query_vport_state(mvdev->mdev, MLX5_VPORT_STATE_OP_MOD_VNIC_VPORT, 0) ==
|
||||
VPORT_STATE_UP)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void update_carrier(struct work_struct *work)
|
||||
{
|
||||
struct mlx5_vdpa_wq_ent *wqent;
|
||||
struct mlx5_vdpa_dev *mvdev;
|
||||
struct mlx5_vdpa_net *ndev;
|
||||
|
||||
wqent = container_of(work, struct mlx5_vdpa_wq_ent, work);
|
||||
mvdev = wqent->mvdev;
|
||||
ndev = to_mlx5_vdpa_ndev(mvdev);
|
||||
if (get_link_state(mvdev))
|
||||
ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
|
||||
else
|
||||
ndev->config.status &= cpu_to_mlx5vdpa16(mvdev, ~VIRTIO_NET_S_LINK_UP);
|
||||
|
||||
if (ndev->config_cb.callback)
|
||||
ndev->config_cb.callback(ndev->config_cb.private);
|
||||
|
||||
kfree(wqent);
|
||||
}
|
||||
|
||||
static int event_handler(struct notifier_block *nb, unsigned long event, void *param)
|
||||
{
|
||||
struct mlx5_vdpa_net *ndev = container_of(nb, struct mlx5_vdpa_net, nb);
|
||||
struct mlx5_eqe *eqe = param;
|
||||
int ret = NOTIFY_DONE;
|
||||
struct mlx5_vdpa_wq_ent *wqent;
|
||||
|
||||
if (event == MLX5_EVENT_TYPE_PORT_CHANGE) {
|
||||
switch (eqe->sub_type) {
|
||||
case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
|
||||
case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
|
||||
wqent = kzalloc(sizeof(*wqent), GFP_ATOMIC);
|
||||
if (!wqent)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
wqent->mvdev = &ndev->mvdev;
|
||||
INIT_WORK(&wqent->work, update_carrier);
|
||||
queue_work(ndev->mvdev.wq, &wqent->work);
|
||||
ret = NOTIFY_OK;
|
||||
break;
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name)
|
||||
{
|
||||
struct mlx5_vdpa_mgmtdev *mgtdev = container_of(v_mdev, struct mlx5_vdpa_mgmtdev, mgtdev);
|
||||
@@ -2413,6 +2491,7 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name)
|
||||
struct mlx5_vdpa_net *ndev;
|
||||
struct mlx5_core_dev *mdev;
|
||||
u32 max_vqs;
|
||||
u16 mtu;
|
||||
int err;
|
||||
|
||||
if (mgtdev->ndev)
|
||||
@@ -2440,14 +2519,22 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name)
|
||||
init_mvqs(ndev);
|
||||
mutex_init(&ndev->reslock);
|
||||
config = &ndev->config;
|
||||
err = query_mtu(mdev, &ndev->mtu);
|
||||
err = query_mtu(mdev, &mtu);
|
||||
if (err)
|
||||
goto err_mtu;
|
||||
|
||||
ndev->config.mtu = cpu_to_mlx5vdpa16(mvdev, mtu);
|
||||
ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
|
||||
|
||||
err = mlx5_query_nic_vport_mac_address(mdev, 0, 0, config->mac);
|
||||
if (err)
|
||||
goto err_mtu;
|
||||
|
||||
if (get_link_state(mvdev))
|
||||
ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
|
||||
else
|
||||
ndev->config.status &= cpu_to_mlx5vdpa16(mvdev, ~VIRTIO_NET_S_LINK_UP);
|
||||
|
||||
if (!is_zero_ether_addr(config->mac)) {
|
||||
pfmdev = pci_get_drvdata(pci_physfn(mdev->pdev));
|
||||
err = mlx5_mpfs_add_mac(pfmdev, config->mac);
|
||||
@@ -2473,12 +2560,14 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name)
|
||||
if (err)
|
||||
goto err_mr;
|
||||
|
||||
mvdev->wq = create_singlethread_workqueue("mlx5_vdpa_ctrl_wq");
|
||||
mvdev->wq = create_singlethread_workqueue("mlx5_vdpa_wq");
|
||||
if (!mvdev->wq) {
|
||||
err = -ENOMEM;
|
||||
goto err_res2;
|
||||
}
|
||||
|
||||
ndev->nb.notifier_call = event_handler;
|
||||
mlx5_notifier_register(mdev, &ndev->nb);
|
||||
ndev->cur_num_vqs = 2 * mlx5_vdpa_max_qps(max_vqs);
|
||||
mvdev->vdev.mdev = &mgtdev->mgtdev;
|
||||
err = _vdpa_register_device(&mvdev->vdev, ndev->cur_num_vqs + 1);
|
||||
@@ -2509,7 +2598,9 @@ static void mlx5_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev, struct vdpa_device *
|
||||
{
|
||||
struct mlx5_vdpa_mgmtdev *mgtdev = container_of(v_mdev, struct mlx5_vdpa_mgmtdev, mgtdev);
|
||||
struct mlx5_vdpa_dev *mvdev = to_mvdev(dev);
|
||||
struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
|
||||
|
||||
mlx5_notifier_unregister(mvdev->mdev, &ndev->nb);
|
||||
destroy_workqueue(mvdev->wq);
|
||||
_vdpa_unregister_device(dev);
|
||||
mgtdev->ndev = NULL;
|
||||
|
||||
@@ -26,8 +26,16 @@ static int vdpa_dev_probe(struct device *d)
|
||||
{
|
||||
struct vdpa_device *vdev = dev_to_vdpa(d);
|
||||
struct vdpa_driver *drv = drv_to_vdpa(vdev->dev.driver);
|
||||
const struct vdpa_config_ops *ops = vdev->config;
|
||||
u32 max_num, min_num = 1;
|
||||
int ret = 0;
|
||||
|
||||
max_num = ops->get_vq_num_max(vdev);
|
||||
if (ops->get_vq_num_min)
|
||||
min_num = ops->get_vq_num_min(vdev);
|
||||
if (max_num < min_num)
|
||||
return -EINVAL;
|
||||
|
||||
if (drv && drv->probe)
|
||||
ret = drv->probe(vdev);
|
||||
|
||||
@@ -492,6 +500,7 @@ vdpa_dev_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, u32 seq
|
||||
int flags, struct netlink_ext_ack *extack)
|
||||
{
|
||||
u16 max_vq_size;
|
||||
u16 min_vq_size = 1;
|
||||
u32 device_id;
|
||||
u32 vendor_id;
|
||||
void *hdr;
|
||||
@@ -508,6 +517,8 @@ vdpa_dev_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, u32 seq
|
||||
device_id = vdev->config->get_device_id(vdev);
|
||||
vendor_id = vdev->config->get_vendor_id(vdev);
|
||||
max_vq_size = vdev->config->get_vq_num_max(vdev);
|
||||
if (vdev->config->get_vq_num_min)
|
||||
min_vq_size = vdev->config->get_vq_num_min(vdev);
|
||||
|
||||
err = -EMSGSIZE;
|
||||
if (nla_put_string(msg, VDPA_ATTR_DEV_NAME, dev_name(&vdev->dev)))
|
||||
@@ -520,6 +531,8 @@ vdpa_dev_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, u32 seq
|
||||
goto msg_err;
|
||||
if (nla_put_u16(msg, VDPA_ATTR_DEV_MAX_VQ_SIZE, max_vq_size))
|
||||
goto msg_err;
|
||||
if (nla_put_u16(msg, VDPA_ATTR_DEV_MIN_VQ_SIZE, min_vq_size))
|
||||
goto msg_err;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return 0;
|
||||
|
||||
@@ -76,6 +76,17 @@ static u8 vp_vdpa_get_status(struct vdpa_device *vdpa)
|
||||
return vp_modern_get_status(mdev);
|
||||
}
|
||||
|
||||
static int vp_vdpa_get_vq_irq(struct vdpa_device *vdpa, u16 idx)
|
||||
{
|
||||
struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
|
||||
int irq = vp_vdpa->vring[idx].irq;
|
||||
|
||||
if (irq == VIRTIO_MSI_NO_VECTOR)
|
||||
return -EINVAL;
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static void vp_vdpa_free_irq(struct vp_vdpa *vp_vdpa)
|
||||
{
|
||||
struct virtio_pci_modern_device *mdev = &vp_vdpa->mdev;
|
||||
@@ -427,6 +438,7 @@ static const struct vdpa_config_ops vp_vdpa_ops = {
|
||||
.get_config = vp_vdpa_get_config,
|
||||
.set_config = vp_vdpa_set_config,
|
||||
.set_config_cb = vp_vdpa_set_config_cb,
|
||||
.get_vq_irq = vp_vdpa_get_vq_irq,
|
||||
};
|
||||
|
||||
static void vp_vdpa_free_irq_vectors(void *data)
|
||||
|
||||
@@ -20,6 +20,15 @@ config VIRTIO_PCI_LIB
|
||||
PCI device with possible vendor specific extensions. Any
|
||||
module that selects this module must depend on PCI.
|
||||
|
||||
config VIRTIO_PCI_LIB_LEGACY
|
||||
tristate
|
||||
help
|
||||
Legacy PCI device (Virtio PCI Card 0.9.x Draft and older device)
|
||||
implementation.
|
||||
This module implements the basic probe and control for devices
|
||||
which are based on legacy PCI device. Any module that selects this
|
||||
module must depend on PCI.
|
||||
|
||||
menuconfig VIRTIO_MENU
|
||||
bool "Virtio drivers"
|
||||
default y
|
||||
@@ -43,6 +52,7 @@ config VIRTIO_PCI_LEGACY
|
||||
bool "Support for legacy virtio draft 0.9.X and older devices"
|
||||
default y
|
||||
depends on VIRTIO_PCI
|
||||
select VIRTIO_PCI_LIB_LEGACY
|
||||
help
|
||||
Virtio PCI Card 0.9.X Draft (circa 2014) and older device support.
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_VIRTIO) += virtio.o virtio_ring.o
|
||||
obj-$(CONFIG_VIRTIO_PCI_LIB) += virtio_pci_modern_dev.o
|
||||
obj-$(CONFIG_VIRTIO_PCI_LIB_LEGACY) += virtio_pci_legacy_dev.o
|
||||
obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
|
||||
obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
|
||||
virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
|
||||
|
||||
@@ -549,6 +549,8 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
|
||||
|
||||
pci_set_master(pci_dev);
|
||||
|
||||
vp_dev->is_legacy = vp_dev->ldev.ioaddr ? true : false;
|
||||
|
||||
rc = register_virtio_device(&vp_dev->vdev);
|
||||
reg_dev = vp_dev;
|
||||
if (rc)
|
||||
@@ -557,10 +559,10 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
|
||||
return 0;
|
||||
|
||||
err_register:
|
||||
if (vp_dev->ioaddr)
|
||||
virtio_pci_legacy_remove(vp_dev);
|
||||
if (vp_dev->is_legacy)
|
||||
virtio_pci_legacy_remove(vp_dev);
|
||||
else
|
||||
virtio_pci_modern_remove(vp_dev);
|
||||
virtio_pci_modern_remove(vp_dev);
|
||||
err_probe:
|
||||
pci_disable_device(pci_dev);
|
||||
err_enable_device:
|
||||
@@ -587,7 +589,7 @@ static void virtio_pci_remove(struct pci_dev *pci_dev)
|
||||
|
||||
unregister_virtio_device(&vp_dev->vdev);
|
||||
|
||||
if (vp_dev->ioaddr)
|
||||
if (vp_dev->is_legacy)
|
||||
virtio_pci_legacy_remove(vp_dev);
|
||||
else
|
||||
virtio_pci_modern_remove(vp_dev);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/virtio_ring.h>
|
||||
#include <linux/virtio_pci.h>
|
||||
#include <linux/virtio_pci_legacy.h>
|
||||
#include <linux/virtio_pci_modern.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/spinlock.h>
|
||||
@@ -44,16 +45,14 @@ struct virtio_pci_vq_info {
|
||||
struct virtio_pci_device {
|
||||
struct virtio_device vdev;
|
||||
struct pci_dev *pci_dev;
|
||||
struct virtio_pci_legacy_device ldev;
|
||||
struct virtio_pci_modern_device mdev;
|
||||
|
||||
/* In legacy mode, these two point to within ->legacy. */
|
||||
bool is_legacy;
|
||||
|
||||
/* Where to read and clear interrupt */
|
||||
u8 __iomem *isr;
|
||||
|
||||
/* Legacy only field */
|
||||
/* the IO mapping for the PCI config space */
|
||||
void __iomem *ioaddr;
|
||||
|
||||
/* a list of queues so we can dispatch IRQs */
|
||||
spinlock_t lock;
|
||||
struct list_head virtqueues;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* Michael S. Tsirkin <mst@redhat.com>
|
||||
*/
|
||||
|
||||
#include "linux/virtio_pci_legacy.h"
|
||||
#include "virtio_pci_common.h"
|
||||
|
||||
/* virtio config->get_features() implementation */
|
||||
@@ -23,7 +24,7 @@ static u64 vp_get_features(struct virtio_device *vdev)
|
||||
|
||||
/* When someone needs more than 32 feature bits, we'll need to
|
||||
* steal a bit to indicate that the rest are somewhere else. */
|
||||
return ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES);
|
||||
return vp_legacy_get_features(&vp_dev->ldev);
|
||||
}
|
||||
|
||||
/* virtio config->finalize_features() implementation */
|
||||
@@ -38,7 +39,7 @@ static int vp_finalize_features(struct virtio_device *vdev)
|
||||
BUG_ON((u32)vdev->features != vdev->features);
|
||||
|
||||
/* We only support 32 feature bits. */
|
||||
iowrite32(vdev->features, vp_dev->ioaddr + VIRTIO_PCI_GUEST_FEATURES);
|
||||
vp_legacy_set_features(&vp_dev->ldev, vdev->features);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -48,7 +49,7 @@ static void vp_get(struct virtio_device *vdev, unsigned offset,
|
||||
void *buf, unsigned len)
|
||||
{
|
||||
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
|
||||
void __iomem *ioaddr = vp_dev->ioaddr +
|
||||
void __iomem *ioaddr = vp_dev->ldev.ioaddr +
|
||||
VIRTIO_PCI_CONFIG_OFF(vp_dev->msix_enabled) +
|
||||
offset;
|
||||
u8 *ptr = buf;
|
||||
@@ -64,7 +65,7 @@ static void vp_set(struct virtio_device *vdev, unsigned offset,
|
||||
const void *buf, unsigned len)
|
||||
{
|
||||
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
|
||||
void __iomem *ioaddr = vp_dev->ioaddr +
|
||||
void __iomem *ioaddr = vp_dev->ldev.ioaddr +
|
||||
VIRTIO_PCI_CONFIG_OFF(vp_dev->msix_enabled) +
|
||||
offset;
|
||||
const u8 *ptr = buf;
|
||||
@@ -78,7 +79,7 @@ static void vp_set(struct virtio_device *vdev, unsigned offset,
|
||||
static u8 vp_get_status(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
|
||||
return ioread8(vp_dev->ioaddr + VIRTIO_PCI_STATUS);
|
||||
return vp_legacy_get_status(&vp_dev->ldev);
|
||||
}
|
||||
|
||||
static void vp_set_status(struct virtio_device *vdev, u8 status)
|
||||
@@ -86,28 +87,24 @@ static void vp_set_status(struct virtio_device *vdev, u8 status)
|
||||
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
|
||||
/* We should never be setting status to 0. */
|
||||
BUG_ON(status == 0);
|
||||
iowrite8(status, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
|
||||
vp_legacy_set_status(&vp_dev->ldev, status);
|
||||
}
|
||||
|
||||
static void vp_reset(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
|
||||
/* 0 status means a reset. */
|
||||
iowrite8(0, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
|
||||
vp_legacy_set_status(&vp_dev->ldev, 0);
|
||||
/* Flush out the status write, and flush in device writes,
|
||||
* including MSi-X interrupts, if any. */
|
||||
ioread8(vp_dev->ioaddr + VIRTIO_PCI_STATUS);
|
||||
vp_legacy_get_status(&vp_dev->ldev);
|
||||
/* Flush pending VQ/configuration callbacks. */
|
||||
vp_synchronize_vectors(vdev);
|
||||
}
|
||||
|
||||
static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector)
|
||||
{
|
||||
/* Setup the vector used for configuration events */
|
||||
iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
|
||||
/* Verify we had enough resources to assign the vector */
|
||||
/* Will also flush the write out to device */
|
||||
return ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
|
||||
return vp_legacy_config_vector(&vp_dev->ldev, vector);
|
||||
}
|
||||
|
||||
static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
|
||||
@@ -123,12 +120,9 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
|
||||
int err;
|
||||
u64 q_pfn;
|
||||
|
||||
/* Select the queue we're interested in */
|
||||
iowrite16(index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
|
||||
|
||||
/* Check if queue is either not available or already active. */
|
||||
num = ioread16(vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NUM);
|
||||
if (!num || ioread32(vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN))
|
||||
num = vp_legacy_get_queue_size(&vp_dev->ldev, index);
|
||||
if (!num || vp_legacy_get_queue_enable(&vp_dev->ldev, index))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
info->msix_vector = msix_vec;
|
||||
@@ -151,13 +145,12 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
|
||||
}
|
||||
|
||||
/* activate the queue */
|
||||
iowrite32(q_pfn, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
|
||||
vp_legacy_set_queue_address(&vp_dev->ldev, index, q_pfn);
|
||||
|
||||
vq->priv = (void __force *)vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY;
|
||||
vq->priv = (void __force *)vp_dev->ldev.ioaddr + VIRTIO_PCI_QUEUE_NOTIFY;
|
||||
|
||||
if (msix_vec != VIRTIO_MSI_NO_VECTOR) {
|
||||
iowrite16(msix_vec, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
|
||||
msix_vec = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
|
||||
msix_vec = vp_legacy_queue_vector(&vp_dev->ldev, index, msix_vec);
|
||||
if (msix_vec == VIRTIO_MSI_NO_VECTOR) {
|
||||
err = -EBUSY;
|
||||
goto out_deactivate;
|
||||
@@ -167,7 +160,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
|
||||
return vq;
|
||||
|
||||
out_deactivate:
|
||||
iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
|
||||
vp_legacy_set_queue_address(&vp_dev->ldev, index, 0);
|
||||
out_del_vq:
|
||||
vring_del_virtqueue(vq);
|
||||
return ERR_PTR(err);
|
||||
@@ -178,17 +171,15 @@ static void del_vq(struct virtio_pci_vq_info *info)
|
||||
struct virtqueue *vq = info->vq;
|
||||
struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
|
||||
|
||||
iowrite16(vq->index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
|
||||
|
||||
if (vp_dev->msix_enabled) {
|
||||
iowrite16(VIRTIO_MSI_NO_VECTOR,
|
||||
vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
|
||||
vp_legacy_queue_vector(&vp_dev->ldev, vq->index,
|
||||
VIRTIO_MSI_NO_VECTOR);
|
||||
/* Flush the write out to device */
|
||||
ioread8(vp_dev->ioaddr + VIRTIO_PCI_ISR);
|
||||
ioread8(vp_dev->ldev.ioaddr + VIRTIO_PCI_ISR);
|
||||
}
|
||||
|
||||
/* Select and deactivate the queue */
|
||||
iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
|
||||
vp_legacy_set_queue_address(&vp_dev->ldev, vq->index, 0);
|
||||
|
||||
vring_del_virtqueue(vq);
|
||||
}
|
||||
@@ -211,51 +202,18 @@ static const struct virtio_config_ops virtio_pci_config_ops = {
|
||||
/* the PCI probing function */
|
||||
int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev)
|
||||
{
|
||||
struct virtio_pci_legacy_device *ldev = &vp_dev->ldev;
|
||||
struct pci_dev *pci_dev = vp_dev->pci_dev;
|
||||
int rc;
|
||||
|
||||
/* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */
|
||||
if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f)
|
||||
return -ENODEV;
|
||||
ldev->pci_dev = pci_dev;
|
||||
|
||||
if (pci_dev->revision != VIRTIO_PCI_ABI_VERSION) {
|
||||
printk(KERN_ERR "virtio_pci: expected ABI version %d, got %d\n",
|
||||
VIRTIO_PCI_ABI_VERSION, pci_dev->revision);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
|
||||
if (rc) {
|
||||
rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32));
|
||||
} else {
|
||||
/*
|
||||
* The virtio ring base address is expressed as a 32-bit PFN,
|
||||
* with a page size of 1 << VIRTIO_PCI_QUEUE_ADDR_SHIFT.
|
||||
*/
|
||||
dma_set_coherent_mask(&pci_dev->dev,
|
||||
DMA_BIT_MASK(32 + VIRTIO_PCI_QUEUE_ADDR_SHIFT));
|
||||
}
|
||||
|
||||
if (rc)
|
||||
dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA. Trying to continue, but this might not work.\n");
|
||||
|
||||
rc = pci_request_region(pci_dev, 0, "virtio-pci-legacy");
|
||||
rc = vp_legacy_probe(ldev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = -ENOMEM;
|
||||
vp_dev->ioaddr = pci_iomap(pci_dev, 0, 0);
|
||||
if (!vp_dev->ioaddr)
|
||||
goto err_iomap;
|
||||
|
||||
vp_dev->isr = vp_dev->ioaddr + VIRTIO_PCI_ISR;
|
||||
|
||||
/* we use the subsystem vendor/device id as the virtio vendor/device
|
||||
* id. this allows us to use the same PCI vendor/device id for all
|
||||
* virtio devices and to identify the particular virtio driver by
|
||||
* the subsystem ids */
|
||||
vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor;
|
||||
vp_dev->vdev.id.device = pci_dev->subsystem_device;
|
||||
vp_dev->isr = ldev->isr;
|
||||
vp_dev->vdev.id = ldev->id;
|
||||
|
||||
vp_dev->vdev.config = &virtio_pci_config_ops;
|
||||
|
||||
@@ -264,16 +222,11 @@ int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev)
|
||||
vp_dev->del_vq = del_vq;
|
||||
|
||||
return 0;
|
||||
|
||||
err_iomap:
|
||||
pci_release_region(pci_dev, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void virtio_pci_legacy_remove(struct virtio_pci_device *vp_dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = vp_dev->pci_dev;
|
||||
struct virtio_pci_legacy_device *ldev = &vp_dev->ldev;
|
||||
|
||||
pci_iounmap(pci_dev, vp_dev->ioaddr);
|
||||
pci_release_region(pci_dev, 0);
|
||||
vp_legacy_remove(ldev);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "linux/virtio_pci.h"
|
||||
#include <linux/virtio_pci_legacy.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
|
||||
/*
|
||||
* vp_legacy_probe: probe the legacy virtio pci device, note that the
|
||||
* caller is required to enable PCI device before calling this function.
|
||||
* @ldev: the legacy virtio-pci device
|
||||
*
|
||||
* Return 0 on succeed otherwise fail
|
||||
*/
|
||||
int vp_legacy_probe(struct virtio_pci_legacy_device *ldev)
|
||||
{
|
||||
struct pci_dev *pci_dev = ldev->pci_dev;
|
||||
int rc;
|
||||
|
||||
/* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */
|
||||
if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f)
|
||||
return -ENODEV;
|
||||
|
||||
if (pci_dev->revision != VIRTIO_PCI_ABI_VERSION)
|
||||
return -ENODEV;
|
||||
|
||||
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
|
||||
if (rc) {
|
||||
rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32));
|
||||
} else {
|
||||
/*
|
||||
* The virtio ring base address is expressed as a 32-bit PFN,
|
||||
* with a page size of 1 << VIRTIO_PCI_QUEUE_ADDR_SHIFT.
|
||||
*/
|
||||
dma_set_coherent_mask(&pci_dev->dev,
|
||||
DMA_BIT_MASK(32 + VIRTIO_PCI_QUEUE_ADDR_SHIFT));
|
||||
}
|
||||
|
||||
if (rc)
|
||||
dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA. Trying to continue, but this might not work.\n");
|
||||
|
||||
rc = pci_request_region(pci_dev, 0, "virtio-pci-legacy");
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ldev->ioaddr = pci_iomap(pci_dev, 0, 0);
|
||||
if (!ldev->ioaddr)
|
||||
goto err_iomap;
|
||||
|
||||
ldev->isr = ldev->ioaddr + VIRTIO_PCI_ISR;
|
||||
|
||||
ldev->id.vendor = pci_dev->subsystem_vendor;
|
||||
ldev->id.device = pci_dev->subsystem_device;
|
||||
|
||||
return 0;
|
||||
err_iomap:
|
||||
pci_release_region(pci_dev, 0);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_probe);
|
||||
|
||||
/*
|
||||
* vp_legacy_probe: remove and cleanup the legacy virtio pci device
|
||||
* @ldev: the legacy virtio-pci device
|
||||
*/
|
||||
void vp_legacy_remove(struct virtio_pci_legacy_device *ldev)
|
||||
{
|
||||
struct pci_dev *pci_dev = ldev->pci_dev;
|
||||
|
||||
pci_iounmap(pci_dev, ldev->ioaddr);
|
||||
pci_release_region(pci_dev, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_remove);
|
||||
|
||||
/*
|
||||
* vp_legacy_get_features - get features from device
|
||||
* @ldev: the legacy virtio-pci device
|
||||
*
|
||||
* Returns the features read from the device
|
||||
*/
|
||||
u64 vp_legacy_get_features(struct virtio_pci_legacy_device *ldev)
|
||||
{
|
||||
|
||||
return ioread32(ldev->ioaddr + VIRTIO_PCI_HOST_FEATURES);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_get_features);
|
||||
|
||||
/*
|
||||
* vp_legacy_get_driver_features - get driver features from device
|
||||
* @ldev: the legacy virtio-pci device
|
||||
*
|
||||
* Returns the driver features read from the device
|
||||
*/
|
||||
u64 vp_legacy_get_driver_features(struct virtio_pci_legacy_device *ldev)
|
||||
{
|
||||
return ioread32(ldev->ioaddr + VIRTIO_PCI_GUEST_FEATURES);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_get_driver_features);
|
||||
|
||||
/*
|
||||
* vp_legacy_set_features - set features to device
|
||||
* @ldev: the legacy virtio-pci device
|
||||
* @features: the features set to device
|
||||
*/
|
||||
void vp_legacy_set_features(struct virtio_pci_legacy_device *ldev,
|
||||
u32 features)
|
||||
{
|
||||
iowrite32(features, ldev->ioaddr + VIRTIO_PCI_GUEST_FEATURES);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_set_features);
|
||||
|
||||
/*
|
||||
* vp_legacy_get_status - get the device status
|
||||
* @ldev: the legacy virtio-pci device
|
||||
*
|
||||
* Returns the status read from device
|
||||
*/
|
||||
u8 vp_legacy_get_status(struct virtio_pci_legacy_device *ldev)
|
||||
{
|
||||
return ioread8(ldev->ioaddr + VIRTIO_PCI_STATUS);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_get_status);
|
||||
|
||||
/*
|
||||
* vp_legacy_set_status - set status to device
|
||||
* @ldev: the legacy virtio-pci device
|
||||
* @status: the status set to device
|
||||
*/
|
||||
void vp_legacy_set_status(struct virtio_pci_legacy_device *ldev,
|
||||
u8 status)
|
||||
{
|
||||
iowrite8(status, ldev->ioaddr + VIRTIO_PCI_STATUS);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_set_status);
|
||||
|
||||
/*
|
||||
* vp_legacy_queue_vector - set the MSIX vector for a specific virtqueue
|
||||
* @ldev: the legacy virtio-pci device
|
||||
* @index: queue index
|
||||
* @vector: the config vector
|
||||
*
|
||||
* Returns the config vector read from the device
|
||||
*/
|
||||
u16 vp_legacy_queue_vector(struct virtio_pci_legacy_device *ldev,
|
||||
u16 index, u16 vector)
|
||||
{
|
||||
iowrite16(index, ldev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
|
||||
iowrite16(vector, ldev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
|
||||
/* Flush the write out to device */
|
||||
return ioread16(ldev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_queue_vector);
|
||||
|
||||
/*
|
||||
* vp_legacy_config_vector - set the vector for config interrupt
|
||||
* @ldev: the legacy virtio-pci device
|
||||
* @vector: the config vector
|
||||
*
|
||||
* Returns the config vector read from the device
|
||||
*/
|
||||
u16 vp_legacy_config_vector(struct virtio_pci_legacy_device *ldev,
|
||||
u16 vector)
|
||||
{
|
||||
/* Setup the vector used for configuration events */
|
||||
iowrite16(vector, ldev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
|
||||
/* Verify we had enough resources to assign the vector */
|
||||
/* Will also flush the write out to device */
|
||||
return ioread16(ldev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_config_vector);
|
||||
|
||||
/*
|
||||
* vp_legacy_set_queue_address - set the virtqueue address
|
||||
* @ldev: the legacy virtio-pci device
|
||||
* @index: the queue index
|
||||
* @queue_pfn: pfn of the virtqueue
|
||||
*/
|
||||
void vp_legacy_set_queue_address(struct virtio_pci_legacy_device *ldev,
|
||||
u16 index, u32 queue_pfn)
|
||||
{
|
||||
iowrite16(index, ldev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
|
||||
iowrite32(queue_pfn, ldev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_set_queue_address);
|
||||
|
||||
/*
|
||||
* vp_legacy_get_queue_enable - enable a virtqueue
|
||||
* @ldev: the legacy virtio-pci device
|
||||
* @index: the queue index
|
||||
*
|
||||
* Returns whether a virtqueue is enabled or not
|
||||
*/
|
||||
bool vp_legacy_get_queue_enable(struct virtio_pci_legacy_device *ldev,
|
||||
u16 index)
|
||||
{
|
||||
iowrite16(index, ldev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
|
||||
return ioread32(ldev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_get_queue_enable);
|
||||
|
||||
/*
|
||||
* vp_legacy_get_queue_size - get size for a virtqueue
|
||||
* @ldev: the legacy virtio-pci device
|
||||
* @index: the queue index
|
||||
*
|
||||
* Returns the size of the virtqueue
|
||||
*/
|
||||
u16 vp_legacy_get_queue_size(struct virtio_pci_legacy_device *ldev,
|
||||
u16 index)
|
||||
{
|
||||
iowrite16(index, ldev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
|
||||
return ioread16(ldev->ioaddr + VIRTIO_PCI_QUEUE_NUM);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vp_legacy_get_queue_size);
|
||||
|
||||
MODULE_VERSION("0.1");
|
||||
MODULE_DESCRIPTION("Legacy Virtio PCI Device");
|
||||
MODULE_AUTHOR("Wu Zongyong <wuzongyong@linux.alibaba.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -1050,12 +1050,12 @@ static struct vring_packed_desc *alloc_indirect_packed(unsigned int total_sg,
|
||||
}
|
||||
|
||||
static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq,
|
||||
struct scatterlist *sgs[],
|
||||
unsigned int total_sg,
|
||||
unsigned int out_sgs,
|
||||
unsigned int in_sgs,
|
||||
void *data,
|
||||
gfp_t gfp)
|
||||
struct scatterlist *sgs[],
|
||||
unsigned int total_sg,
|
||||
unsigned int out_sgs,
|
||||
unsigned int in_sgs,
|
||||
void *data,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct vring_packed_desc *desc;
|
||||
struct scatterlist *sg;
|
||||
@@ -1065,6 +1065,8 @@ static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq,
|
||||
|
||||
head = vq->packed.next_avail_idx;
|
||||
desc = alloc_indirect_packed(total_sg, gfp);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
if (unlikely(vq->vq.num_free < 1)) {
|
||||
pr_debug("Can't add buf len 1 - avail = 0\n");
|
||||
@@ -1176,6 +1178,7 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq,
|
||||
unsigned int i, n, c, descs_used, err_idx;
|
||||
__le16 head_flags, flags;
|
||||
u16 head, id, prev, curr, avail_used_flags;
|
||||
int err;
|
||||
|
||||
START_USE(vq);
|
||||
|
||||
@@ -1191,9 +1194,14 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq,
|
||||
|
||||
BUG_ON(total_sg == 0);
|
||||
|
||||
if (virtqueue_use_indirect(_vq, total_sg))
|
||||
return virtqueue_add_indirect_packed(vq, sgs, total_sg,
|
||||
out_sgs, in_sgs, data, gfp);
|
||||
if (virtqueue_use_indirect(_vq, total_sg)) {
|
||||
err = virtqueue_add_indirect_packed(vq, sgs, total_sg, out_sgs,
|
||||
in_sgs, data, gfp);
|
||||
if (err != -ENOMEM)
|
||||
return err;
|
||||
|
||||
/* fall back on direct */
|
||||
}
|
||||
|
||||
head = vq->packed.next_avail_idx;
|
||||
avail_used_flags = vq->packed.avail_used_flags;
|
||||
|
||||
@@ -145,7 +145,8 @@ virtio_vdpa_setup_vq(struct virtio_device *vdev, unsigned int index,
|
||||
/* Assume split virtqueue, switch to packed if necessary */
|
||||
struct vdpa_vq_state state = {0};
|
||||
unsigned long flags;
|
||||
u32 align, num;
|
||||
u32 align, max_num, min_num = 1;
|
||||
bool may_reduce_num = true;
|
||||
int err;
|
||||
|
||||
if (!name)
|
||||
@@ -163,16 +164,21 @@ virtio_vdpa_setup_vq(struct virtio_device *vdev, unsigned int index,
|
||||
if (!info)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
num = ops->get_vq_num_max(vdpa);
|
||||
if (num == 0) {
|
||||
max_num = ops->get_vq_num_max(vdpa);
|
||||
if (max_num == 0) {
|
||||
err = -ENOENT;
|
||||
goto error_new_virtqueue;
|
||||
}
|
||||
|
||||
if (ops->get_vq_num_min)
|
||||
min_num = ops->get_vq_num_min(vdpa);
|
||||
|
||||
may_reduce_num = (max_num == min_num) ? false : true;
|
||||
|
||||
/* Create the vring */
|
||||
align = ops->get_vq_align(vdpa);
|
||||
vq = vring_create_virtqueue(index, num, align, vdev,
|
||||
true, true, ctx,
|
||||
vq = vring_create_virtqueue(index, max_num, align, vdev,
|
||||
true, may_reduce_num, ctx,
|
||||
virtio_vdpa_notify, callback, name);
|
||||
if (!vq) {
|
||||
err = -ENOMEM;
|
||||
|
||||
@@ -171,6 +171,9 @@ struct vdpa_map_file {
|
||||
* @get_vq_num_max: Get the max size of virtqueue
|
||||
* @vdev: vdpa device
|
||||
* Returns u16: max size of virtqueue
|
||||
* @get_vq_num_min: Get the min size of virtqueue (optional)
|
||||
* @vdev: vdpa device
|
||||
* Returns u16: min size of virtqueue
|
||||
* @get_device_id: Get virtio device id
|
||||
* @vdev: vdpa device
|
||||
* Returns u32: virtio device id
|
||||
@@ -257,7 +260,7 @@ struct vdpa_config_ops {
|
||||
struct vdpa_notification_area
|
||||
(*get_vq_notification)(struct vdpa_device *vdev, u16 idx);
|
||||
/* vq irq is not expected to be changed once DRIVER_OK is set */
|
||||
int (*get_vq_irq)(struct vdpa_device *vdv, u16 idx);
|
||||
int (*get_vq_irq)(struct vdpa_device *vdev, u16 idx);
|
||||
|
||||
/* Device ops */
|
||||
u32 (*get_vq_align)(struct vdpa_device *vdev);
|
||||
@@ -266,6 +269,7 @@ struct vdpa_config_ops {
|
||||
void (*set_config_cb)(struct vdpa_device *vdev,
|
||||
struct vdpa_callback *cb);
|
||||
u16 (*get_vq_num_max)(struct vdpa_device *vdev);
|
||||
u16 (*get_vq_num_min)(struct vdpa_device *vdev);
|
||||
u32 (*get_device_id)(struct vdpa_device *vdev);
|
||||
u32 (*get_vendor_id)(struct vdpa_device *vdev);
|
||||
u8 (*get_status)(struct vdpa_device *vdev);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_VIRTIO_PCI_LEGACY_H
|
||||
#define _LINUX_VIRTIO_PCI_LEGACY_H
|
||||
|
||||
#include "linux/mod_devicetable.h"
|
||||
#include <linux/pci.h>
|
||||
#include <linux/virtio_pci.h>
|
||||
|
||||
struct virtio_pci_legacy_device {
|
||||
struct pci_dev *pci_dev;
|
||||
|
||||
/* Where to read and clear interrupt */
|
||||
u8 __iomem *isr;
|
||||
/* The IO mapping for the PCI config space (legacy mode only) */
|
||||
void __iomem *ioaddr;
|
||||
|
||||
struct virtio_device_id id;
|
||||
};
|
||||
|
||||
u64 vp_legacy_get_features(struct virtio_pci_legacy_device *ldev);
|
||||
u64 vp_legacy_get_driver_features(struct virtio_pci_legacy_device *ldev);
|
||||
void vp_legacy_set_features(struct virtio_pci_legacy_device *ldev,
|
||||
u32 features);
|
||||
u8 vp_legacy_get_status(struct virtio_pci_legacy_device *ldev);
|
||||
void vp_legacy_set_status(struct virtio_pci_legacy_device *ldev,
|
||||
u8 status);
|
||||
u16 vp_legacy_queue_vector(struct virtio_pci_legacy_device *ldev,
|
||||
u16 idx, u16 vector);
|
||||
u16 vp_legacy_config_vector(struct virtio_pci_legacy_device *ldev,
|
||||
u16 vector);
|
||||
void vp_legacy_set_queue_address(struct virtio_pci_legacy_device *ldev,
|
||||
u16 index, u32 queue_pfn);
|
||||
bool vp_legacy_get_queue_enable(struct virtio_pci_legacy_device *ldev,
|
||||
u16 idx);
|
||||
void vp_legacy_set_queue_size(struct virtio_pci_legacy_device *ldev,
|
||||
u16 idx, u16 size);
|
||||
u16 vp_legacy_get_queue_size(struct virtio_pci_legacy_device *ldev,
|
||||
u16 idx);
|
||||
int vp_legacy_probe(struct virtio_pci_legacy_device *ldev);
|
||||
void vp_legacy_remove(struct virtio_pci_legacy_device *ldev);
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,7 @@ enum vdpa_attr {
|
||||
VDPA_ATTR_DEV_VENDOR_ID, /* u32 */
|
||||
VDPA_ATTR_DEV_MAX_VQS, /* u32 */
|
||||
VDPA_ATTR_DEV_MAX_VQ_SIZE, /* u16 */
|
||||
VDPA_ATTR_DEV_MIN_VQ_SIZE, /* u16 */
|
||||
|
||||
/* new attributes must be added above here */
|
||||
VDPA_ATTR_MAX,
|
||||
|
||||
Reference in New Issue
Block a user