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

Pull virtio updates from Michael Tsirkin:

 - IRQ bypass support for vdpa and IFC

 - MLX5 vdpa driver

 - Endianness fixes for virtio drivers

 - Misc other fixes

* tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost: (71 commits)
  vdpa/mlx5: fix up endian-ness for mtu
  vdpa: Fix pointer math bug in vdpasim_get_config()
  vdpa/mlx5: Fix pointer math in mlx5_vdpa_get_config()
  vdpa/mlx5: fix memory allocation failure checks
  vdpa/mlx5: Fix uninitialised variable in core/mr.c
  vdpa_sim: init iommu lock
  virtio_config: fix up warnings on parisc
  vdpa/mlx5: Add VDPA driver for supported mlx5 devices
  vdpa/mlx5: Add shared memory registration code
  vdpa/mlx5: Add support library for mlx5 VDPA implementation
  vdpa/mlx5: Add hardware descriptive header file
  vdpa: Modify get_vq_state() to return error code
  net/vdpa: Use struct for set/get vq state
  vdpa: remove hard coded virtq num
  vdpasim: support batch updating
  vhost-vdpa: support IOTLB batching hints
  vhost-vdpa: support get/set backend features
  vhost: generialize backend features setting/getting
  vhost-vdpa: refine ioctl pre-processing
  vDPA: dont change vq irq after DRIVER_OK
  ...
This commit is contained in:
Linus Torvalds
2020-08-11 14:34:17 -07:00
60 changed files with 3888 additions and 410 deletions

View File

@@ -385,7 +385,7 @@ static irqreturn_t vu_req_interrupt(int irq, void *data)
} }
break; break;
case VHOST_USER_SLAVE_IOTLB_MSG: case VHOST_USER_SLAVE_IOTLB_MSG:
/* not supported - VIRTIO_F_IOMMU_PLATFORM */ /* not supported - VIRTIO_F_ACCESS_PLATFORM */
case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG: case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG:
/* not supported - VHOST_USER_PROTOCOL_F_HOST_NOTIFIER */ /* not supported - VHOST_USER_PROTOCOL_F_HOST_NOTIFIER */
default: default:

View File

@@ -10667,11 +10667,17 @@ int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons,
{ {
struct kvm_kernel_irqfd *irqfd = struct kvm_kernel_irqfd *irqfd =
container_of(cons, struct kvm_kernel_irqfd, consumer); container_of(cons, struct kvm_kernel_irqfd, consumer);
int ret;
irqfd->producer = prod; irqfd->producer = prod;
kvm_arch_start_assignment(irqfd->kvm);
ret = kvm_x86_ops.update_pi_irte(irqfd->kvm,
prod->irq, irqfd->gsi, 1);
return kvm_x86_ops.update_pi_irte(irqfd->kvm, if (ret)
prod->irq, irqfd->gsi, 1); kvm_arch_end_assignment(irqfd->kvm);
return ret;
} }
void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons, void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
@@ -10694,6 +10700,8 @@ void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
if (ret) if (ret)
printk(KERN_INFO "irq bypass consumer (token %p) unregistration" printk(KERN_INFO "irq bypass consumer (token %p) unregistration"
" fails: %d\n", irqfd->consumer.token, ret); " fails: %d\n", irqfd->consumer.token, ret);
kvm_arch_end_assignment(irqfd->kvm);
} }
int kvm_arch_update_irqfd_routing(struct kvm *kvm, unsigned int host_irq, int kvm_arch_update_irqfd_routing(struct kvm *kvm, unsigned int host_irq,

View File

@@ -204,8 +204,8 @@ static int virtcrypto_update_status(struct virtio_crypto *vcrypto)
u32 status; u32 status;
int err; int err;
virtio_cread(vcrypto->vdev, virtio_cread_le(vcrypto->vdev,
struct virtio_crypto_config, status, &status); struct virtio_crypto_config, status, &status);
/* /*
* Unknown status bits would be a host error and the driver * Unknown status bits would be a host error and the driver
@@ -323,31 +323,31 @@ static int virtcrypto_probe(struct virtio_device *vdev)
if (!vcrypto) if (!vcrypto)
return -ENOMEM; return -ENOMEM;
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
max_dataqueues, &max_data_queues); max_dataqueues, &max_data_queues);
if (max_data_queues < 1) if (max_data_queues < 1)
max_data_queues = 1; max_data_queues = 1;
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
max_cipher_key_len, &max_cipher_key_len); max_cipher_key_len, &max_cipher_key_len);
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
max_auth_key_len, &max_auth_key_len); max_auth_key_len, &max_auth_key_len);
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
max_size, &max_size); max_size, &max_size);
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
crypto_services, &crypto_services); crypto_services, &crypto_services);
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
cipher_algo_l, &cipher_algo_l); cipher_algo_l, &cipher_algo_l);
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
cipher_algo_h, &cipher_algo_h); cipher_algo_h, &cipher_algo_h);
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
hash_algo, &hash_algo); hash_algo, &hash_algo);
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
mac_algo_l, &mac_algo_l); mac_algo_l, &mac_algo_l);
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
mac_algo_h, &mac_algo_h); mac_algo_h, &mac_algo_h);
virtio_cread(vdev, struct virtio_crypto_config, virtio_cread_le(vdev, struct virtio_crypto_config,
aead_algo, &aead_algo); aead_algo, &aead_algo);
/* Add virtio crypto device to global table */ /* Add virtio crypto device to global table */
err = virtcrypto_devmgr_add_dev(vcrypto); err = virtcrypto_devmgr_add_dev(vcrypto);

View File

@@ -39,8 +39,8 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
u32 events_read, events_clear = 0; u32 events_read, events_clear = 0;
/* read the config space */ /* read the config space */
virtio_cread(vgdev->vdev, struct virtio_gpu_config, virtio_cread_le(vgdev->vdev, struct virtio_gpu_config,
events_read, &events_read); events_read, &events_read);
if (events_read & VIRTIO_GPU_EVENT_DISPLAY) { if (events_read & VIRTIO_GPU_EVENT_DISPLAY) {
if (vgdev->has_edid) if (vgdev->has_edid)
virtio_gpu_cmd_get_edids(vgdev); virtio_gpu_cmd_get_edids(vgdev);
@@ -49,8 +49,8 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
drm_helper_hpd_irq_event(vgdev->ddev); drm_helper_hpd_irq_event(vgdev->ddev);
events_clear |= VIRTIO_GPU_EVENT_DISPLAY; events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
} }
virtio_cwrite(vgdev->vdev, struct virtio_gpu_config, virtio_cwrite_le(vgdev->vdev, struct virtio_gpu_config,
events_clear, &events_clear); events_clear, &events_clear);
} }
static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq, static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
@@ -165,8 +165,8 @@ int virtio_gpu_init(struct drm_device *dev)
} }
/* get display info */ /* get display info */
virtio_cread(vgdev->vdev, struct virtio_gpu_config, virtio_cread_le(vgdev->vdev, struct virtio_gpu_config,
num_scanouts, &num_scanouts); num_scanouts, &num_scanouts);
vgdev->num_scanouts = min_t(uint32_t, num_scanouts, vgdev->num_scanouts = min_t(uint32_t, num_scanouts,
VIRTIO_GPU_MAX_SCANOUTS); VIRTIO_GPU_MAX_SCANOUTS);
if (!vgdev->num_scanouts) { if (!vgdev->num_scanouts) {
@@ -176,8 +176,8 @@ int virtio_gpu_init(struct drm_device *dev)
} }
DRM_INFO("number of scanouts: %d\n", num_scanouts); DRM_INFO("number of scanouts: %d\n", num_scanouts);
virtio_cread(vgdev->vdev, struct virtio_gpu_config, virtio_cread_le(vgdev->vdev, struct virtio_gpu_config,
num_capsets, &num_capsets); num_capsets, &num_capsets);
DRM_INFO("number of cap sets: %d\n", num_capsets); DRM_INFO("number of cap sets: %d\n", num_capsets);
virtio_gpu_modeset_init(vgdev); virtio_gpu_modeset_init(vgdev);

View File

@@ -141,7 +141,7 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
struct virtio_gpu_mem_entry **ents, struct virtio_gpu_mem_entry **ents,
unsigned int *nents) unsigned int *nents)
{ {
bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev); bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo); struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo);
struct scatterlist *sg; struct scatterlist *sg;
int si, ret; int si, ret;

View File

@@ -599,7 +599,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]); struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_to_host_2d *cmd_p; struct virtio_gpu_transfer_to_host_2d *cmd_p;
struct virtio_gpu_vbuffer *vbuf; struct virtio_gpu_vbuffer *vbuf;
bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev); bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo); struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo);
if (use_dma_api) if (use_dma_api)
@@ -1015,7 +1015,7 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]); struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_host_3d *cmd_p; struct virtio_gpu_transfer_host_3d *cmd_p;
struct virtio_gpu_vbuffer *vbuf; struct virtio_gpu_vbuffer *vbuf;
bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev); bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo); struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo);
if (use_dma_api) if (use_dma_api)

View File

@@ -1010,8 +1010,8 @@ static int viommu_probe(struct virtio_device *vdev)
if (ret) if (ret)
return ret; return ret;
virtio_cread(vdev, struct virtio_iommu_config, page_size_mask, virtio_cread_le(vdev, struct virtio_iommu_config, page_size_mask,
&viommu->pgsize_bitmap); &viommu->pgsize_bitmap);
if (!viommu->pgsize_bitmap) { if (!viommu->pgsize_bitmap) {
ret = -EINVAL; ret = -EINVAL;
@@ -1022,25 +1022,25 @@ static int viommu_probe(struct virtio_device *vdev)
viommu->last_domain = ~0U; viommu->last_domain = ~0U;
/* Optional features */ /* Optional features */
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE, virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
struct virtio_iommu_config, input_range.start, struct virtio_iommu_config, input_range.start,
&input_start); &input_start);
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE, virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
struct virtio_iommu_config, input_range.end, struct virtio_iommu_config, input_range.end,
&input_end); &input_end);
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_RANGE, virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_RANGE,
struct virtio_iommu_config, domain_range.start, struct virtio_iommu_config, domain_range.start,
&viommu->first_domain); &viommu->first_domain);
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_RANGE, virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_RANGE,
struct virtio_iommu_config, domain_range.end, struct virtio_iommu_config, domain_range.end,
&viommu->last_domain); &viommu->last_domain);
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_PROBE, virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_PROBE,
struct virtio_iommu_config, probe_size, struct virtio_iommu_config, probe_size,
&viommu->probe_size); &viommu->probe_size);
viommu->geometry = (struct iommu_domain_geometry) { viommu->geometry = (struct iommu_domain_geometry) {
.aperture_start = input_start, .aperture_start = input_start,

View File

@@ -2264,12 +2264,13 @@ static void virtnet_update_settings(struct virtnet_info *vi)
if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_SPEED_DUPLEX)) if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_SPEED_DUPLEX))
return; return;
speed = virtio_cread32(vi->vdev, offsetof(struct virtio_net_config, virtio_cread_le(vi->vdev, struct virtio_net_config, speed, &speed);
speed));
if (ethtool_validate_speed(speed)) if (ethtool_validate_speed(speed))
vi->speed = speed; vi->speed = speed;
duplex = virtio_cread8(vi->vdev, offsetof(struct virtio_net_config,
duplex)); virtio_cread_le(vi->vdev, struct virtio_net_config, duplex, &duplex);
if (ethtool_validate_duplex(duplex)) if (ethtool_validate_duplex(duplex))
vi->duplex = duplex; vi->duplex = duplex;
} }

View File

@@ -58,9 +58,9 @@ static int virtio_pmem_probe(struct virtio_device *vdev)
goto out_err; goto out_err;
} }
virtio_cread(vpmem->vdev, struct virtio_pmem_config, virtio_cread_le(vpmem->vdev, struct virtio_pmem_config,
start, &vpmem->start); start, &vpmem->start);
virtio_cread(vpmem->vdev, struct virtio_pmem_config, virtio_cread_le(vpmem->vdev, struct virtio_pmem_config,
size, &vpmem->size); size, &vpmem->size);
res.start = vpmem->start; res.start = vpmem->start;

View File

@@ -625,7 +625,10 @@ static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring,
vdev_id = VIRTIO_ID_NET; vdev_id = VIRTIO_ID_NET;
hdr_len = sizeof(struct virtio_net_hdr); hdr_len = sizeof(struct virtio_net_hdr);
config = &fifo->vdev[vdev_id]->config.net; config = &fifo->vdev[vdev_id]->config.net;
if (ntohs(hdr.len) > config->mtu + /* A legacy-only interface for now. */
if (ntohs(hdr.len) >
__virtio16_to_cpu(virtio_legacy_is_little_endian(),
config->mtu) +
MLXBF_TMFIFO_NET_L2_OVERHEAD) MLXBF_TMFIFO_NET_L2_OVERHEAD)
return; return;
} else { } else {
@@ -1231,8 +1234,12 @@ static int mlxbf_tmfifo_probe(struct platform_device *pdev)
/* Create the network vdev. */ /* Create the network vdev. */
memset(&net_config, 0, sizeof(net_config)); memset(&net_config, 0, sizeof(net_config));
net_config.mtu = ETH_DATA_LEN;
net_config.status = VIRTIO_NET_S_LINK_UP; /* A legacy-only interface for now. */
net_config.mtu = __cpu_to_virtio16(virtio_legacy_is_little_endian(),
ETH_DATA_LEN);
net_config.status = __cpu_to_virtio16(virtio_legacy_is_little_endian(),
VIRTIO_NET_S_LINK_UP);
mlxbf_tmfifo_get_cfg_mac(net_config.mac); mlxbf_tmfifo_get_cfg_mac(net_config.mac);
rc = mlxbf_tmfifo_create_vdev(dev, fifo, VIRTIO_ID_NET, rc = mlxbf_tmfifo_create_vdev(dev, fifo, VIRTIO_ID_NET,
MLXBF_TMFIFO_NET_FEATURES, &net_config, MLXBF_TMFIFO_NET_FEATURES, &net_config,

View File

@@ -754,14 +754,14 @@ static struct scsi_host_template virtscsi_host_template = {
#define virtscsi_config_get(vdev, fld) \ #define virtscsi_config_get(vdev, fld) \
({ \ ({ \
typeof(((struct virtio_scsi_config *)0)->fld) __val; \ __virtio_native_type(struct virtio_scsi_config, fld) __val; \
virtio_cread(vdev, struct virtio_scsi_config, fld, &__val); \ virtio_cread(vdev, struct virtio_scsi_config, fld, &__val); \
__val; \ __val; \
}) })
#define virtscsi_config_set(vdev, fld, val) \ #define virtscsi_config_set(vdev, fld, val) \
do { \ do { \
typeof(((struct virtio_scsi_config *)0)->fld) __val = (val); \ __virtio_native_type(struct virtio_scsi_config, fld) __val = (val); \
virtio_cwrite(vdev, struct virtio_scsi_config, fld, &__val); \ virtio_cwrite(vdev, struct virtio_scsi_config, fld, &__val); \
} while(0) } while(0)

View File

@@ -29,4 +29,23 @@ config IFCVF
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called ifcvf. be called ifcvf.
config MLX5_VDPA
bool "MLX5 VDPA support library for ConnectX devices"
depends on MLX5_CORE
default n
help
Support library for Mellanox VDPA drivers. Provides code that is
common for all types of VDPA drivers. The following drivers are planned:
net, block.
config MLX5_VDPA_NET
tristate "vDPA driver for ConnectX devices"
depends on MLX5_VDPA
default n
help
VDPA network driver for ConnectX6 and newer. Provides offloading
of virtio net datapath such that descriptors put on the ring will
be executed by the hardware. It also supports a variety of stateless
offloads depending on the actual device used and firmware version.
endif # VDPA endif # VDPA

View File

@@ -2,3 +2,4 @@
obj-$(CONFIG_VDPA) += vdpa.o obj-$(CONFIG_VDPA) += vdpa.o
obj-$(CONFIG_VDPA_SIM) += vdpa_sim/ obj-$(CONFIG_VDPA_SIM) += vdpa_sim/
obj-$(CONFIG_IFCVF) += ifcvf/ obj-$(CONFIG_IFCVF) += ifcvf/
obj-$(CONFIG_MLX5_VDPA) += mlx5/

View File

@@ -272,7 +272,7 @@ static int ifcvf_config_features(struct ifcvf_hw *hw)
return 0; return 0;
} }
u64 ifcvf_get_vq_state(struct ifcvf_hw *hw, u16 qid) u16 ifcvf_get_vq_state(struct ifcvf_hw *hw, u16 qid)
{ {
struct ifcvf_lm_cfg __iomem *ifcvf_lm; struct ifcvf_lm_cfg __iomem *ifcvf_lm;
void __iomem *avail_idx_addr; void __iomem *avail_idx_addr;
@@ -287,7 +287,7 @@ u64 ifcvf_get_vq_state(struct ifcvf_hw *hw, u16 qid)
return last_avail_idx; return last_avail_idx;
} }
int ifcvf_set_vq_state(struct ifcvf_hw *hw, u16 qid, u64 num) int ifcvf_set_vq_state(struct ifcvf_hw *hw, u16 qid, u16 num)
{ {
struct ifcvf_lm_cfg __iomem *ifcvf_lm; struct ifcvf_lm_cfg __iomem *ifcvf_lm;
void __iomem *avail_idx_addr; void __iomem *avail_idx_addr;

View File

@@ -29,7 +29,7 @@
(1ULL << VIRTIO_F_VERSION_1) | \ (1ULL << VIRTIO_F_VERSION_1) | \
(1ULL << VIRTIO_NET_F_STATUS) | \ (1ULL << VIRTIO_NET_F_STATUS) | \
(1ULL << VIRTIO_F_ORDER_PLATFORM) | \ (1ULL << VIRTIO_F_ORDER_PLATFORM) | \
(1ULL << VIRTIO_F_IOMMU_PLATFORM) | \ (1ULL << VIRTIO_F_ACCESS_PLATFORM) | \
(1ULL << VIRTIO_NET_F_MRG_RXBUF)) (1ULL << VIRTIO_NET_F_MRG_RXBUF))
/* Only one queue pair for now. */ /* Only one queue pair for now. */
@@ -116,7 +116,7 @@ void ifcvf_set_status(struct ifcvf_hw *hw, u8 status);
void io_write64_twopart(u64 val, u32 *lo, u32 *hi); void io_write64_twopart(u64 val, u32 *lo, u32 *hi);
void ifcvf_reset(struct ifcvf_hw *hw); void ifcvf_reset(struct ifcvf_hw *hw);
u64 ifcvf_get_features(struct ifcvf_hw *hw); u64 ifcvf_get_features(struct ifcvf_hw *hw);
u64 ifcvf_get_vq_state(struct ifcvf_hw *hw, u16 qid); u16 ifcvf_get_vq_state(struct ifcvf_hw *hw, u16 qid);
int ifcvf_set_vq_state(struct ifcvf_hw *hw, u16 qid, u64 num); int ifcvf_set_vq_state(struct ifcvf_hw *hw, u16 qid, u16 num);
struct ifcvf_adapter *vf_to_adapter(struct ifcvf_hw *hw); struct ifcvf_adapter *vf_to_adapter(struct ifcvf_hw *hw);
#endif /* _IFCVF_H_ */ #endif /* _IFCVF_H_ */

View File

@@ -50,8 +50,10 @@ static void ifcvf_free_irq(struct ifcvf_adapter *adapter, int queues)
int i; int i;
for (i = 0; i < queues; i++) for (i = 0; i < queues; i++) {
devm_free_irq(&pdev->dev, vf->vring[i].irq, &vf->vring[i]); devm_free_irq(&pdev->dev, vf->vring[i].irq, &vf->vring[i]);
vf->vring[i].irq = -EINVAL;
}
ifcvf_free_irq_vectors(pdev); ifcvf_free_irq_vectors(pdev);
} }
@@ -235,19 +237,21 @@ static u16 ifcvf_vdpa_get_vq_num_max(struct vdpa_device *vdpa_dev)
return IFCVF_QUEUE_MAX; return IFCVF_QUEUE_MAX;
} }
static u64 ifcvf_vdpa_get_vq_state(struct vdpa_device *vdpa_dev, u16 qid) static int ifcvf_vdpa_get_vq_state(struct vdpa_device *vdpa_dev, u16 qid,
struct vdpa_vq_state *state)
{ {
struct ifcvf_hw *vf = vdpa_to_vf(vdpa_dev); struct ifcvf_hw *vf = vdpa_to_vf(vdpa_dev);
return ifcvf_get_vq_state(vf, qid); state->avail_index = ifcvf_get_vq_state(vf, qid);
return 0;
} }
static int ifcvf_vdpa_set_vq_state(struct vdpa_device *vdpa_dev, u16 qid, static int ifcvf_vdpa_set_vq_state(struct vdpa_device *vdpa_dev, u16 qid,
u64 num) const struct vdpa_vq_state *state)
{ {
struct ifcvf_hw *vf = vdpa_to_vf(vdpa_dev); struct ifcvf_hw *vf = vdpa_to_vf(vdpa_dev);
return ifcvf_set_vq_state(vf, qid, num); return ifcvf_set_vq_state(vf, qid, state->avail_index);
} }
static void ifcvf_vdpa_set_vq_cb(struct vdpa_device *vdpa_dev, u16 qid, static void ifcvf_vdpa_set_vq_cb(struct vdpa_device *vdpa_dev, u16 qid,
@@ -352,6 +356,14 @@ static void ifcvf_vdpa_set_config_cb(struct vdpa_device *vdpa_dev,
vf->config_cb.private = cb->private; vf->config_cb.private = cb->private;
} }
static int ifcvf_vdpa_get_vq_irq(struct vdpa_device *vdpa_dev,
u16 qid)
{
struct ifcvf_hw *vf = vdpa_to_vf(vdpa_dev);
return vf->vring[qid].irq;
}
/* /*
* IFCVF currently does't have on-chip IOMMU, so not * IFCVF currently does't have on-chip IOMMU, so not
* implemented set_map()/dma_map()/dma_unmap() * implemented set_map()/dma_map()/dma_unmap()
@@ -369,6 +381,7 @@ static const struct vdpa_config_ops ifc_vdpa_ops = {
.get_vq_ready = ifcvf_vdpa_get_vq_ready, .get_vq_ready = ifcvf_vdpa_get_vq_ready,
.set_vq_num = ifcvf_vdpa_set_vq_num, .set_vq_num = ifcvf_vdpa_set_vq_num,
.set_vq_address = ifcvf_vdpa_set_vq_address, .set_vq_address = ifcvf_vdpa_set_vq_address,
.get_vq_irq = ifcvf_vdpa_get_vq_irq,
.kick_vq = ifcvf_vdpa_kick_vq, .kick_vq = ifcvf_vdpa_kick_vq,
.get_generation = ifcvf_vdpa_get_generation, .get_generation = ifcvf_vdpa_get_generation,
.get_device_id = ifcvf_vdpa_get_device_id, .get_device_id = ifcvf_vdpa_get_device_id,
@@ -384,7 +397,7 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct ifcvf_adapter *adapter; struct ifcvf_adapter *adapter;
struct ifcvf_hw *vf; struct ifcvf_hw *vf;
int ret; int ret, i;
ret = pcim_enable_device(pdev); ret = pcim_enable_device(pdev);
if (ret) { if (ret) {
@@ -420,7 +433,8 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
} }
adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa, adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa,
dev, &ifc_vdpa_ops); dev, &ifc_vdpa_ops,
IFCVF_MAX_QUEUE_PAIRS * 2);
if (adapter == NULL) { if (adapter == NULL) {
IFCVF_ERR(pdev, "Failed to allocate vDPA structure"); IFCVF_ERR(pdev, "Failed to allocate vDPA structure");
return -ENOMEM; return -ENOMEM;
@@ -441,6 +455,9 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err; goto err;
} }
for (i = 0; i < IFCVF_MAX_QUEUE_PAIRS * 2; i++)
vf->vring[i].irq = -EINVAL;
ret = vdpa_register_device(&adapter->vdpa); ret = vdpa_register_device(&adapter->vdpa);
if (ret) { if (ret) {
IFCVF_ERR(pdev, "Failed to register ifcvf to vdpa bus"); IFCVF_ERR(pdev, "Failed to register ifcvf to vdpa bus");

View File

@@ -0,0 +1,4 @@
subdir-ccflags-y += -I$(srctree)/drivers/vdpa/mlx5/core
obj-$(CONFIG_MLX5_VDPA_NET) += mlx5_vdpa.o
mlx5_vdpa-$(CONFIG_MLX5_VDPA_NET) += net/main.o net/mlx5_vnet.o core/resources.o core/mr.o

View File

@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#ifndef __MLX5_VDPA_H__
#define __MLX5_VDPA_H__
#include <linux/vdpa.h>
#include <linux/mlx5/driver.h>
struct mlx5_vdpa_direct_mr {
u64 start;
u64 end;
u32 perm;
struct mlx5_core_mkey mr;
struct sg_table sg_head;
int log_size;
int nsg;
struct list_head list;
u64 offset;
};
struct mlx5_vdpa_mr {
struct mlx5_core_mkey mkey;
/* list of direct MRs descendants of this indirect mr */
struct list_head head;
unsigned long num_directs;
unsigned long num_klms;
bool initialized;
/* serialize mkey creation and destruction */
struct mutex mkey_mtx;
};
struct mlx5_vdpa_resources {
u32 pdn;
struct mlx5_uars_page *uar;
void __iomem *kick_addr;
u16 uid;
u32 null_mkey;
bool valid;
};
struct mlx5_vdpa_dev {
struct vdpa_device vdev;
struct mlx5_core_dev *mdev;
struct mlx5_vdpa_resources res;
u64 mlx_features;
u64 actual_features;
u8 status;
u32 max_vqs;
u32 generation;
struct mlx5_vdpa_mr mr;
};
int mlx5_vdpa_alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid);
int mlx5_vdpa_dealloc_pd(struct mlx5_vdpa_dev *dev, u32 pdn, u16 uid);
int mlx5_vdpa_get_null_mkey(struct mlx5_vdpa_dev *dev, u32 *null_mkey);
int mlx5_vdpa_create_tis(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tisn);
void mlx5_vdpa_destroy_tis(struct mlx5_vdpa_dev *mvdev, u32 tisn);
int mlx5_vdpa_create_rqt(struct mlx5_vdpa_dev *mvdev, void *in, int inlen, u32 *rqtn);
void mlx5_vdpa_destroy_rqt(struct mlx5_vdpa_dev *mvdev, u32 rqtn);
int mlx5_vdpa_create_tir(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tirn);
void mlx5_vdpa_destroy_tir(struct mlx5_vdpa_dev *mvdev, u32 tirn);
int mlx5_vdpa_alloc_transport_domain(struct mlx5_vdpa_dev *mvdev, u32 *tdn);
void mlx5_vdpa_dealloc_transport_domain(struct mlx5_vdpa_dev *mvdev, u32 tdn);
int mlx5_vdpa_alloc_resources(struct mlx5_vdpa_dev *mvdev);
void mlx5_vdpa_free_resources(struct mlx5_vdpa_dev *mvdev);
int mlx5_vdpa_create_mkey(struct mlx5_vdpa_dev *mvdev, struct mlx5_core_mkey *mkey, u32 *in,
int inlen);
int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, struct mlx5_core_mkey *mkey);
int mlx5_vdpa_handle_set_map(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
bool *change_map);
int mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb);
void mlx5_vdpa_destroy_mr(struct mlx5_vdpa_dev *mvdev);
#define mlx5_vdpa_warn(__dev, format, ...) \
dev_warn((__dev)->mdev->device, "%s:%d:(pid %d) warning: " format, __func__, __LINE__, \
current->pid, ##__VA_ARGS__)
#define mlx5_vdpa_info(__dev, format, ...) \
dev_info((__dev)->mdev->device, "%s:%d:(pid %d): " format, __func__, __LINE__, \
current->pid, ##__VA_ARGS__)
#define mlx5_vdpa_dbg(__dev, format, ...) \
dev_debug((__dev)->mdev->device, "%s:%d:(pid %d): " format, __func__, __LINE__, \
current->pid, ##__VA_ARGS__)
#endif /* __MLX5_VDPA_H__ */

View File

@@ -0,0 +1,168 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#ifndef __MLX5_VDPA_IFC_H_
#define __MLX5_VDPA_IFC_H_
#include <linux/mlx5/mlx5_ifc.h>
enum {
MLX5_VIRTIO_Q_EVENT_MODE_NO_MSIX_MODE = 0x0,
MLX5_VIRTIO_Q_EVENT_MODE_QP_MODE = 0x1,
MLX5_VIRTIO_Q_EVENT_MODE_MSIX_MODE = 0x2,
};
enum {
MLX5_VIRTIO_EMULATION_CAP_VIRTIO_QUEUE_TYPE_SPLIT = 0x1, // do I check this caps?
MLX5_VIRTIO_EMULATION_CAP_VIRTIO_QUEUE_TYPE_PACKED = 0x2,
};
enum {
MLX5_VIRTIO_EMULATION_VIRTIO_QUEUE_TYPE_SPLIT = 0,
MLX5_VIRTIO_EMULATION_VIRTIO_QUEUE_TYPE_PACKED = 1,
};
struct mlx5_ifc_virtio_q_bits {
u8 virtio_q_type[0x8];
u8 reserved_at_8[0x5];
u8 event_mode[0x3];
u8 queue_index[0x10];
u8 full_emulation[0x1];
u8 virtio_version_1_0[0x1];
u8 reserved_at_22[0x2];
u8 offload_type[0x4];
u8 event_qpn_or_msix[0x18];
u8 doorbell_stride_index[0x10];
u8 queue_size[0x10];
u8 device_emulation_id[0x20];
u8 desc_addr[0x40];
u8 used_addr[0x40];
u8 available_addr[0x40];
u8 virtio_q_mkey[0x20];
u8 max_tunnel_desc[0x10];
u8 reserved_at_170[0x8];
u8 error_type[0x8];
u8 umem_1_id[0x20];
u8 umem_1_size[0x20];
u8 umem_1_offset[0x40];
u8 umem_2_id[0x20];
u8 umem_2_size[0x20];
u8 umem_2_offset[0x40];
u8 umem_3_id[0x20];
u8 umem_3_size[0x20];
u8 umem_3_offset[0x40];
u8 counter_set_id[0x20];
u8 reserved_at_320[0x8];
u8 pd[0x18];
u8 reserved_at_340[0xc0];
};
struct mlx5_ifc_virtio_net_q_object_bits {
u8 modify_field_select[0x40];
u8 reserved_at_40[0x20];
u8 vhca_id[0x10];
u8 reserved_at_70[0x10];
u8 queue_feature_bit_mask_12_3[0xa];
u8 dirty_bitmap_dump_enable[0x1];
u8 vhost_log_page[0x5];
u8 reserved_at_90[0xc];
u8 state[0x4];
u8 reserved_at_a0[0x5];
u8 queue_feature_bit_mask_2_0[0x3];
u8 tisn_or_qpn[0x18];
u8 dirty_bitmap_mkey[0x20];
u8 dirty_bitmap_size[0x20];
u8 dirty_bitmap_addr[0x40];
u8 hw_available_index[0x10];
u8 hw_used_index[0x10];
u8 reserved_at_160[0xa0];
struct mlx5_ifc_virtio_q_bits virtio_q_context;
};
struct mlx5_ifc_create_virtio_net_q_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits general_obj_in_cmd_hdr;
struct mlx5_ifc_virtio_net_q_object_bits obj_context;
};
struct mlx5_ifc_create_virtio_net_q_out_bits {
struct mlx5_ifc_general_obj_out_cmd_hdr_bits general_obj_out_cmd_hdr;
};
struct mlx5_ifc_destroy_virtio_net_q_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits general_obj_out_cmd_hdr;
};
struct mlx5_ifc_destroy_virtio_net_q_out_bits {
struct mlx5_ifc_general_obj_out_cmd_hdr_bits general_obj_out_cmd_hdr;
};
struct mlx5_ifc_query_virtio_net_q_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits general_obj_in_cmd_hdr;
};
struct mlx5_ifc_query_virtio_net_q_out_bits {
struct mlx5_ifc_general_obj_out_cmd_hdr_bits general_obj_out_cmd_hdr;
struct mlx5_ifc_virtio_net_q_object_bits obj_context;
};
enum {
MLX5_VIRTQ_MODIFY_MASK_STATE = (u64)1 << 0,
MLX5_VIRTQ_MODIFY_MASK_DIRTY_BITMAP_PARAMS = (u64)1 << 3,
MLX5_VIRTQ_MODIFY_MASK_DIRTY_BITMAP_DUMP_ENABLE = (u64)1 << 4,
};
enum {
MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT = 0x0,
MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY = 0x1,
MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND = 0x2,
MLX5_VIRTIO_NET_Q_OBJECT_STATE_ERR = 0x3,
};
enum {
MLX5_RQTC_LIST_Q_TYPE_RQ = 0x0,
MLX5_RQTC_LIST_Q_TYPE_VIRTIO_NET_Q = 0x1,
};
struct mlx5_ifc_modify_virtio_net_q_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits general_obj_in_cmd_hdr;
struct mlx5_ifc_virtio_net_q_object_bits obj_context;
};
struct mlx5_ifc_modify_virtio_net_q_out_bits {
struct mlx5_ifc_general_obj_out_cmd_hdr_bits general_obj_out_cmd_hdr;
};
#endif /* __MLX5_VDPA_IFC_H_ */

486
drivers/vdpa/mlx5/core/mr.c Normal file
View File

@@ -0,0 +1,486 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#include <linux/vdpa.h>
#include <linux/gcd.h>
#include <linux/string.h>
#include <linux/mlx5/qp.h>
#include "mlx5_vdpa.h"
/* DIV_ROUND_UP where the divider is a power of 2 give by its log base 2 value */
#define MLX5_DIV_ROUND_UP_POW2(_n, _s) \
({ \
u64 __s = _s; \
u64 _res; \
_res = (((_n) + (1 << (__s)) - 1) >> (__s)); \
_res; \
})
static int get_octo_len(u64 len, int page_shift)
{
u64 page_size = 1ULL << page_shift;
int npages;
npages = ALIGN(len, page_size) >> page_shift;
return (npages + 1) / 2;
}
static void fill_sg(struct mlx5_vdpa_direct_mr *mr, void *in)
{
struct scatterlist *sg;
__be64 *pas;
int i;
pas = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
for_each_sg(mr->sg_head.sgl, sg, mr->nsg, i)
(*pas) = cpu_to_be64(sg_dma_address(sg));
}
static void mlx5_set_access_mode(void *mkc, int mode)
{
MLX5_SET(mkc, mkc, access_mode_1_0, mode & 0x3);
MLX5_SET(mkc, mkc, access_mode_4_2, mode >> 2);
}
static void populate_mtts(struct mlx5_vdpa_direct_mr *mr, __be64 *mtt)
{
struct scatterlist *sg;
int i;
for_each_sg(mr->sg_head.sgl, sg, mr->nsg, i)
mtt[i] = cpu_to_be64(sg_dma_address(sg));
}
static int create_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr *mr)
{
int inlen;
void *mkc;
void *in;
int err;
inlen = MLX5_ST_SZ_BYTES(create_mkey_in) + roundup(MLX5_ST_SZ_BYTES(mtt) * mr->nsg, 16);
in = kvzalloc(inlen, GFP_KERNEL);
if (!in)
return -ENOMEM;
MLX5_SET(create_mkey_in, in, uid, mvdev->res.uid);
fill_sg(mr, in);
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
MLX5_SET(mkc, mkc, lw, !!(mr->perm & VHOST_MAP_WO));
MLX5_SET(mkc, mkc, lr, !!(mr->perm & VHOST_MAP_RO));
mlx5_set_access_mode(mkc, MLX5_MKC_ACCESS_MODE_MTT);
MLX5_SET(mkc, mkc, qpn, 0xffffff);
MLX5_SET(mkc, mkc, pd, mvdev->res.pdn);
MLX5_SET64(mkc, mkc, start_addr, mr->offset);
MLX5_SET64(mkc, mkc, len, mr->end - mr->start);
MLX5_SET(mkc, mkc, log_page_size, mr->log_size);
MLX5_SET(mkc, mkc, translations_octword_size,
get_octo_len(mr->end - mr->start, mr->log_size));
MLX5_SET(create_mkey_in, in, translations_octword_actual_size,
get_octo_len(mr->end - mr->start, mr->log_size));
populate_mtts(mr, MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt));
err = mlx5_vdpa_create_mkey(mvdev, &mr->mr, in, inlen);
kvfree(in);
if (err) {
mlx5_vdpa_warn(mvdev, "Failed to create direct MR\n");
return err;
}
return 0;
}
static void destroy_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr *mr)
{
mlx5_vdpa_destroy_mkey(mvdev, &mr->mr);
}
static u64 map_start(struct vhost_iotlb_map *map, struct mlx5_vdpa_direct_mr *mr)
{
return max_t(u64, map->start, mr->start);
}
static u64 map_end(struct vhost_iotlb_map *map, struct mlx5_vdpa_direct_mr *mr)
{
return min_t(u64, map->last + 1, mr->end);
}
static u64 maplen(struct vhost_iotlb_map *map, struct mlx5_vdpa_direct_mr *mr)
{
return map_end(map, mr) - map_start(map, mr);
}
#define MLX5_VDPA_INVALID_START_ADDR ((u64)-1)
#define MLX5_VDPA_INVALID_LEN ((u64)-1)
static u64 indir_start_addr(struct mlx5_vdpa_mr *mkey)
{
struct mlx5_vdpa_direct_mr *s;
s = list_first_entry_or_null(&mkey->head, struct mlx5_vdpa_direct_mr, list);
if (!s)
return MLX5_VDPA_INVALID_START_ADDR;
return s->start;
}
static u64 indir_len(struct mlx5_vdpa_mr *mkey)
{
struct mlx5_vdpa_direct_mr *s;
struct mlx5_vdpa_direct_mr *e;
s = list_first_entry_or_null(&mkey->head, struct mlx5_vdpa_direct_mr, list);
if (!s)
return MLX5_VDPA_INVALID_LEN;
e = list_last_entry(&mkey->head, struct mlx5_vdpa_direct_mr, list);
return e->end - s->start;
}
#define LOG_MAX_KLM_SIZE 30
#define MAX_KLM_SIZE BIT(LOG_MAX_KLM_SIZE)
static u32 klm_bcount(u64 size)
{
return (u32)size;
}
static void fill_indir(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mkey, void *in)
{
struct mlx5_vdpa_direct_mr *dmr;
struct mlx5_klm *klmarr;
struct mlx5_klm *klm;
bool first = true;
u64 preve;
int i;
klmarr = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
i = 0;
list_for_each_entry(dmr, &mkey->head, list) {
again:
klm = &klmarr[i++];
if (first) {
preve = dmr->start;
first = false;
}
if (preve == dmr->start) {
klm->key = cpu_to_be32(dmr->mr.key);
klm->bcount = cpu_to_be32(klm_bcount(dmr->end - dmr->start));
preve = dmr->end;
} else {
klm->key = cpu_to_be32(mvdev->res.null_mkey);
klm->bcount = cpu_to_be32(klm_bcount(dmr->start - preve));
preve = dmr->start;
goto again;
}
}
}
static int klm_byte_size(int nklms)
{
return 16 * ALIGN(nklms, 4);
}
static int create_indirect_key(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr)
{
int inlen;
void *mkc;
void *in;
int err;
u64 start;
u64 len;
start = indir_start_addr(mr);
len = indir_len(mr);
if (start == MLX5_VDPA_INVALID_START_ADDR || len == MLX5_VDPA_INVALID_LEN)
return -EINVAL;
inlen = MLX5_ST_SZ_BYTES(create_mkey_in) + klm_byte_size(mr->num_klms);
in = kzalloc(inlen, GFP_KERNEL);
if (!in)
return -ENOMEM;
MLX5_SET(create_mkey_in, in, uid, mvdev->res.uid);
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
MLX5_SET(mkc, mkc, lw, 1);
MLX5_SET(mkc, mkc, lr, 1);
mlx5_set_access_mode(mkc, MLX5_MKC_ACCESS_MODE_KLMS);
MLX5_SET(mkc, mkc, qpn, 0xffffff);
MLX5_SET(mkc, mkc, pd, mvdev->res.pdn);
MLX5_SET64(mkc, mkc, start_addr, start);
MLX5_SET64(mkc, mkc, len, len);
MLX5_SET(mkc, mkc, translations_octword_size, klm_byte_size(mr->num_klms) / 16);
MLX5_SET(create_mkey_in, in, translations_octword_actual_size, mr->num_klms);
fill_indir(mvdev, mr, in);
err = mlx5_vdpa_create_mkey(mvdev, &mr->mkey, in, inlen);
kfree(in);
return err;
}
static void destroy_indirect_key(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mkey)
{
mlx5_vdpa_destroy_mkey(mvdev, &mkey->mkey);
}
static int map_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr *mr,
struct vhost_iotlb *iotlb)
{
struct vhost_iotlb_map *map;
unsigned long lgcd = 0;
int log_entity_size;
unsigned long size;
u64 start = 0;
int err;
struct page *pg;
unsigned int nsg;
int sglen;
u64 pa;
u64 paend;
struct scatterlist *sg;
struct device *dma = mvdev->mdev->device;
int ret;
for (map = vhost_iotlb_itree_first(iotlb, mr->start, mr->end - 1);
map; map = vhost_iotlb_itree_next(map, start, mr->end - 1)) {
size = maplen(map, mr);
lgcd = gcd(lgcd, size);
start += size;
}
log_entity_size = ilog2(lgcd);
sglen = 1 << log_entity_size;
nsg = MLX5_DIV_ROUND_UP_POW2(mr->end - mr->start, log_entity_size);
err = sg_alloc_table(&mr->sg_head, nsg, GFP_KERNEL);
if (err)
return err;
sg = mr->sg_head.sgl;
for (map = vhost_iotlb_itree_first(iotlb, mr->start, mr->end - 1);
map; map = vhost_iotlb_itree_next(map, mr->start, mr->end - 1)) {
paend = map->addr + maplen(map, mr);
for (pa = map->addr; pa < paend; pa += sglen) {
pg = pfn_to_page(__phys_to_pfn(pa));
if (!sg) {
mlx5_vdpa_warn(mvdev, "sg null. start 0x%llx, end 0x%llx\n",
map->start, map->last + 1);
err = -ENOMEM;
goto err_map;
}
sg_set_page(sg, pg, sglen, 0);
sg = sg_next(sg);
if (!sg)
goto done;
}
}
done:
mr->log_size = log_entity_size;
mr->nsg = nsg;
ret = dma_map_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0);
if (!ret)
goto err_map;
err = create_direct_mr(mvdev, mr);
if (err)
goto err_direct;
return 0;
err_direct:
dma_unmap_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0);
err_map:
sg_free_table(&mr->sg_head);
return err;
}
static void unmap_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr *mr)
{
struct device *dma = mvdev->mdev->device;
destroy_direct_mr(mvdev, mr);
dma_unmap_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0);
sg_free_table(&mr->sg_head);
}
static int add_direct_chain(struct mlx5_vdpa_dev *mvdev, u64 start, u64 size, u8 perm,
struct vhost_iotlb *iotlb)
{
struct mlx5_vdpa_mr *mr = &mvdev->mr;
struct mlx5_vdpa_direct_mr *dmr;
struct mlx5_vdpa_direct_mr *n;
LIST_HEAD(tmp);
u64 st;
u64 sz;
int err;
int i = 0;
st = start;
while (size) {
sz = (u32)min_t(u64, MAX_KLM_SIZE, size);
dmr = kzalloc(sizeof(*dmr), GFP_KERNEL);
if (!dmr) {
err = -ENOMEM;
goto err_alloc;
}
dmr->start = st;
dmr->end = st + sz;
dmr->perm = perm;
err = map_direct_mr(mvdev, dmr, iotlb);
if (err) {
kfree(dmr);
goto err_alloc;
}
list_add_tail(&dmr->list, &tmp);
size -= sz;
mr->num_directs++;
mr->num_klms++;
st += sz;
i++;
}
list_splice_tail(&tmp, &mr->head);
return 0;
err_alloc:
list_for_each_entry_safe(dmr, n, &mr->head, list) {
list_del_init(&dmr->list);
unmap_direct_mr(mvdev, dmr);
kfree(dmr);
}
return err;
}
/* The iotlb pointer contains a list of maps. Go over the maps, possibly
* merging mergeable maps, and create direct memory keys that provide the
* device access to memory. The direct mkeys are then referred to by the
* indirect memory key that provides access to the enitre address space given
* by iotlb.
*/
static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb)
{
struct mlx5_vdpa_mr *mr = &mvdev->mr;
struct mlx5_vdpa_direct_mr *dmr;
struct mlx5_vdpa_direct_mr *n;
struct vhost_iotlb_map *map;
u32 pperm = U16_MAX;
u64 last = U64_MAX;
u64 ps = U64_MAX;
u64 pe = U64_MAX;
u64 start = 0;
int err = 0;
int nnuls;
if (mr->initialized)
return 0;
INIT_LIST_HEAD(&mr->head);
for (map = vhost_iotlb_itree_first(iotlb, start, last); map;
map = vhost_iotlb_itree_next(map, start, last)) {
start = map->start;
if (pe == map->start && pperm == map->perm) {
pe = map->last + 1;
} else {
if (ps != U64_MAX) {
if (pe < map->start) {
/* We have a hole in the map. Check how
* many null keys are required to fill it.
*/
nnuls = MLX5_DIV_ROUND_UP_POW2(map->start - pe,
LOG_MAX_KLM_SIZE);
mr->num_klms += nnuls;
}
err = add_direct_chain(mvdev, ps, pe - ps, pperm, iotlb);
if (err)
goto err_chain;
}
ps = map->start;
pe = map->last + 1;
pperm = map->perm;
}
}
err = add_direct_chain(mvdev, ps, pe - ps, pperm, iotlb);
if (err)
goto err_chain;
/* Create the memory key that defines the guests's address space. This
* memory key refers to the direct keys that contain the MTT
* translations
*/
err = create_indirect_key(mvdev, mr);
if (err)
goto err_chain;
mr->initialized = true;
return 0;
err_chain:
list_for_each_entry_safe_reverse(dmr, n, &mr->head, list) {
list_del_init(&dmr->list);
unmap_direct_mr(mvdev, dmr);
kfree(dmr);
}
return err;
}
int mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb)
{
struct mlx5_vdpa_mr *mr = &mvdev->mr;
int err;
mutex_lock(&mr->mkey_mtx);
err = _mlx5_vdpa_create_mr(mvdev, iotlb);
mutex_unlock(&mr->mkey_mtx);
return err;
}
void mlx5_vdpa_destroy_mr(struct mlx5_vdpa_dev *mvdev)
{
struct mlx5_vdpa_mr *mr = &mvdev->mr;
struct mlx5_vdpa_direct_mr *dmr;
struct mlx5_vdpa_direct_mr *n;
mutex_lock(&mr->mkey_mtx);
if (!mr->initialized)
goto out;
destroy_indirect_key(mvdev, mr);
list_for_each_entry_safe_reverse(dmr, n, &mr->head, list) {
list_del_init(&dmr->list);
unmap_direct_mr(mvdev, dmr);
kfree(dmr);
}
memset(mr, 0, sizeof(*mr));
mr->initialized = false;
out:
mutex_unlock(&mr->mkey_mtx);
}
static bool map_empty(struct vhost_iotlb *iotlb)
{
return !vhost_iotlb_itree_first(iotlb, 0, U64_MAX);
}
int mlx5_vdpa_handle_set_map(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
bool *change_map)
{
struct mlx5_vdpa_mr *mr = &mvdev->mr;
int err = 0;
*change_map = false;
if (map_empty(iotlb)) {
mlx5_vdpa_destroy_mr(mvdev);
return 0;
}
mutex_lock(&mr->mkey_mtx);
if (mr->initialized) {
mlx5_vdpa_info(mvdev, "memory map update\n");
*change_map = true;
}
if (!*change_map)
err = _mlx5_vdpa_create_mr(mvdev, iotlb);
mutex_unlock(&mr->mkey_mtx);
return err;
}

View File

@@ -0,0 +1,284 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#include <linux/mlx5/driver.h>
#include "mlx5_vdpa.h"
static int alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid)
{
struct mlx5_core_dev *mdev = dev->mdev;
u32 out[MLX5_ST_SZ_DW(alloc_pd_out)] = {};
u32 in[MLX5_ST_SZ_DW(alloc_pd_in)] = {};
int err;
MLX5_SET(alloc_pd_in, in, opcode, MLX5_CMD_OP_ALLOC_PD);
MLX5_SET(alloc_pd_in, in, uid, uid);
err = mlx5_cmd_exec_inout(mdev, alloc_pd, in, out);
if (!err)
*pdn = MLX5_GET(alloc_pd_out, out, pd);
return err;
}
static int dealloc_pd(struct mlx5_vdpa_dev *dev, u32 pdn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(dealloc_pd_in)] = {};
struct mlx5_core_dev *mdev = dev->mdev;
MLX5_SET(dealloc_pd_in, in, opcode, MLX5_CMD_OP_DEALLOC_PD);
MLX5_SET(dealloc_pd_in, in, pd, pdn);
MLX5_SET(dealloc_pd_in, in, uid, uid);
return mlx5_cmd_exec_in(mdev, dealloc_pd, in);
}
static int get_null_mkey(struct mlx5_vdpa_dev *dev, u32 *null_mkey)
{
u32 out[MLX5_ST_SZ_DW(query_special_contexts_out)] = {};
u32 in[MLX5_ST_SZ_DW(query_special_contexts_in)] = {};
struct mlx5_core_dev *mdev = dev->mdev;
int err;
MLX5_SET(query_special_contexts_in, in, opcode, MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS);
err = mlx5_cmd_exec_inout(mdev, query_special_contexts, in, out);
if (!err)
*null_mkey = MLX5_GET(query_special_contexts_out, out, null_mkey);
return err;
}
static int create_uctx(struct mlx5_vdpa_dev *mvdev, u16 *uid)
{
u32 out[MLX5_ST_SZ_DW(create_uctx_out)] = {};
int inlen;
void *in;
int err;
/* 0 means not supported */
if (!MLX5_CAP_GEN(mvdev->mdev, log_max_uctx))
return -EOPNOTSUPP;
inlen = MLX5_ST_SZ_BYTES(create_uctx_in);
in = kzalloc(inlen, GFP_KERNEL);
if (!in)
return -ENOMEM;
MLX5_SET(create_uctx_in, in, opcode, MLX5_CMD_OP_CREATE_UCTX);
MLX5_SET(create_uctx_in, in, uctx.cap, MLX5_UCTX_CAP_RAW_TX);
err = mlx5_cmd_exec(mvdev->mdev, in, inlen, out, sizeof(out));
kfree(in);
if (!err)
*uid = MLX5_GET(create_uctx_out, out, uid);
return err;
}
static void destroy_uctx(struct mlx5_vdpa_dev *mvdev, u32 uid)
{
u32 out[MLX5_ST_SZ_DW(destroy_uctx_out)] = {};
u32 in[MLX5_ST_SZ_DW(destroy_uctx_in)] = {};
MLX5_SET(destroy_uctx_in, in, opcode, MLX5_CMD_OP_DESTROY_UCTX);
MLX5_SET(destroy_uctx_in, in, uid, uid);
mlx5_cmd_exec(mvdev->mdev, in, sizeof(in), out, sizeof(out));
}
int mlx5_vdpa_create_tis(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tisn)
{
u32 out[MLX5_ST_SZ_DW(create_tis_out)] = {};
int err;
MLX5_SET(create_tis_in, in, opcode, MLX5_CMD_OP_CREATE_TIS);
MLX5_SET(create_tis_in, in, uid, mvdev->res.uid);
err = mlx5_cmd_exec_inout(mvdev->mdev, create_tis, in, out);
if (!err)
*tisn = MLX5_GET(create_tis_out, out, tisn);
return err;
}
void mlx5_vdpa_destroy_tis(struct mlx5_vdpa_dev *mvdev, u32 tisn)
{
u32 in[MLX5_ST_SZ_DW(destroy_tis_in)] = {};
MLX5_SET(destroy_tis_in, in, opcode, MLX5_CMD_OP_DESTROY_TIS);
MLX5_SET(destroy_tis_in, in, uid, mvdev->res.uid);
MLX5_SET(destroy_tis_in, in, tisn, tisn);
mlx5_cmd_exec_in(mvdev->mdev, destroy_tis, in);
}
int mlx5_vdpa_create_rqt(struct mlx5_vdpa_dev *mvdev, void *in, int inlen, u32 *rqtn)
{
u32 out[MLX5_ST_SZ_DW(create_rqt_out)] = {};
int err;
MLX5_SET(create_rqt_in, in, opcode, MLX5_CMD_OP_CREATE_RQT);
err = mlx5_cmd_exec(mvdev->mdev, in, inlen, out, sizeof(out));
if (!err)
*rqtn = MLX5_GET(create_rqt_out, out, rqtn);
return err;
}
void mlx5_vdpa_destroy_rqt(struct mlx5_vdpa_dev *mvdev, u32 rqtn)
{
u32 in[MLX5_ST_SZ_DW(destroy_rqt_in)] = {};
MLX5_SET(destroy_rqt_in, in, opcode, MLX5_CMD_OP_DESTROY_RQT);
MLX5_SET(destroy_rqt_in, in, uid, mvdev->res.uid);
MLX5_SET(destroy_rqt_in, in, rqtn, rqtn);
mlx5_cmd_exec_in(mvdev->mdev, destroy_rqt, in);
}
int mlx5_vdpa_create_tir(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tirn)
{
u32 out[MLX5_ST_SZ_DW(create_tir_out)] = {};
int err;
MLX5_SET(create_tir_in, in, opcode, MLX5_CMD_OP_CREATE_TIR);
err = mlx5_cmd_exec_inout(mvdev->mdev, create_tir, in, out);
if (!err)
*tirn = MLX5_GET(create_tir_out, out, tirn);
return err;
}
void mlx5_vdpa_destroy_tir(struct mlx5_vdpa_dev *mvdev, u32 tirn)
{
u32 in[MLX5_ST_SZ_DW(destroy_tir_in)] = {};
MLX5_SET(destroy_tir_in, in, opcode, MLX5_CMD_OP_DESTROY_TIR);
MLX5_SET(destroy_tir_in, in, uid, mvdev->res.uid);
MLX5_SET(destroy_tir_in, in, tirn, tirn);
mlx5_cmd_exec_in(mvdev->mdev, destroy_tir, in);
}
int mlx5_vdpa_alloc_transport_domain(struct mlx5_vdpa_dev *mvdev, u32 *tdn)
{
u32 out[MLX5_ST_SZ_DW(alloc_transport_domain_out)] = {};
u32 in[MLX5_ST_SZ_DW(alloc_transport_domain_in)] = {};
int err;
MLX5_SET(alloc_transport_domain_in, in, opcode, MLX5_CMD_OP_ALLOC_TRANSPORT_DOMAIN);
MLX5_SET(alloc_transport_domain_in, in, uid, mvdev->res.uid);
err = mlx5_cmd_exec_inout(mvdev->mdev, alloc_transport_domain, in, out);
if (!err)
*tdn = MLX5_GET(alloc_transport_domain_out, out, transport_domain);
return err;
}
void mlx5_vdpa_dealloc_transport_domain(struct mlx5_vdpa_dev *mvdev, u32 tdn)
{
u32 in[MLX5_ST_SZ_DW(dealloc_transport_domain_in)] = {};
MLX5_SET(dealloc_transport_domain_in, in, opcode, MLX5_CMD_OP_DEALLOC_TRANSPORT_DOMAIN);
MLX5_SET(dealloc_transport_domain_in, in, uid, mvdev->res.uid);
MLX5_SET(dealloc_transport_domain_in, in, transport_domain, tdn);
mlx5_cmd_exec_in(mvdev->mdev, dealloc_transport_domain, in);
}
int mlx5_vdpa_create_mkey(struct mlx5_vdpa_dev *mvdev, struct mlx5_core_mkey *mkey, u32 *in,
int inlen)
{
u32 lout[MLX5_ST_SZ_DW(create_mkey_out)] = {};
u32 mkey_index;
void *mkc;
int err;
MLX5_SET(create_mkey_in, in, opcode, MLX5_CMD_OP_CREATE_MKEY);
MLX5_SET(create_mkey_in, in, uid, mvdev->res.uid);
err = mlx5_cmd_exec(mvdev->mdev, in, inlen, lout, sizeof(lout));
if (err)
return err;
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
mkey_index = MLX5_GET(create_mkey_out, lout, mkey_index);
mkey->iova = MLX5_GET64(mkc, mkc, start_addr);
mkey->size = MLX5_GET64(mkc, mkc, len);
mkey->key |= mlx5_idx_to_mkey(mkey_index);
mkey->pd = MLX5_GET(mkc, mkc, pd);
return 0;
}
int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, struct mlx5_core_mkey *mkey)
{
u32 in[MLX5_ST_SZ_DW(destroy_mkey_in)] = {};
MLX5_SET(destroy_mkey_in, in, uid, mvdev->res.uid);
MLX5_SET(destroy_mkey_in, in, opcode, MLX5_CMD_OP_DESTROY_MKEY);
MLX5_SET(destroy_mkey_in, in, mkey_index, mlx5_mkey_to_idx(mkey->key));
return mlx5_cmd_exec_in(mvdev->mdev, destroy_mkey, in);
}
int mlx5_vdpa_alloc_resources(struct mlx5_vdpa_dev *mvdev)
{
u64 offset = MLX5_CAP64_DEV_VDPA_EMULATION(mvdev->mdev, doorbell_bar_offset);
struct mlx5_vdpa_resources *res = &mvdev->res;
struct mlx5_core_dev *mdev = mvdev->mdev;
u64 kick_addr;
int err;
if (res->valid) {
mlx5_vdpa_warn(mvdev, "resources already allocated\n");
return -EINVAL;
}
mutex_init(&mvdev->mr.mkey_mtx);
res->uar = mlx5_get_uars_page(mdev);
if (IS_ERR(res->uar)) {
err = PTR_ERR(res->uar);
goto err_uars;
}
err = create_uctx(mvdev, &res->uid);
if (err)
goto err_uctx;
err = alloc_pd(mvdev, &res->pdn, res->uid);
if (err)
goto err_pd;
err = get_null_mkey(mvdev, &res->null_mkey);
if (err)
goto err_key;
kick_addr = pci_resource_start(mdev->pdev, 0) + offset;
res->kick_addr = ioremap(kick_addr, PAGE_SIZE);
if (!res->kick_addr) {
err = -ENOMEM;
goto err_key;
}
res->valid = true;
return 0;
err_key:
dealloc_pd(mvdev, res->pdn, res->uid);
err_pd:
destroy_uctx(mvdev, res->uid);
err_uctx:
mlx5_put_uars_page(mdev, res->uar);
err_uars:
mutex_destroy(&mvdev->mr.mkey_mtx);
return err;
}
void mlx5_vdpa_free_resources(struct mlx5_vdpa_dev *mvdev)
{
struct mlx5_vdpa_resources *res = &mvdev->res;
if (!res->valid)
return;
iounmap(res->kick_addr);
res->kick_addr = NULL;
dealloc_pd(mvdev, res->pdn, res->uid);
destroy_uctx(mvdev, res->uid);
mlx5_put_uars_page(mvdev->mdev, res->uar);
mutex_destroy(&mvdev->mr.mkey_mtx);
res->valid = false;
}

View File

@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#include <linux/module.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/device.h>
#include "mlx5_vdpa_ifc.h"
#include "mlx5_vnet.h"
MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
MODULE_DESCRIPTION("Mellanox VDPA driver");
MODULE_LICENSE("Dual BSD/GPL");
static bool required_caps_supported(struct mlx5_core_dev *mdev)
{
u8 event_mode;
u64 got;
got = MLX5_CAP_GEN_64(mdev, general_obj_types);
if (!(got & MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q))
return false;
event_mode = MLX5_CAP_DEV_VDPA_EMULATION(mdev, event_mode);
if (!(event_mode & MLX5_VIRTIO_Q_EVENT_MODE_QP_MODE))
return false;
if (!MLX5_CAP_DEV_VDPA_EMULATION(mdev, eth_frame_offload_type))
return false;
return true;
}
static void *mlx5_vdpa_add(struct mlx5_core_dev *mdev)
{
struct mlx5_vdpa_dev *vdev;
if (mlx5_core_is_pf(mdev))
return NULL;
if (!required_caps_supported(mdev)) {
dev_info(mdev->device, "virtio net emulation not supported\n");
return NULL;
}
vdev = mlx5_vdpa_add_dev(mdev);
if (IS_ERR(vdev))
return NULL;
return vdev;
}
static void mlx5_vdpa_remove(struct mlx5_core_dev *mdev, void *context)
{
struct mlx5_vdpa_dev *vdev = context;
mlx5_vdpa_remove_dev(vdev);
}
static struct mlx5_interface mlx5_vdpa_interface = {
.add = mlx5_vdpa_add,
.remove = mlx5_vdpa_remove,
.protocol = MLX5_INTERFACE_PROTOCOL_VDPA,
};
static int __init mlx5_vdpa_init(void)
{
return mlx5_register_interface(&mlx5_vdpa_interface);
}
static void __exit mlx5_vdpa_exit(void)
{
mlx5_unregister_interface(&mlx5_vdpa_interface);
}
module_init(mlx5_vdpa_init);
module_exit(mlx5_vdpa_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#ifndef __MLX5_VNET_H_
#define __MLX5_VNET_H_
#include <linux/vdpa.h>
#include <linux/virtio_net.h>
#include <linux/vringh.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/cq.h>
#include <linux/mlx5/qp.h>
#include "mlx5_vdpa.h"
static inline u32 mlx5_vdpa_max_qps(int max_vqs)
{
return max_vqs / 2;
}
#define to_mlx5_vdpa_ndev(__mvdev) container_of(__mvdev, struct mlx5_vdpa_net, mvdev)
void *mlx5_vdpa_add_dev(struct mlx5_core_dev *mdev);
void mlx5_vdpa_remove_dev(struct mlx5_vdpa_dev *mvdev);
#endif /* __MLX5_VNET_H_ */

View File

@@ -61,6 +61,7 @@ static void vdpa_release_dev(struct device *d)
* initialized but before registered. * initialized but before registered.
* @parent: the parent device * @parent: the parent device
* @config: the bus operations that is supported by this device * @config: the bus operations that is supported by this device
* @nvqs: number of virtqueues supported by this device
* @size: size of the parent structure that contains private data * @size: size of the parent structure that contains private data
* *
* Driver should use vdpa_alloc_device() wrapper macro instead of * Driver should use vdpa_alloc_device() wrapper macro instead of
@@ -71,6 +72,7 @@ static void vdpa_release_dev(struct device *d)
*/ */
struct vdpa_device *__vdpa_alloc_device(struct device *parent, struct vdpa_device *__vdpa_alloc_device(struct device *parent,
const struct vdpa_config_ops *config, const struct vdpa_config_ops *config,
int nvqs,
size_t size) size_t size)
{ {
struct vdpa_device *vdev; struct vdpa_device *vdev;
@@ -96,6 +98,8 @@ struct vdpa_device *__vdpa_alloc_device(struct device *parent,
vdev->dev.release = vdpa_release_dev; vdev->dev.release = vdpa_release_dev;
vdev->index = err; vdev->index = err;
vdev->config = config; vdev->config = config;
vdev->features_valid = false;
vdev->nvqs = nvqs;
err = dev_set_name(&vdev->dev, "vdpa%u", vdev->index); err = dev_set_name(&vdev->dev, "vdpa%u", vdev->index);
if (err) if (err)

View File

@@ -24,6 +24,7 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/vringh.h> #include <linux/vringh.h>
#include <linux/vdpa.h> #include <linux/vdpa.h>
#include <linux/virtio_byteorder.h>
#include <linux/vhost_iotlb.h> #include <linux/vhost_iotlb.h>
#include <uapi/linux/virtio_config.h> #include <uapi/linux/virtio_config.h>
#include <uapi/linux/virtio_net.h> #include <uapi/linux/virtio_net.h>
@@ -33,6 +34,10 @@
#define DRV_DESC "vDPA Device Simulator" #define DRV_DESC "vDPA Device Simulator"
#define DRV_LICENSE "GPL v2" #define DRV_LICENSE "GPL v2"
static int batch_mapping = 1;
module_param(batch_mapping, int, 0444);
MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable");
struct vdpasim_virtqueue { struct vdpasim_virtqueue {
struct vringh vring; struct vringh vring;
struct vringh_kiov iov; struct vringh_kiov iov;
@@ -55,12 +60,12 @@ struct vdpasim_virtqueue {
static u64 vdpasim_features = (1ULL << VIRTIO_F_ANY_LAYOUT) | static u64 vdpasim_features = (1ULL << VIRTIO_F_ANY_LAYOUT) |
(1ULL << VIRTIO_F_VERSION_1) | (1ULL << VIRTIO_F_VERSION_1) |
(1ULL << VIRTIO_F_IOMMU_PLATFORM); (1ULL << VIRTIO_F_ACCESS_PLATFORM);
/* State of each vdpasim device */ /* State of each vdpasim device */
struct vdpasim { struct vdpasim {
struct vdpa_device vdpa; struct vdpa_device vdpa;
struct vdpasim_virtqueue vqs[2]; struct vdpasim_virtqueue vqs[VDPASIM_VQ_NUM];
struct work_struct work; struct work_struct work;
/* spinlock to synchronize virtqueue state */ /* spinlock to synchronize virtqueue state */
spinlock_t lock; spinlock_t lock;
@@ -70,8 +75,27 @@ struct vdpasim {
u32 status; u32 status;
u32 generation; u32 generation;
u64 features; u64 features;
/* spinlock to synchronize iommu table */
spinlock_t iommu_lock;
}; };
/* TODO: cross-endian support */
static inline bool vdpasim_is_little_endian(struct vdpasim *vdpasim)
{
return virtio_legacy_is_little_endian() ||
(vdpasim->features & (1ULL << VIRTIO_F_VERSION_1));
}
static inline u16 vdpasim16_to_cpu(struct vdpasim *vdpasim, __virtio16 val)
{
return __virtio16_to_cpu(vdpasim_is_little_endian(vdpasim), val);
}
static inline __virtio16 cpu_to_vdpasim16(struct vdpasim *vdpasim, u16 val)
{
return __cpu_to_virtio16(vdpasim_is_little_endian(vdpasim), val);
}
static struct vdpasim *vdpasim_dev; static struct vdpasim *vdpasim_dev;
static struct vdpasim *vdpa_to_sim(struct vdpa_device *vdpa) static struct vdpasim *vdpa_to_sim(struct vdpa_device *vdpa)
@@ -118,7 +142,9 @@ static void vdpasim_reset(struct vdpasim *vdpasim)
for (i = 0; i < VDPASIM_VQ_NUM; i++) for (i = 0; i < VDPASIM_VQ_NUM; i++)
vdpasim_vq_reset(&vdpasim->vqs[i]); vdpasim_vq_reset(&vdpasim->vqs[i]);
spin_lock(&vdpasim->iommu_lock);
vhost_iotlb_reset(vdpasim->iommu); vhost_iotlb_reset(vdpasim->iommu);
spin_unlock(&vdpasim->iommu_lock);
vdpasim->features = 0; vdpasim->features = 0;
vdpasim->status = 0; vdpasim->status = 0;
@@ -236,8 +262,10 @@ static dma_addr_t vdpasim_map_page(struct device *dev, struct page *page,
/* For simplicity, use identical mapping to avoid e.g iova /* For simplicity, use identical mapping to avoid e.g iova
* allocator. * allocator.
*/ */
spin_lock(&vdpasim->iommu_lock);
ret = vhost_iotlb_add_range(iommu, pa, pa + size - 1, ret = vhost_iotlb_add_range(iommu, pa, pa + size - 1,
pa, dir_to_perm(dir)); pa, dir_to_perm(dir));
spin_unlock(&vdpasim->iommu_lock);
if (ret) if (ret)
return DMA_MAPPING_ERROR; return DMA_MAPPING_ERROR;
@@ -251,8 +279,10 @@ static void vdpasim_unmap_page(struct device *dev, dma_addr_t dma_addr,
struct vdpasim *vdpasim = dev_to_sim(dev); struct vdpasim *vdpasim = dev_to_sim(dev);
struct vhost_iotlb *iommu = vdpasim->iommu; struct vhost_iotlb *iommu = vdpasim->iommu;
spin_lock(&vdpasim->iommu_lock);
vhost_iotlb_del_range(iommu, (u64)dma_addr, vhost_iotlb_del_range(iommu, (u64)dma_addr,
(u64)dma_addr + size - 1); (u64)dma_addr + size - 1);
spin_unlock(&vdpasim->iommu_lock);
} }
static void *vdpasim_alloc_coherent(struct device *dev, size_t size, static void *vdpasim_alloc_coherent(struct device *dev, size_t size,
@@ -264,9 +294,10 @@ static void *vdpasim_alloc_coherent(struct device *dev, size_t size,
void *addr = kmalloc(size, flag); void *addr = kmalloc(size, flag);
int ret; int ret;
if (!addr) spin_lock(&vdpasim->iommu_lock);
if (!addr) {
*dma_addr = DMA_MAPPING_ERROR; *dma_addr = DMA_MAPPING_ERROR;
else { } else {
u64 pa = virt_to_phys(addr); u64 pa = virt_to_phys(addr);
ret = vhost_iotlb_add_range(iommu, (u64)pa, ret = vhost_iotlb_add_range(iommu, (u64)pa,
@@ -279,6 +310,7 @@ static void *vdpasim_alloc_coherent(struct device *dev, size_t size,
} else } else
*dma_addr = (dma_addr_t)pa; *dma_addr = (dma_addr_t)pa;
} }
spin_unlock(&vdpasim->iommu_lock);
return addr; return addr;
} }
@@ -290,8 +322,11 @@ static void vdpasim_free_coherent(struct device *dev, size_t size,
struct vdpasim *vdpasim = dev_to_sim(dev); struct vdpasim *vdpasim = dev_to_sim(dev);
struct vhost_iotlb *iommu = vdpasim->iommu; struct vhost_iotlb *iommu = vdpasim->iommu;
spin_lock(&vdpasim->iommu_lock);
vhost_iotlb_del_range(iommu, (u64)dma_addr, vhost_iotlb_del_range(iommu, (u64)dma_addr,
(u64)dma_addr + size - 1); (u64)dma_addr + size - 1);
spin_unlock(&vdpasim->iommu_lock);
kfree(phys_to_virt((uintptr_t)dma_addr)); kfree(phys_to_virt((uintptr_t)dma_addr));
} }
@@ -303,21 +338,27 @@ static const struct dma_map_ops vdpasim_dma_ops = {
}; };
static const struct vdpa_config_ops vdpasim_net_config_ops; static const struct vdpa_config_ops vdpasim_net_config_ops;
static const struct vdpa_config_ops vdpasim_net_batch_config_ops;
static struct vdpasim *vdpasim_create(void) static struct vdpasim *vdpasim_create(void)
{ {
struct virtio_net_config *config; const struct vdpa_config_ops *ops;
struct vdpasim *vdpasim; struct vdpasim *vdpasim;
struct device *dev; struct device *dev;
int ret = -ENOMEM; int ret = -ENOMEM;
vdpasim = vdpa_alloc_device(struct vdpasim, vdpa, NULL, if (batch_mapping)
&vdpasim_net_config_ops); ops = &vdpasim_net_batch_config_ops;
else
ops = &vdpasim_net_config_ops;
vdpasim = vdpa_alloc_device(struct vdpasim, vdpa, NULL, ops, VDPASIM_VQ_NUM);
if (!vdpasim) if (!vdpasim)
goto err_alloc; goto err_alloc;
INIT_WORK(&vdpasim->work, vdpasim_work); INIT_WORK(&vdpasim->work, vdpasim_work);
spin_lock_init(&vdpasim->lock); spin_lock_init(&vdpasim->lock);
spin_lock_init(&vdpasim->iommu_lock);
dev = &vdpasim->vdpa.dev; dev = &vdpasim->vdpa.dev;
dev->coherent_dma_mask = DMA_BIT_MASK(64); dev->coherent_dma_mask = DMA_BIT_MASK(64);
@@ -331,10 +372,7 @@ static struct vdpasim *vdpasim_create(void)
if (!vdpasim->buffer) if (!vdpasim->buffer)
goto err_iommu; goto err_iommu;
config = &vdpasim->config; eth_random_addr(vdpasim->config.mac);
config->mtu = 1500;
config->status = VIRTIO_NET_S_LINK_UP;
eth_random_addr(config->mac);
vringh_set_iotlb(&vdpasim->vqs[0].vring, vdpasim->iommu); vringh_set_iotlb(&vdpasim->vqs[0].vring, vdpasim->iommu);
vringh_set_iotlb(&vdpasim->vqs[1].vring, vdpasim->iommu); vringh_set_iotlb(&vdpasim->vqs[1].vring, vdpasim->iommu);
@@ -413,26 +451,29 @@ static bool vdpasim_get_vq_ready(struct vdpa_device *vdpa, u16 idx)
return vq->ready; return vq->ready;
} }
static int vdpasim_set_vq_state(struct vdpa_device *vdpa, u16 idx, u64 state) static int vdpasim_set_vq_state(struct vdpa_device *vdpa, u16 idx,
const struct vdpa_vq_state *state)
{ {
struct vdpasim *vdpasim = vdpa_to_sim(vdpa); struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
struct vdpasim_virtqueue *vq = &vdpasim->vqs[idx]; struct vdpasim_virtqueue *vq = &vdpasim->vqs[idx];
struct vringh *vrh = &vq->vring; struct vringh *vrh = &vq->vring;
spin_lock(&vdpasim->lock); spin_lock(&vdpasim->lock);
vrh->last_avail_idx = state; vrh->last_avail_idx = state->avail_index;
spin_unlock(&vdpasim->lock); spin_unlock(&vdpasim->lock);
return 0; return 0;
} }
static u64 vdpasim_get_vq_state(struct vdpa_device *vdpa, u16 idx) static int vdpasim_get_vq_state(struct vdpa_device *vdpa, u16 idx,
struct vdpa_vq_state *state)
{ {
struct vdpasim *vdpasim = vdpa_to_sim(vdpa); struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
struct vdpasim_virtqueue *vq = &vdpasim->vqs[idx]; struct vdpasim_virtqueue *vq = &vdpasim->vqs[idx];
struct vringh *vrh = &vq->vring; struct vringh *vrh = &vq->vring;
return vrh->last_avail_idx; state->avail_index = vrh->last_avail_idx;
return 0;
} }
static u32 vdpasim_get_vq_align(struct vdpa_device *vdpa) static u32 vdpasim_get_vq_align(struct vdpa_device *vdpa)
@@ -448,13 +489,22 @@ static u64 vdpasim_get_features(struct vdpa_device *vdpa)
static int vdpasim_set_features(struct vdpa_device *vdpa, u64 features) static int vdpasim_set_features(struct vdpa_device *vdpa, u64 features)
{ {
struct vdpasim *vdpasim = vdpa_to_sim(vdpa); struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
struct virtio_net_config *config = &vdpasim->config;
/* DMA mapping must be done by driver */ /* DMA mapping must be done by driver */
if (!(features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))) if (!(features & (1ULL << VIRTIO_F_ACCESS_PLATFORM)))
return -EINVAL; return -EINVAL;
vdpasim->features = features & vdpasim_features; vdpasim->features = features & vdpasim_features;
/* We generally only know whether guest is using the legacy interface
* here, so generally that's the earliest we can set config fields.
* Note: We actually require VIRTIO_F_ACCESS_PLATFORM above which
* implies VIRTIO_F_VERSION_1, but let's not try to be clever here.
*/
config->mtu = cpu_to_vdpasim16(vdpasim, 1500);
config->status = cpu_to_vdpasim16(vdpasim, VIRTIO_NET_S_LINK_UP);
return 0; return 0;
} }
@@ -508,7 +558,7 @@ static void vdpasim_get_config(struct vdpa_device *vdpa, unsigned int offset,
struct vdpasim *vdpasim = vdpa_to_sim(vdpa); struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
if (offset + len < sizeof(struct virtio_net_config)) if (offset + len < sizeof(struct virtio_net_config))
memcpy(buf, &vdpasim->config + offset, len); memcpy(buf, (u8 *)&vdpasim->config + offset, len);
} }
static void vdpasim_set_config(struct vdpa_device *vdpa, unsigned int offset, static void vdpasim_set_config(struct vdpa_device *vdpa, unsigned int offset,
@@ -532,6 +582,7 @@ static int vdpasim_set_map(struct vdpa_device *vdpa,
u64 start = 0ULL, last = 0ULL - 1; u64 start = 0ULL, last = 0ULL - 1;
int ret; int ret;
spin_lock(&vdpasim->iommu_lock);
vhost_iotlb_reset(vdpasim->iommu); vhost_iotlb_reset(vdpasim->iommu);
for (map = vhost_iotlb_itree_first(iotlb, start, last); map; for (map = vhost_iotlb_itree_first(iotlb, start, last); map;
@@ -541,10 +592,12 @@ static int vdpasim_set_map(struct vdpa_device *vdpa,
if (ret) if (ret)
goto err; goto err;
} }
spin_unlock(&vdpasim->iommu_lock);
return 0; return 0;
err: err:
vhost_iotlb_reset(vdpasim->iommu); vhost_iotlb_reset(vdpasim->iommu);
spin_unlock(&vdpasim->iommu_lock);
return ret; return ret;
} }
@@ -552,16 +605,23 @@ static int vdpasim_dma_map(struct vdpa_device *vdpa, u64 iova, u64 size,
u64 pa, u32 perm) u64 pa, u32 perm)
{ {
struct vdpasim *vdpasim = vdpa_to_sim(vdpa); struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
int ret;
return vhost_iotlb_add_range(vdpasim->iommu, iova, spin_lock(&vdpasim->iommu_lock);
iova + size - 1, pa, perm); ret = vhost_iotlb_add_range(vdpasim->iommu, iova, iova + size - 1, pa,
perm);
spin_unlock(&vdpasim->iommu_lock);
return ret;
} }
static int vdpasim_dma_unmap(struct vdpa_device *vdpa, u64 iova, u64 size) static int vdpasim_dma_unmap(struct vdpa_device *vdpa, u64 iova, u64 size)
{ {
struct vdpasim *vdpasim = vdpa_to_sim(vdpa); struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
spin_lock(&vdpasim->iommu_lock);
vhost_iotlb_del_range(vdpasim->iommu, iova, iova + size - 1); vhost_iotlb_del_range(vdpasim->iommu, iova, iova + size - 1);
spin_unlock(&vdpasim->iommu_lock);
return 0; return 0;
} }
@@ -597,12 +657,36 @@ static const struct vdpa_config_ops vdpasim_net_config_ops = {
.get_config = vdpasim_get_config, .get_config = vdpasim_get_config,
.set_config = vdpasim_set_config, .set_config = vdpasim_set_config,
.get_generation = vdpasim_get_generation, .get_generation = vdpasim_get_generation,
.set_map = vdpasim_set_map,
.dma_map = vdpasim_dma_map, .dma_map = vdpasim_dma_map,
.dma_unmap = vdpasim_dma_unmap, .dma_unmap = vdpasim_dma_unmap,
.free = vdpasim_free, .free = vdpasim_free,
}; };
static const struct vdpa_config_ops vdpasim_net_batch_config_ops = {
.set_vq_address = vdpasim_set_vq_address,
.set_vq_num = vdpasim_set_vq_num,
.kick_vq = vdpasim_kick_vq,
.set_vq_cb = vdpasim_set_vq_cb,
.set_vq_ready = vdpasim_set_vq_ready,
.get_vq_ready = vdpasim_get_vq_ready,
.set_vq_state = vdpasim_set_vq_state,
.get_vq_state = vdpasim_get_vq_state,
.get_vq_align = vdpasim_get_vq_align,
.get_features = vdpasim_get_features,
.set_features = vdpasim_set_features,
.set_config_cb = vdpasim_set_config_cb,
.get_vq_num_max = vdpasim_get_vq_num_max,
.get_device_id = vdpasim_get_device_id,
.get_vendor_id = vdpasim_get_vendor_id,
.get_status = vdpasim_get_status,
.set_status = vdpasim_set_status,
.get_config = vdpasim_get_config,
.set_config = vdpasim_set_config,
.get_generation = vdpasim_get_generation,
.set_map = vdpasim_set_map,
.free = vdpasim_free,
};
static int __init vdpasim_dev_init(void) static int __init vdpasim_dev_init(void)
{ {
vdpasim_dev = vdpasim_create(); vdpasim_dev = vdpasim_create();

View File

@@ -65,6 +65,7 @@ config VHOST_VDPA
tristate "Vhost driver for vDPA-based backend" tristate "Vhost driver for vDPA-based backend"
depends on EVENTFD depends on EVENTFD
select VHOST select VHOST
select IRQ_BYPASS_MANAGER
depends on VDPA depends on VDPA
help help
This kernel module can be loaded in host kernel to accelerate This kernel module can be loaded in host kernel to accelerate

View File

@@ -73,7 +73,7 @@ enum {
VHOST_NET_FEATURES = VHOST_FEATURES | VHOST_NET_FEATURES = VHOST_FEATURES |
(1ULL << VHOST_NET_F_VIRTIO_NET_HDR) | (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) |
(1ULL << VIRTIO_NET_F_MRG_RXBUF) | (1ULL << VIRTIO_NET_F_MRG_RXBUF) |
(1ULL << VIRTIO_F_IOMMU_PLATFORM) (1ULL << VIRTIO_F_ACCESS_PLATFORM)
}; };
enum { enum {
@@ -1615,21 +1615,6 @@ done:
return err; return err;
} }
static int vhost_net_set_backend_features(struct vhost_net *n, u64 features)
{
int i;
mutex_lock(&n->dev.mutex);
for (i = 0; i < VHOST_NET_VQ_MAX; ++i) {
mutex_lock(&n->vqs[i].vq.mutex);
n->vqs[i].vq.acked_backend_features = features;
mutex_unlock(&n->vqs[i].vq.mutex);
}
mutex_unlock(&n->dev.mutex);
return 0;
}
static int vhost_net_set_features(struct vhost_net *n, u64 features) static int vhost_net_set_features(struct vhost_net *n, u64 features)
{ {
size_t vhost_hlen, sock_hlen, hdr_len; size_t vhost_hlen, sock_hlen, hdr_len;
@@ -1653,7 +1638,7 @@ static int vhost_net_set_features(struct vhost_net *n, u64 features)
!vhost_log_access_ok(&n->dev)) !vhost_log_access_ok(&n->dev))
goto out_unlock; goto out_unlock;
if ((features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))) { if ((features & (1ULL << VIRTIO_F_ACCESS_PLATFORM))) {
if (vhost_init_device_iotlb(&n->dev, true)) if (vhost_init_device_iotlb(&n->dev, true))
goto out_unlock; goto out_unlock;
} }
@@ -1730,7 +1715,8 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
return -EFAULT; return -EFAULT;
if (features & ~VHOST_NET_BACKEND_FEATURES) if (features & ~VHOST_NET_BACKEND_FEATURES)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return vhost_net_set_backend_features(n, features); vhost_set_backend_features(&n->dev, features);
return 0;
case VHOST_RESET_OWNER: case VHOST_RESET_OWNER:
return vhost_net_reset_owner(n); return vhost_net_reset_owner(n);
case VHOST_SET_OWNER: case VHOST_SET_OWNER:

View File

@@ -27,37 +27,11 @@
#include "vhost.h" #include "vhost.h"
enum { enum {
VHOST_VDPA_FEATURES = VHOST_VDPA_BACKEND_FEATURES =
(1ULL << VIRTIO_F_NOTIFY_ON_EMPTY) | (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2) |
(1ULL << VIRTIO_F_ANY_LAYOUT) | (1ULL << VHOST_BACKEND_F_IOTLB_BATCH),
(1ULL << VIRTIO_F_VERSION_1) |
(1ULL << VIRTIO_F_IOMMU_PLATFORM) |
(1ULL << VIRTIO_F_RING_PACKED) |
(1ULL << VIRTIO_F_ORDER_PLATFORM) |
(1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
(1ULL << VIRTIO_RING_F_EVENT_IDX),
VHOST_VDPA_NET_FEATURES = VHOST_VDPA_FEATURES |
(1ULL << VIRTIO_NET_F_CSUM) |
(1ULL << VIRTIO_NET_F_GUEST_CSUM) |
(1ULL << VIRTIO_NET_F_MTU) |
(1ULL << VIRTIO_NET_F_MAC) |
(1ULL << VIRTIO_NET_F_GUEST_TSO4) |
(1ULL << VIRTIO_NET_F_GUEST_TSO6) |
(1ULL << VIRTIO_NET_F_GUEST_ECN) |
(1ULL << VIRTIO_NET_F_GUEST_UFO) |
(1ULL << VIRTIO_NET_F_HOST_TSO4) |
(1ULL << VIRTIO_NET_F_HOST_TSO6) |
(1ULL << VIRTIO_NET_F_HOST_ECN) |
(1ULL << VIRTIO_NET_F_HOST_UFO) |
(1ULL << VIRTIO_NET_F_MRG_RXBUF) |
(1ULL << VIRTIO_NET_F_STATUS) |
(1ULL << VIRTIO_NET_F_SPEED_DUPLEX),
}; };
/* Currently, only network backend w/o multiqueue is supported. */
#define VHOST_VDPA_VQ_MAX 2
#define VHOST_VDPA_DEV_MAX (1U << MINORBITS) #define VHOST_VDPA_DEV_MAX (1U << MINORBITS)
struct vhost_vdpa { struct vhost_vdpa {
@@ -73,16 +47,13 @@ struct vhost_vdpa {
int virtio_id; int virtio_id;
int minor; int minor;
struct eventfd_ctx *config_ctx; struct eventfd_ctx *config_ctx;
int in_batch;
}; };
static DEFINE_IDA(vhost_vdpa_ida); static DEFINE_IDA(vhost_vdpa_ida);
static dev_t vhost_vdpa_major; static dev_t vhost_vdpa_major;
static const u64 vhost_vdpa_features[] = {
[VIRTIO_ID_NET] = VHOST_VDPA_NET_FEATURES,
};
static void handle_vq_kick(struct vhost_work *work) static void handle_vq_kick(struct vhost_work *work)
{ {
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
@@ -96,7 +67,7 @@ static void handle_vq_kick(struct vhost_work *work)
static irqreturn_t vhost_vdpa_virtqueue_cb(void *private) static irqreturn_t vhost_vdpa_virtqueue_cb(void *private)
{ {
struct vhost_virtqueue *vq = private; struct vhost_virtqueue *vq = private;
struct eventfd_ctx *call_ctx = vq->call_ctx; struct eventfd_ctx *call_ctx = vq->call_ctx.ctx;
if (call_ctx) if (call_ctx)
eventfd_signal(call_ctx, 1); eventfd_signal(call_ctx, 1);
@@ -115,12 +86,45 @@ static irqreturn_t vhost_vdpa_config_cb(void *private)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void vhost_vdpa_setup_vq_irq(struct vhost_vdpa *v, u16 qid)
{
struct vhost_virtqueue *vq = &v->vqs[qid];
const struct vdpa_config_ops *ops = v->vdpa->config;
struct vdpa_device *vdpa = v->vdpa;
int ret, irq;
if (!ops->get_vq_irq)
return;
irq = ops->get_vq_irq(vdpa, qid);
spin_lock(&vq->call_ctx.ctx_lock);
irq_bypass_unregister_producer(&vq->call_ctx.producer);
if (!vq->call_ctx.ctx || irq < 0) {
spin_unlock(&vq->call_ctx.ctx_lock);
return;
}
vq->call_ctx.producer.token = vq->call_ctx.ctx;
vq->call_ctx.producer.irq = irq;
ret = irq_bypass_register_producer(&vq->call_ctx.producer);
spin_unlock(&vq->call_ctx.ctx_lock);
}
static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid)
{
struct vhost_virtqueue *vq = &v->vqs[qid];
spin_lock(&vq->call_ctx.ctx_lock);
irq_bypass_unregister_producer(&vq->call_ctx.producer);
spin_unlock(&vq->call_ctx.ctx_lock);
}
static void vhost_vdpa_reset(struct vhost_vdpa *v) static void vhost_vdpa_reset(struct vhost_vdpa *v)
{ {
struct vdpa_device *vdpa = v->vdpa; struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
ops->set_status(vdpa, 0); vdpa_reset(vdpa);
v->in_batch = 0;
} }
static long vhost_vdpa_get_device_id(struct vhost_vdpa *v, u8 __user *argp) static long vhost_vdpa_get_device_id(struct vhost_vdpa *v, u8 __user *argp)
@@ -155,11 +159,15 @@ static long vhost_vdpa_set_status(struct vhost_vdpa *v, u8 __user *statusp)
{ {
struct vdpa_device *vdpa = v->vdpa; struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config; const struct vdpa_config_ops *ops = vdpa->config;
u8 status; u8 status, status_old;
int nvqs = v->nvqs;
u16 i;
if (copy_from_user(&status, statusp, sizeof(status))) if (copy_from_user(&status, statusp, sizeof(status)))
return -EFAULT; return -EFAULT;
status_old = ops->get_status(vdpa);
/* /*
* Userspace shouldn't remove status bits unless reset the * Userspace shouldn't remove status bits unless reset the
* status to 0. * status to 0.
@@ -169,6 +177,14 @@ static long vhost_vdpa_set_status(struct vhost_vdpa *v, u8 __user *statusp)
ops->set_status(vdpa, status); ops->set_status(vdpa, status);
if ((status & VIRTIO_CONFIG_S_DRIVER_OK) && !(status_old & VIRTIO_CONFIG_S_DRIVER_OK))
for (i = 0; i < nvqs; i++)
vhost_vdpa_setup_vq_irq(v, i);
if ((status_old & VIRTIO_CONFIG_S_DRIVER_OK) && !(status & VIRTIO_CONFIG_S_DRIVER_OK))
for (i = 0; i < nvqs; i++)
vhost_vdpa_unsetup_vq_irq(v, i);
return 0; return 0;
} }
@@ -196,7 +212,6 @@ static long vhost_vdpa_get_config(struct vhost_vdpa *v,
struct vhost_vdpa_config __user *c) struct vhost_vdpa_config __user *c)
{ {
struct vdpa_device *vdpa = v->vdpa; struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
struct vhost_vdpa_config config; struct vhost_vdpa_config config;
unsigned long size = offsetof(struct vhost_vdpa_config, buf); unsigned long size = offsetof(struct vhost_vdpa_config, buf);
u8 *buf; u8 *buf;
@@ -209,7 +224,7 @@ static long vhost_vdpa_get_config(struct vhost_vdpa *v,
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
ops->get_config(vdpa, config.off, buf, config.len); vdpa_get_config(vdpa, config.off, buf, config.len);
if (copy_to_user(c->buf, buf, config.len)) { if (copy_to_user(c->buf, buf, config.len)) {
kvfree(buf); kvfree(buf);
@@ -255,7 +270,6 @@ static long vhost_vdpa_get_features(struct vhost_vdpa *v, u64 __user *featurep)
u64 features; u64 features;
features = ops->get_features(vdpa); features = ops->get_features(vdpa);
features &= vhost_vdpa_features[v->virtio_id];
if (copy_to_user(featurep, &features, sizeof(features))) if (copy_to_user(featurep, &features, sizeof(features)))
return -EFAULT; return -EFAULT;
@@ -279,10 +293,7 @@ static long vhost_vdpa_set_features(struct vhost_vdpa *v, u64 __user *featurep)
if (copy_from_user(&features, featurep, sizeof(features))) if (copy_from_user(&features, featurep, sizeof(features)))
return -EFAULT; return -EFAULT;
if (features & ~vhost_vdpa_features[v->virtio_id]) if (vdpa_set_features(vdpa, features))
return -EINVAL;
if (ops->set_features(vdpa, features))
return -EINVAL; return -EINVAL;
return 0; return 0;
@@ -332,14 +343,18 @@ static long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp)
return 0; return 0;
} }
static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
void __user *argp) void __user *argp)
{ {
struct vdpa_device *vdpa = v->vdpa; struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config; const struct vdpa_config_ops *ops = vdpa->config;
struct vdpa_vq_state vq_state;
struct vdpa_callback cb; struct vdpa_callback cb;
struct vhost_virtqueue *vq; struct vhost_virtqueue *vq;
struct vhost_vring_state s; struct vhost_vring_state s;
u64 __user *featurep = argp;
u64 features;
u32 idx; u32 idx;
long r; long r;
@@ -353,15 +368,32 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
idx = array_index_nospec(idx, v->nvqs); idx = array_index_nospec(idx, v->nvqs);
vq = &v->vqs[idx]; vq = &v->vqs[idx];
if (cmd == VHOST_VDPA_SET_VRING_ENABLE) { switch (cmd) {
case VHOST_VDPA_SET_VRING_ENABLE:
if (copy_from_user(&s, argp, sizeof(s))) if (copy_from_user(&s, argp, sizeof(s)))
return -EFAULT; return -EFAULT;
ops->set_vq_ready(vdpa, idx, s.num); ops->set_vq_ready(vdpa, idx, s.num);
return 0; return 0;
} case VHOST_GET_VRING_BASE:
r = ops->get_vq_state(v->vdpa, idx, &vq_state);
if (r)
return r;
if (cmd == VHOST_GET_VRING_BASE) vq->last_avail_idx = vq_state.avail_index;
vq->last_avail_idx = ops->get_vq_state(v->vdpa, idx); break;
case VHOST_GET_BACKEND_FEATURES:
features = VHOST_VDPA_BACKEND_FEATURES;
if (copy_to_user(featurep, &features, sizeof(features)))
return -EFAULT;
return 0;
case VHOST_SET_BACKEND_FEATURES:
if (copy_from_user(&features, featurep, sizeof(features)))
return -EFAULT;
if (features & ~VHOST_VDPA_BACKEND_FEATURES)
return -EOPNOTSUPP;
vhost_set_backend_features(&v->vdev, features);
return 0;
}
r = vhost_vring_ioctl(&v->vdev, cmd, argp); r = vhost_vring_ioctl(&v->vdev, cmd, argp);
if (r) if (r)
@@ -377,12 +409,13 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
break; break;
case VHOST_SET_VRING_BASE: case VHOST_SET_VRING_BASE:
if (ops->set_vq_state(vdpa, idx, vq->last_avail_idx)) vq_state.avail_index = vq->last_avail_idx;
if (ops->set_vq_state(vdpa, idx, &vq_state))
r = -EINVAL; r = -EINVAL;
break; break;
case VHOST_SET_VRING_CALL: case VHOST_SET_VRING_CALL:
if (vq->call_ctx) { if (vq->call_ctx.ctx) {
cb.callback = vhost_vdpa_virtqueue_cb; cb.callback = vhost_vdpa_virtqueue_cb;
cb.private = vq; cb.private = vq;
} else { } else {
@@ -390,6 +423,7 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
cb.private = NULL; cb.private = NULL;
} }
ops->set_vq_cb(vdpa, idx, &cb); ops->set_vq_cb(vdpa, idx, &cb);
vhost_vdpa_setup_vq_irq(v, idx);
break; break;
case VHOST_SET_VRING_NUM: case VHOST_SET_VRING_NUM:
@@ -519,13 +553,15 @@ static int vhost_vdpa_map(struct vhost_vdpa *v,
if (r) if (r)
return r; return r;
if (ops->dma_map) if (ops->dma_map) {
r = ops->dma_map(vdpa, iova, size, pa, perm); r = ops->dma_map(vdpa, iova, size, pa, perm);
else if (ops->set_map) } else if (ops->set_map) {
r = ops->set_map(vdpa, dev->iotlb); if (!v->in_batch)
else r = ops->set_map(vdpa, dev->iotlb);
} else {
r = iommu_map(v->domain, iova, pa, size, r = iommu_map(v->domain, iova, pa, size,
perm_to_iommu_flags(perm)); perm_to_iommu_flags(perm));
}
return r; return r;
} }
@@ -538,12 +574,14 @@ static void vhost_vdpa_unmap(struct vhost_vdpa *v, u64 iova, u64 size)
vhost_vdpa_iotlb_unmap(v, iova, iova + size - 1); vhost_vdpa_iotlb_unmap(v, iova, iova + size - 1);
if (ops->dma_map) if (ops->dma_map) {
ops->dma_unmap(vdpa, iova, size); ops->dma_unmap(vdpa, iova, size);
else if (ops->set_map) } else if (ops->set_map) {
ops->set_map(vdpa, dev->iotlb); if (!v->in_batch)
else ops->set_map(vdpa, dev->iotlb);
} else {
iommu_unmap(v->domain, iova, size); iommu_unmap(v->domain, iova, size);
}
} }
static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v,
@@ -636,6 +674,8 @@ static int vhost_vdpa_process_iotlb_msg(struct vhost_dev *dev,
struct vhost_iotlb_msg *msg) struct vhost_iotlb_msg *msg)
{ {
struct vhost_vdpa *v = container_of(dev, struct vhost_vdpa, vdev); struct vhost_vdpa *v = container_of(dev, struct vhost_vdpa, vdev);
struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
int r = 0; int r = 0;
r = vhost_dev_check_owner(dev); r = vhost_dev_check_owner(dev);
@@ -649,6 +689,14 @@ static int vhost_vdpa_process_iotlb_msg(struct vhost_dev *dev,
case VHOST_IOTLB_INVALIDATE: case VHOST_IOTLB_INVALIDATE:
vhost_vdpa_unmap(v, msg->iova, msg->size); vhost_vdpa_unmap(v, msg->iova, msg->size);
break; break;
case VHOST_IOTLB_BATCH_BEGIN:
v->in_batch = true;
break;
case VHOST_IOTLB_BATCH_END:
if (v->in_batch && ops->set_map)
ops->set_map(vdpa, dev->iotlb);
v->in_batch = false;
break;
default: default:
r = -EINVAL; r = -EINVAL;
break; break;
@@ -765,6 +813,18 @@ err:
return r; return r;
} }
static void vhost_vdpa_clean_irq(struct vhost_vdpa *v)
{
struct vhost_virtqueue *vq;
int i;
for (i = 0; i < v->nvqs; i++) {
vq = &v->vqs[i];
if (vq->call_ctx.producer.irq)
irq_bypass_unregister_producer(&vq->call_ctx.producer);
}
}
static int vhost_vdpa_release(struct inode *inode, struct file *filep) static int vhost_vdpa_release(struct inode *inode, struct file *filep)
{ {
struct vhost_vdpa *v = filep->private_data; struct vhost_vdpa *v = filep->private_data;
@@ -777,6 +837,7 @@ static int vhost_vdpa_release(struct inode *inode, struct file *filep)
vhost_vdpa_iotlb_free(v); vhost_vdpa_iotlb_free(v);
vhost_vdpa_free_domain(v); vhost_vdpa_free_domain(v);
vhost_vdpa_config_put(v); vhost_vdpa_config_put(v);
vhost_vdpa_clean_irq(v);
vhost_dev_cleanup(&v->vdev); vhost_dev_cleanup(&v->vdev);
kfree(v->vdev.vqs); kfree(v->vdev.vqs);
mutex_unlock(&d->mutex); mutex_unlock(&d->mutex);
@@ -872,7 +933,7 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa)
{ {
const struct vdpa_config_ops *ops = vdpa->config; const struct vdpa_config_ops *ops = vdpa->config;
struct vhost_vdpa *v; struct vhost_vdpa *v;
int minor, nvqs = VHOST_VDPA_VQ_MAX; int minor;
int r; int r;
/* Currently, we only accept the network devices. */ /* Currently, we only accept the network devices. */
@@ -893,14 +954,14 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa)
atomic_set(&v->opened, 0); atomic_set(&v->opened, 0);
v->minor = minor; v->minor = minor;
v->vdpa = vdpa; v->vdpa = vdpa;
v->nvqs = nvqs; v->nvqs = vdpa->nvqs;
v->virtio_id = ops->get_device_id(vdpa); v->virtio_id = ops->get_device_id(vdpa);
device_initialize(&v->dev); device_initialize(&v->dev);
v->dev.release = vhost_vdpa_release_dev; v->dev.release = vhost_vdpa_release_dev;
v->dev.parent = &vdpa->dev; v->dev.parent = &vdpa->dev;
v->dev.devt = MKDEV(MAJOR(vhost_vdpa_major), minor); v->dev.devt = MKDEV(MAJOR(vhost_vdpa_major), minor);
v->vqs = kmalloc_array(nvqs, sizeof(struct vhost_virtqueue), v->vqs = kmalloc_array(v->nvqs, sizeof(struct vhost_virtqueue),
GFP_KERNEL); GFP_KERNEL);
if (!v->vqs) { if (!v->vqs) {
r = -ENOMEM; r = -ENOMEM;

View File

@@ -298,6 +298,13 @@ static void vhost_vq_meta_reset(struct vhost_dev *d)
__vhost_vq_meta_reset(d->vqs[i]); __vhost_vq_meta_reset(d->vqs[i]);
} }
static void vhost_vring_call_reset(struct vhost_vring_call *call_ctx)
{
call_ctx->ctx = NULL;
memset(&call_ctx->producer, 0x0, sizeof(struct irq_bypass_producer));
spin_lock_init(&call_ctx->ctx_lock);
}
static void vhost_vq_reset(struct vhost_dev *dev, static void vhost_vq_reset(struct vhost_dev *dev,
struct vhost_virtqueue *vq) struct vhost_virtqueue *vq)
{ {
@@ -319,13 +326,13 @@ static void vhost_vq_reset(struct vhost_dev *dev,
vq->log_base = NULL; vq->log_base = NULL;
vq->error_ctx = NULL; vq->error_ctx = NULL;
vq->kick = NULL; vq->kick = NULL;
vq->call_ctx = NULL;
vq->log_ctx = NULL; vq->log_ctx = NULL;
vhost_reset_is_le(vq); vhost_reset_is_le(vq);
vhost_disable_cross_endian(vq); vhost_disable_cross_endian(vq);
vq->busyloop_timeout = 0; vq->busyloop_timeout = 0;
vq->umem = NULL; vq->umem = NULL;
vq->iotlb = NULL; vq->iotlb = NULL;
vhost_vring_call_reset(&vq->call_ctx);
__vhost_vq_meta_reset(vq); __vhost_vq_meta_reset(vq);
} }
@@ -685,8 +692,8 @@ void vhost_dev_cleanup(struct vhost_dev *dev)
eventfd_ctx_put(dev->vqs[i]->error_ctx); eventfd_ctx_put(dev->vqs[i]->error_ctx);
if (dev->vqs[i]->kick) if (dev->vqs[i]->kick)
fput(dev->vqs[i]->kick); fput(dev->vqs[i]->kick);
if (dev->vqs[i]->call_ctx) if (dev->vqs[i]->call_ctx.ctx)
eventfd_ctx_put(dev->vqs[i]->call_ctx); eventfd_ctx_put(dev->vqs[i]->call_ctx.ctx);
vhost_vq_reset(dev, dev->vqs[i]); vhost_vq_reset(dev, dev->vqs[i]);
} }
vhost_dev_free_iovecs(dev); vhost_dev_free_iovecs(dev);
@@ -1405,7 +1412,7 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
memcpy(newmem, &mem, size); memcpy(newmem, &mem, size);
if (copy_from_user(newmem->regions, m->regions, if (copy_from_user(newmem->regions, m->regions,
mem.nregions * sizeof *m->regions)) { flex_array_size(newmem, regions, mem.nregions))) {
kvfree(newmem); kvfree(newmem);
return -EFAULT; return -EFAULT;
} }
@@ -1629,7 +1636,10 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg
r = PTR_ERR(ctx); r = PTR_ERR(ctx);
break; break;
} }
swap(ctx, vq->call_ctx);
spin_lock(&vq->call_ctx.ctx_lock);
swap(ctx, vq->call_ctx.ctx);
spin_unlock(&vq->call_ctx.ctx_lock);
break; break;
case VHOST_SET_VRING_ERR: case VHOST_SET_VRING_ERR:
if (copy_from_user(&f, argp, sizeof f)) { if (copy_from_user(&f, argp, sizeof f)) {
@@ -2435,8 +2445,8 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
void vhost_signal(struct vhost_dev *dev, struct vhost_virtqueue *vq) void vhost_signal(struct vhost_dev *dev, struct vhost_virtqueue *vq)
{ {
/* Signal the Guest tell them we used something up. */ /* Signal the Guest tell them we used something up. */
if (vq->call_ctx && vhost_notify(dev, vq)) if (vq->call_ctx.ctx && vhost_notify(dev, vq))
eventfd_signal(vq->call_ctx, 1); eventfd_signal(vq->call_ctx.ctx, 1);
} }
EXPORT_SYMBOL_GPL(vhost_signal); EXPORT_SYMBOL_GPL(vhost_signal);
@@ -2576,6 +2586,21 @@ struct vhost_msg_node *vhost_dequeue_msg(struct vhost_dev *dev,
} }
EXPORT_SYMBOL_GPL(vhost_dequeue_msg); EXPORT_SYMBOL_GPL(vhost_dequeue_msg);
void vhost_set_backend_features(struct vhost_dev *dev, u64 features)
{
struct vhost_virtqueue *vq;
int i;
mutex_lock(&dev->mutex);
for (i = 0; i < dev->nvqs; ++i) {
vq = dev->vqs[i];
mutex_lock(&vq->mutex);
vq->acked_backend_features = features;
mutex_unlock(&vq->mutex);
}
mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL_GPL(vhost_set_backend_features);
static int __init vhost_init(void) static int __init vhost_init(void)
{ {

View File

@@ -13,6 +13,7 @@
#include <linux/virtio_ring.h> #include <linux/virtio_ring.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/vhost_iotlb.h> #include <linux/vhost_iotlb.h>
#include <linux/irqbypass.h>
struct vhost_work; struct vhost_work;
typedef void (*vhost_work_fn_t)(struct vhost_work *work); typedef void (*vhost_work_fn_t)(struct vhost_work *work);
@@ -60,6 +61,12 @@ enum vhost_uaddr_type {
VHOST_NUM_ADDRS = 3, VHOST_NUM_ADDRS = 3,
}; };
struct vhost_vring_call {
struct eventfd_ctx *ctx;
struct irq_bypass_producer producer;
spinlock_t ctx_lock;
};
/* The virtqueue structure describes a queue attached to a device. */ /* The virtqueue structure describes a queue attached to a device. */
struct vhost_virtqueue { struct vhost_virtqueue {
struct vhost_dev *dev; struct vhost_dev *dev;
@@ -72,7 +79,7 @@ struct vhost_virtqueue {
vring_used_t __user *used; vring_used_t __user *used;
const struct vhost_iotlb_map *meta_iotlb[VHOST_NUM_ADDRS]; const struct vhost_iotlb_map *meta_iotlb[VHOST_NUM_ADDRS];
struct file *kick; struct file *kick;
struct eventfd_ctx *call_ctx; struct vhost_vring_call call_ctx;
struct eventfd_ctx *error_ctx; struct eventfd_ctx *error_ctx;
struct eventfd_ctx *log_ctx; struct eventfd_ctx *log_ctx;
@@ -207,6 +214,8 @@ void vhost_enqueue_msg(struct vhost_dev *dev,
struct vhost_msg_node *node); struct vhost_msg_node *node);
struct vhost_msg_node *vhost_dequeue_msg(struct vhost_dev *dev, struct vhost_msg_node *vhost_dequeue_msg(struct vhost_dev *dev,
struct list_head *head); struct list_head *head);
void vhost_set_backend_features(struct vhost_dev *dev, u64 features);
__poll_t vhost_chr_poll(struct file *file, struct vhost_dev *dev, __poll_t vhost_chr_poll(struct file *file, struct vhost_dev *dev,
poll_table *wait); poll_table *wait);
ssize_t vhost_chr_read_iter(struct vhost_dev *dev, struct iov_iter *to, ssize_t vhost_chr_read_iter(struct vhost_dev *dev, struct iov_iter *to,

View File

@@ -398,12 +398,9 @@ static inline s64 towards_target(struct virtio_balloon *vb)
s64 target; s64 target;
u32 num_pages; u32 num_pages;
virtio_cread(vb->vdev, struct virtio_balloon_config, num_pages,
&num_pages);
/* Legacy balloon config space is LE, unlike all other devices. */ /* Legacy balloon config space is LE, unlike all other devices. */
if (!virtio_has_feature(vb->vdev, VIRTIO_F_VERSION_1)) virtio_cread_le(vb->vdev, struct virtio_balloon_config, num_pages,
num_pages = le32_to_cpu((__force __le32)num_pages); &num_pages);
target = num_pages; target = num_pages;
return target - vb->num_pages; return target - vb->num_pages;
@@ -462,11 +459,8 @@ static void update_balloon_size(struct virtio_balloon *vb)
u32 actual = vb->num_pages; u32 actual = vb->num_pages;
/* Legacy balloon config space is LE, unlike all other devices. */ /* Legacy balloon config space is LE, unlike all other devices. */
if (!virtio_has_feature(vb->vdev, VIRTIO_F_VERSION_1)) virtio_cwrite_le(vb->vdev, struct virtio_balloon_config, actual,
actual = (__force u32)cpu_to_le32(actual); &actual);
virtio_cwrite(vb->vdev, struct virtio_balloon_config, actual,
&actual);
} }
static void update_balloon_stats_func(struct work_struct *work) static void update_balloon_stats_func(struct work_struct *work)
@@ -579,12 +573,10 @@ static u32 virtio_balloon_cmd_id_received(struct virtio_balloon *vb)
{ {
if (test_and_clear_bit(VIRTIO_BALLOON_CONFIG_READ_CMD_ID, if (test_and_clear_bit(VIRTIO_BALLOON_CONFIG_READ_CMD_ID,
&vb->config_read_bitmap)) { &vb->config_read_bitmap)) {
virtio_cread(vb->vdev, struct virtio_balloon_config,
free_page_hint_cmd_id,
&vb->cmd_id_received_cache);
/* Legacy balloon config space is LE, unlike all other devices. */ /* Legacy balloon config space is LE, unlike all other devices. */
if (!virtio_has_feature(vb->vdev, VIRTIO_F_VERSION_1)) virtio_cread_le(vb->vdev, struct virtio_balloon_config,
vb->cmd_id_received_cache = le32_to_cpu((__force __le32)vb->cmd_id_received_cache); free_page_hint_cmd_id,
&vb->cmd_id_received_cache);
} }
return vb->cmd_id_received_cache; return vb->cmd_id_received_cache;
@@ -600,7 +592,7 @@ static int send_cmd_id_start(struct virtio_balloon *vb)
while (virtqueue_get_buf(vq, &unused)) while (virtqueue_get_buf(vq, &unused))
; ;
vb->cmd_id_active = virtio32_to_cpu(vb->vdev, vb->cmd_id_active = cpu_to_virtio32(vb->vdev,
virtio_balloon_cmd_id_received(vb)); virtio_balloon_cmd_id_received(vb));
sg_init_one(&sg, &vb->cmd_id_active, sizeof(vb->cmd_id_active)); sg_init_one(&sg, &vb->cmd_id_active, sizeof(vb->cmd_id_active));
err = virtqueue_add_outbuf(vq, &sg, 1, &vb->cmd_id_active, GFP_KERNEL); err = virtqueue_add_outbuf(vq, &sg, 1, &vb->cmd_id_active, GFP_KERNEL);
@@ -987,8 +979,8 @@ static int virtballoon_probe(struct virtio_device *vdev)
if (!want_init_on_free()) if (!want_init_on_free())
memset(&poison_val, PAGE_POISON, sizeof(poison_val)); memset(&poison_val, PAGE_POISON, sizeof(poison_val));
virtio_cwrite(vb->vdev, struct virtio_balloon_config, virtio_cwrite_le(vb->vdev, struct virtio_balloon_config,
poison_val, &poison_val); poison_val, &poison_val);
} }
vb->pr_dev_info.report = virtballoon_free_page_report; vb->pr_dev_info.report = virtballoon_free_page_report;
@@ -1129,7 +1121,7 @@ static int virtballoon_validate(struct virtio_device *vdev)
else if (!virtio_has_feature(vdev, VIRTIO_BALLOON_F_PAGE_POISON)) else if (!virtio_has_feature(vdev, VIRTIO_BALLOON_F_PAGE_POISON))
__virtio_clear_bit(vdev, VIRTIO_BALLOON_F_REPORTING); __virtio_clear_bit(vdev, VIRTIO_BALLOON_F_REPORTING);
__virtio_clear_bit(vdev, VIRTIO_F_IOMMU_PLATFORM); __virtio_clear_bit(vdev, VIRTIO_F_ACCESS_PLATFORM);
return 0; return 0;
} }

View File

@@ -113,9 +113,9 @@ static u8 virtinput_cfg_select(struct virtio_input *vi,
{ {
u8 size; u8 size;
virtio_cwrite(vi->vdev, struct virtio_input_config, select, &select); virtio_cwrite_le(vi->vdev, struct virtio_input_config, select, &select);
virtio_cwrite(vi->vdev, struct virtio_input_config, subsel, &subsel); virtio_cwrite_le(vi->vdev, struct virtio_input_config, subsel, &subsel);
virtio_cread(vi->vdev, struct virtio_input_config, size, &size); virtio_cread_le(vi->vdev, struct virtio_input_config, size, &size);
return size; return size;
} }
@@ -158,11 +158,11 @@ static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
u32 mi, ma, re, fu, fl; u32 mi, ma, re, fu, fl;
virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs); virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi); virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma); virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re); virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.res, &re);
virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu); virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl); virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
input_set_abs_params(vi->idev, abs, mi, ma, fu, fl); input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
input_abs_set_res(vi->idev, abs, re); input_abs_set_res(vi->idev, abs, re);
} }
@@ -244,14 +244,14 @@ static int virtinput_probe(struct virtio_device *vdev)
size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0); size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
if (size >= sizeof(struct virtio_input_devids)) { if (size >= sizeof(struct virtio_input_devids)) {
virtio_cread(vi->vdev, struct virtio_input_config, virtio_cread_le(vi->vdev, struct virtio_input_config,
u.ids.bustype, &vi->idev->id.bustype); u.ids.bustype, &vi->idev->id.bustype);
virtio_cread(vi->vdev, struct virtio_input_config, virtio_cread_le(vi->vdev, struct virtio_input_config,
u.ids.vendor, &vi->idev->id.vendor); u.ids.vendor, &vi->idev->id.vendor);
virtio_cread(vi->vdev, struct virtio_input_config, virtio_cread_le(vi->vdev, struct virtio_input_config,
u.ids.product, &vi->idev->id.product); u.ids.product, &vi->idev->id.product);
virtio_cread(vi->vdev, struct virtio_input_config, virtio_cread_le(vi->vdev, struct virtio_input_config,
u.ids.version, &vi->idev->id.version); u.ids.version, &vi->idev->id.version);
} else { } else {
vi->idev->id.bustype = BUS_VIRTUAL; vi->idev->id.bustype = BUS_VIRTUAL;
} }

View File

@@ -1530,21 +1530,21 @@ static void virtio_mem_refresh_config(struct virtio_mem *vm)
uint64_t new_plugged_size, usable_region_size, end_addr; uint64_t new_plugged_size, usable_region_size, end_addr;
/* the plugged_size is just a reflection of what _we_ did previously */ /* the plugged_size is just a reflection of what _we_ did previously */
virtio_cread(vm->vdev, struct virtio_mem_config, plugged_size, virtio_cread_le(vm->vdev, struct virtio_mem_config, plugged_size,
&new_plugged_size); &new_plugged_size);
if (WARN_ON_ONCE(new_plugged_size != vm->plugged_size)) if (WARN_ON_ONCE(new_plugged_size != vm->plugged_size))
vm->plugged_size = new_plugged_size; vm->plugged_size = new_plugged_size;
/* calculate the last usable memory block id */ /* calculate the last usable memory block id */
virtio_cread(vm->vdev, struct virtio_mem_config, virtio_cread_le(vm->vdev, struct virtio_mem_config,
usable_region_size, &usable_region_size); usable_region_size, &usable_region_size);
end_addr = vm->addr + usable_region_size; end_addr = vm->addr + usable_region_size;
end_addr = min(end_addr, phys_limit); end_addr = min(end_addr, phys_limit);
vm->last_usable_mb_id = virtio_mem_phys_to_mb_id(end_addr) - 1; vm->last_usable_mb_id = virtio_mem_phys_to_mb_id(end_addr) - 1;
/* see if there is a request to change the size */ /* see if there is a request to change the size */
virtio_cread(vm->vdev, struct virtio_mem_config, requested_size, virtio_cread_le(vm->vdev, struct virtio_mem_config, requested_size,
&vm->requested_size); &vm->requested_size);
dev_info(&vm->vdev->dev, "plugged size: 0x%llx", vm->plugged_size); dev_info(&vm->vdev->dev, "plugged size: 0x%llx", vm->plugged_size);
dev_info(&vm->vdev->dev, "requested size: 0x%llx", vm->requested_size); dev_info(&vm->vdev->dev, "requested size: 0x%llx", vm->requested_size);
@@ -1677,16 +1677,16 @@ static int virtio_mem_init(struct virtio_mem *vm)
} }
/* Fetch all properties that can't change. */ /* Fetch all properties that can't change. */
virtio_cread(vm->vdev, struct virtio_mem_config, plugged_size, virtio_cread_le(vm->vdev, struct virtio_mem_config, plugged_size,
&vm->plugged_size); &vm->plugged_size);
virtio_cread(vm->vdev, struct virtio_mem_config, block_size, virtio_cread_le(vm->vdev, struct virtio_mem_config, block_size,
&vm->device_block_size); &vm->device_block_size);
virtio_cread(vm->vdev, struct virtio_mem_config, node_id, virtio_cread_le(vm->vdev, struct virtio_mem_config, node_id,
&node_id); &node_id);
vm->nid = virtio_mem_translate_node_id(vm, node_id); vm->nid = virtio_mem_translate_node_id(vm, node_id);
virtio_cread(vm->vdev, struct virtio_mem_config, addr, &vm->addr); virtio_cread_le(vm->vdev, struct virtio_mem_config, addr, &vm->addr);
virtio_cread(vm->vdev, struct virtio_mem_config, region_size, virtio_cread_le(vm->vdev, struct virtio_mem_config, region_size,
&vm->region_size); &vm->region_size);
/* /*
* We always hotplug memory in memory block granularity. This way, * We always hotplug memory in memory block granularity. This way,

View File

@@ -481,6 +481,7 @@ static const struct virtio_config_ops virtio_pci_config_ops = {
* @dev: the pci device * @dev: the pci device
* @cfg_type: the VIRTIO_PCI_CAP_* value we seek * @cfg_type: the VIRTIO_PCI_CAP_* value we seek
* @ioresource_types: IORESOURCE_MEM and/or IORESOURCE_IO. * @ioresource_types: IORESOURCE_MEM and/or IORESOURCE_IO.
* @bars: the bitmask of BARs
* *
* Returns offset of the capability, or 0. * Returns offset of the capability, or 0.
*/ */

View File

@@ -240,7 +240,7 @@ static inline bool virtqueue_use_indirect(struct virtqueue *_vq,
static bool vring_use_dma_api(struct virtio_device *vdev) static bool vring_use_dma_api(struct virtio_device *vdev)
{ {
if (!virtio_has_iommu_quirk(vdev)) if (!virtio_has_dma_quirk(vdev))
return true; return true;
/* Otherwise, we are left to guess. */ /* Otherwise, we are left to guess. */
@@ -1960,6 +1960,9 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx)
{ {
struct vring_virtqueue *vq = to_vvq(_vq); struct vring_virtqueue *vq = to_vvq(_vq);
if (unlikely(vq->broken))
return false;
virtio_mb(vq->weak_barriers); virtio_mb(vq->weak_barriers);
return vq->packed_ring ? virtqueue_poll_packed(_vq, last_used_idx) : return vq->packed_ring ? virtqueue_poll_packed(_vq, last_used_idx) :
virtqueue_poll_split(_vq, last_used_idx); virtqueue_poll_split(_vq, last_used_idx);
@@ -2225,7 +2228,7 @@ void vring_transport_features(struct virtio_device *vdev)
break; break;
case VIRTIO_F_VERSION_1: case VIRTIO_F_VERSION_1:
break; break;
case VIRTIO_F_IOMMU_PLATFORM: case VIRTIO_F_ACCESS_PLATFORM:
break; break;
case VIRTIO_F_RING_PACKED: case VIRTIO_F_RING_PACKED:
break; break;

View File

@@ -57,9 +57,8 @@ static void virtio_vdpa_get(struct virtio_device *vdev, unsigned offset,
void *buf, unsigned len) void *buf, unsigned len)
{ {
struct vdpa_device *vdpa = vd_get_vdpa(vdev); struct vdpa_device *vdpa = vd_get_vdpa(vdev);
const struct vdpa_config_ops *ops = vdpa->config;
ops->get_config(vdpa, offset, buf, len); vdpa_get_config(vdpa, offset, buf, len);
} }
static void virtio_vdpa_set(struct virtio_device *vdev, unsigned offset, static void virtio_vdpa_set(struct virtio_device *vdev, unsigned offset,
@@ -101,9 +100,8 @@ static void virtio_vdpa_set_status(struct virtio_device *vdev, u8 status)
static void virtio_vdpa_reset(struct virtio_device *vdev) static void virtio_vdpa_reset(struct virtio_device *vdev)
{ {
struct vdpa_device *vdpa = vd_get_vdpa(vdev); struct vdpa_device *vdpa = vd_get_vdpa(vdev);
const struct vdpa_config_ops *ops = vdpa->config;
return ops->set_status(vdpa, 0); vdpa_reset(vdpa);
} }
static bool virtio_vdpa_notify(struct virtqueue *vq) static bool virtio_vdpa_notify(struct virtqueue *vq)
@@ -294,12 +292,11 @@ static u64 virtio_vdpa_get_features(struct virtio_device *vdev)
static int virtio_vdpa_finalize_features(struct virtio_device *vdev) static int virtio_vdpa_finalize_features(struct virtio_device *vdev)
{ {
struct vdpa_device *vdpa = vd_get_vdpa(vdev); struct vdpa_device *vdpa = vd_get_vdpa(vdev);
const struct vdpa_config_ops *ops = vdpa->config;
/* Give virtio_ring a chance to accept features. */ /* Give virtio_ring a chance to accept features. */
vring_transport_features(vdev); vring_transport_features(vdev);
return ops->set_features(vdpa, vdev->features); return vdpa_set_features(vdpa, vdev->features);
} }
static const char *virtio_vdpa_bus_name(struct virtio_device *vdev) static const char *virtio_vdpa_bus_name(struct virtio_device *vdev)

View File

@@ -606,8 +606,8 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
unsigned int i; unsigned int i;
int ret = 0; int ret = 0;
virtio_cread(vdev, struct virtio_fs_config, num_request_queues, virtio_cread_le(vdev, struct virtio_fs_config, num_request_queues,
&fs->num_request_queues); &fs->num_request_queues);
if (fs->num_request_queues == 0) if (fs->num_request_queues == 0)
return -EINVAL; return -EINVAL;

View File

@@ -27,18 +27,29 @@ struct vdpa_notification_area {
resource_size_t size; resource_size_t size;
}; };
/**
* vDPA vq_state definition
* @avail_index: available index
*/
struct vdpa_vq_state {
u16 avail_index;
};
/** /**
* vDPA device - representation of a vDPA device * vDPA device - representation of a vDPA device
* @dev: underlying device * @dev: underlying device
* @dma_dev: the actual device that is performing DMA * @dma_dev: the actual device that is performing DMA
* @config: the configuration ops for this device. * @config: the configuration ops for this device.
* @index: device index * @index: device index
* @features_valid: were features initialized? for legacy guests
*/ */
struct vdpa_device { struct vdpa_device {
struct device dev; struct device dev;
struct device *dma_dev; struct device *dma_dev;
const struct vdpa_config_ops *config; const struct vdpa_config_ops *config;
unsigned int index; unsigned int index;
bool features_valid;
int nvqs;
}; };
/** /**
@@ -77,16 +88,22 @@ struct vdpa_device {
* @set_vq_state: Set the state for a virtqueue * @set_vq_state: Set the state for a virtqueue
* @vdev: vdpa device * @vdev: vdpa device
* @idx: virtqueue index * @idx: virtqueue index
* @state: virtqueue state (last_avail_idx) * @state: pointer to set virtqueue state (last_avail_idx)
* Returns integer: success (0) or error (< 0) * Returns integer: success (0) or error (< 0)
* @get_vq_state: Get the state for a virtqueue * @get_vq_state: Get the state for a virtqueue
* @vdev: vdpa device * @vdev: vdpa device
* @idx: virtqueue index * @idx: virtqueue index
* Returns virtqueue state (last_avail_idx) * @state: pointer to returned state (last_avail_idx)
* @get_vq_notification: Get the notification area for a virtqueue * @get_vq_notification: Get the notification area for a virtqueue
* @vdev: vdpa device * @vdev: vdpa device
* @idx: virtqueue index * @idx: virtqueue index
* Returns the notifcation area * Returns the notifcation area
* @get_vq_irq: Get the irq number of a virtqueue (optional,
* but must implemented if require vq irq offloading)
* @vdev: vdpa device
* @idx: virtqueue index
* Returns int: irq number of a virtqueue,
* negative number if no irq assigned.
* @get_vq_align: Get the virtqueue align requirement * @get_vq_align: Get the virtqueue align requirement
* for the device * for the device
* @vdev: vdpa device * @vdev: vdpa device
@@ -174,10 +191,14 @@ struct vdpa_config_ops {
struct vdpa_callback *cb); struct vdpa_callback *cb);
void (*set_vq_ready)(struct vdpa_device *vdev, u16 idx, bool ready); void (*set_vq_ready)(struct vdpa_device *vdev, u16 idx, bool ready);
bool (*get_vq_ready)(struct vdpa_device *vdev, u16 idx); bool (*get_vq_ready)(struct vdpa_device *vdev, u16 idx);
int (*set_vq_state)(struct vdpa_device *vdev, u16 idx, u64 state); int (*set_vq_state)(struct vdpa_device *vdev, u16 idx,
u64 (*get_vq_state)(struct vdpa_device *vdev, u16 idx); const struct vdpa_vq_state *state);
int (*get_vq_state)(struct vdpa_device *vdev, u16 idx,
struct vdpa_vq_state *state);
struct vdpa_notification_area struct vdpa_notification_area
(*get_vq_notification)(struct vdpa_device *vdev, u16 idx); (*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);
/* Device ops */ /* Device ops */
u32 (*get_vq_align)(struct vdpa_device *vdev); u32 (*get_vq_align)(struct vdpa_device *vdev);
@@ -208,11 +229,12 @@ struct vdpa_config_ops {
struct vdpa_device *__vdpa_alloc_device(struct device *parent, struct vdpa_device *__vdpa_alloc_device(struct device *parent,
const struct vdpa_config_ops *config, const struct vdpa_config_ops *config,
int nvqs,
size_t size); size_t size);
#define vdpa_alloc_device(dev_struct, member, parent, config) \ #define vdpa_alloc_device(dev_struct, member, parent, config, nvqs) \
container_of(__vdpa_alloc_device( \ container_of(__vdpa_alloc_device( \
parent, config, \ parent, config, nvqs, \
sizeof(dev_struct) + \ sizeof(dev_struct) + \
BUILD_BUG_ON_ZERO(offsetof( \ BUILD_BUG_ON_ZERO(offsetof( \
dev_struct, member))), \ dev_struct, member))), \
@@ -266,4 +288,36 @@ static inline struct device *vdpa_get_dma_dev(struct vdpa_device *vdev)
{ {
return vdev->dma_dev; return vdev->dma_dev;
} }
static inline void vdpa_reset(struct vdpa_device *vdev)
{
const struct vdpa_config_ops *ops = vdev->config;
vdev->features_valid = false;
ops->set_status(vdev, 0);
}
static inline int vdpa_set_features(struct vdpa_device *vdev, u64 features)
{
const struct vdpa_config_ops *ops = vdev->config;
vdev->features_valid = true;
return ops->set_features(vdev, features);
}
static inline void vdpa_get_config(struct vdpa_device *vdev, unsigned offset,
void *buf, unsigned int len)
{
const struct vdpa_config_ops *ops = vdev->config;
/*
* Config accesses aren't supposed to trigger before features are set.
* If it does happen we assume a legacy guest.
*/
if (!vdev->features_valid)
vdpa_set_features(vdev, 0);
ops->get_config(vdev, offset, buf, len);
}
#endif /* _LINUX_VDPA_H */ #endif /* _LINUX_VDPA_H */

View File

@@ -11,9 +11,9 @@
#include <linux/types.h> #include <linux/types.h>
struct virtio_caif_transf_config { struct virtio_caif_transf_config {
u16 headroom; __virtio16 headroom;
u16 tailroom; __virtio16 tailroom;
u32 mtu; __virtio32 mtu;
u8 reserved[4]; u8 reserved[4];
}; };

View File

@@ -6,6 +6,7 @@
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/virtio_byteorder.h> #include <linux/virtio_byteorder.h>
#include <linux/compiler_types.h>
#include <uapi/linux/virtio_config.h> #include <uapi/linux/virtio_config.h>
struct irq_affinity; struct irq_affinity;
@@ -162,16 +163,16 @@ static inline bool virtio_has_feature(const struct virtio_device *vdev,
} }
/** /**
* virtio_has_iommu_quirk - determine whether this device has the iommu quirk * virtio_has_dma_quirk - determine whether this device has the DMA quirk
* @vdev: the device * @vdev: the device
*/ */
static inline bool virtio_has_iommu_quirk(const struct virtio_device *vdev) static inline bool virtio_has_dma_quirk(const struct virtio_device *vdev)
{ {
/* /*
* Note the reverse polarity of the quirk feature (compared to most * Note the reverse polarity of the quirk feature (compared to most
* other features), this is for compatibility with legacy systems. * other features), this is for compatibility with legacy systems.
*/ */
return !virtio_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); return !virtio_has_feature(vdev, VIRTIO_F_ACCESS_PLATFORM);
} }
static inline static inline
@@ -287,70 +288,133 @@ static inline __virtio64 cpu_to_virtio64(struct virtio_device *vdev, u64 val)
return __cpu_to_virtio64(virtio_is_little_endian(vdev), val); return __cpu_to_virtio64(virtio_is_little_endian(vdev), val);
} }
#define virtio_to_cpu(vdev, x) \
_Generic((x), \
__u8: (x), \
__virtio16: virtio16_to_cpu((vdev), (x)), \
__virtio32: virtio32_to_cpu((vdev), (x)), \
__virtio64: virtio64_to_cpu((vdev), (x)) \
)
#define cpu_to_virtio(vdev, x, m) \
_Generic((m), \
__u8: (x), \
__virtio16: cpu_to_virtio16((vdev), (x)), \
__virtio32: cpu_to_virtio32((vdev), (x)), \
__virtio64: cpu_to_virtio64((vdev), (x)) \
)
#define __virtio_native_type(structname, member) \
typeof(virtio_to_cpu(NULL, ((structname*)0)->member))
/* Config space accessors. */ /* Config space accessors. */
#define virtio_cread(vdev, structname, member, ptr) \ #define virtio_cread(vdev, structname, member, ptr) \
do { \ do { \
might_sleep(); \ typeof(((structname*)0)->member) virtio_cread_v; \
/* Must match the member's type, and be integer */ \
if (!typecheck(typeof((((structname*)0)->member)), *(ptr))) \
(*ptr) = 1; \
\ \
switch (sizeof(*ptr)) { \ might_sleep(); \
/* Sanity check: must match the member's type */ \
typecheck(typeof(virtio_to_cpu((vdev), virtio_cread_v)), *(ptr)); \
\
switch (sizeof(virtio_cread_v)) { \
case 1: \ case 1: \
*(ptr) = virtio_cread8(vdev, \
offsetof(structname, member)); \
break; \
case 2: \ case 2: \
*(ptr) = virtio_cread16(vdev, \
offsetof(structname, member)); \
break; \
case 4: \ case 4: \
*(ptr) = virtio_cread32(vdev, \ vdev->config->get((vdev), \
offsetof(structname, member)); \ offsetof(structname, member), \
break; \ &virtio_cread_v, \
case 8: \ sizeof(virtio_cread_v)); \
*(ptr) = virtio_cread64(vdev, \
offsetof(structname, member)); \
break; \ break; \
default: \ default: \
BUG(); \ __virtio_cread_many((vdev), \
offsetof(structname, member), \
&virtio_cread_v, \
1, \
sizeof(virtio_cread_v)); \
break; \
} \ } \
*(ptr) = virtio_to_cpu(vdev, virtio_cread_v); \
} while(0) } while(0)
/* Config space accessors. */ /* Config space accessors. */
#define virtio_cwrite(vdev, structname, member, ptr) \ #define virtio_cwrite(vdev, structname, member, ptr) \
do { \ do { \
might_sleep(); \ typeof(((structname*)0)->member) virtio_cwrite_v = \
/* Must match the member's type, and be integer */ \ cpu_to_virtio(vdev, *(ptr), ((structname*)0)->member); \
if (!typecheck(typeof((((structname*)0)->member)), *(ptr))) \
BUG_ON((*ptr) == 1); \
\ \
switch (sizeof(*ptr)) { \ might_sleep(); \
/* Sanity check: must match the member's type */ \
typecheck(typeof(virtio_to_cpu((vdev), virtio_cwrite_v)), *(ptr)); \
\
vdev->config->set((vdev), offsetof(structname, member), \
&virtio_cwrite_v, \
sizeof(virtio_cwrite_v)); \
} while(0)
/*
* Nothing virtio-specific about these, but let's worry about generalizing
* these later.
*/
#define virtio_le_to_cpu(x) \
_Generic((x), \
__u8: (u8)(x), \
__le16: (u16)le16_to_cpu(x), \
__le32: (u32)le32_to_cpu(x), \
__le64: (u64)le64_to_cpu(x) \
)
#define virtio_cpu_to_le(x, m) \
_Generic((m), \
__u8: (x), \
__le16: cpu_to_le16(x), \
__le32: cpu_to_le32(x), \
__le64: cpu_to_le64(x) \
)
/* LE (e.g. modern) Config space accessors. */
#define virtio_cread_le(vdev, structname, member, ptr) \
do { \
typeof(((structname*)0)->member) virtio_cread_v; \
\
might_sleep(); \
/* Sanity check: must match the member's type */ \
typecheck(typeof(virtio_le_to_cpu(virtio_cread_v)), *(ptr)); \
\
switch (sizeof(virtio_cread_v)) { \
case 1: \ case 1: \
virtio_cwrite8(vdev, \
offsetof(structname, member), \
*(ptr)); \
break; \
case 2: \ case 2: \
virtio_cwrite16(vdev, \
offsetof(structname, member), \
*(ptr)); \
break; \
case 4: \ case 4: \
virtio_cwrite32(vdev, \ vdev->config->get((vdev), \
offsetof(structname, member), \ offsetof(structname, member), \
*(ptr)); \ &virtio_cread_v, \
break; \ sizeof(virtio_cread_v)); \
case 8: \
virtio_cwrite64(vdev, \
offsetof(structname, member), \
*(ptr)); \
break; \ break; \
default: \ default: \
BUG(); \ __virtio_cread_many((vdev), \
offsetof(structname, member), \
&virtio_cread_v, \
1, \
sizeof(virtio_cread_v)); \
break; \
} \ } \
*(ptr) = virtio_le_to_cpu(virtio_cread_v); \
} while(0) } while(0)
#define virtio_cwrite_le(vdev, structname, member, ptr) \
do { \
typeof(((structname*)0)->member) virtio_cwrite_v = \
virtio_cpu_to_le(*(ptr), ((structname*)0)->member); \
\
might_sleep(); \
/* Sanity check: must match the member's type */ \
typecheck(typeof(virtio_le_to_cpu(virtio_cwrite_v)), *(ptr)); \
\
vdev->config->set((vdev), offsetof(structname, member), \
&virtio_cwrite_v, \
sizeof(virtio_cwrite_v)); \
} while(0)
/* Read @count fields, @bytes each. */ /* Read @count fields, @bytes each. */
static inline void __virtio_cread_many(struct virtio_device *vdev, static inline void __virtio_cread_many(struct virtio_device *vdev,
unsigned int offset, unsigned int offset,
@@ -399,53 +463,60 @@ static inline void virtio_cwrite8(struct virtio_device *vdev,
static inline u16 virtio_cread16(struct virtio_device *vdev, static inline u16 virtio_cread16(struct virtio_device *vdev,
unsigned int offset) unsigned int offset)
{ {
u16 ret; __virtio16 ret;
might_sleep(); might_sleep();
vdev->config->get(vdev, offset, &ret, sizeof(ret)); vdev->config->get(vdev, offset, &ret, sizeof(ret));
return virtio16_to_cpu(vdev, (__force __virtio16)ret); return virtio16_to_cpu(vdev, ret);
} }
static inline void virtio_cwrite16(struct virtio_device *vdev, static inline void virtio_cwrite16(struct virtio_device *vdev,
unsigned int offset, u16 val) unsigned int offset, u16 val)
{ {
__virtio16 v;
might_sleep(); might_sleep();
val = (__force u16)cpu_to_virtio16(vdev, val); v = cpu_to_virtio16(vdev, val);
vdev->config->set(vdev, offset, &val, sizeof(val)); vdev->config->set(vdev, offset, &v, sizeof(v));
} }
static inline u32 virtio_cread32(struct virtio_device *vdev, static inline u32 virtio_cread32(struct virtio_device *vdev,
unsigned int offset) unsigned int offset)
{ {
u32 ret; __virtio32 ret;
might_sleep(); might_sleep();
vdev->config->get(vdev, offset, &ret, sizeof(ret)); vdev->config->get(vdev, offset, &ret, sizeof(ret));
return virtio32_to_cpu(vdev, (__force __virtio32)ret); return virtio32_to_cpu(vdev, ret);
} }
static inline void virtio_cwrite32(struct virtio_device *vdev, static inline void virtio_cwrite32(struct virtio_device *vdev,
unsigned int offset, u32 val) unsigned int offset, u32 val)
{ {
__virtio32 v;
might_sleep(); might_sleep();
val = (__force u32)cpu_to_virtio32(vdev, val); v = cpu_to_virtio32(vdev, val);
vdev->config->set(vdev, offset, &val, sizeof(val)); vdev->config->set(vdev, offset, &v, sizeof(v));
} }
static inline u64 virtio_cread64(struct virtio_device *vdev, static inline u64 virtio_cread64(struct virtio_device *vdev,
unsigned int offset) unsigned int offset)
{ {
u64 ret; __virtio64 ret;
__virtio_cread_many(vdev, offset, &ret, 1, sizeof(ret)); __virtio_cread_many(vdev, offset, &ret, 1, sizeof(ret));
return virtio64_to_cpu(vdev, (__force __virtio64)ret); return virtio64_to_cpu(vdev, ret);
} }
static inline void virtio_cwrite64(struct virtio_device *vdev, static inline void virtio_cwrite64(struct virtio_device *vdev,
unsigned int offset, u64 val) unsigned int offset, u64 val)
{ {
__virtio64 v;
might_sleep(); might_sleep();
val = (__force u64)cpu_to_virtio64(vdev, val); v = cpu_to_virtio64(vdev, val);
vdev->config->set(vdev, offset, &val, sizeof(val)); vdev->config->set(vdev, offset, &v, sizeof(v));
} }
/* Conditional config space accessors. */ /* Conditional config space accessors. */
@@ -459,4 +530,14 @@ static inline void virtio_cwrite64(struct virtio_device *vdev,
_r; \ _r; \
}) })
/* Conditional config space accessors. */
#define virtio_cread_le_feature(vdev, fbit, structname, member, ptr) \
({ \
int _r = 0; \
if (!virtio_has_feature(vdev, fbit)) \
_r = -ENOENT; \
else \
virtio_cread_le((vdev), structname, member, ptr); \
_r; \
})
#endif /* _LINUX_VIRTIO_CONFIG_H */ #endif /* _LINUX_VIRTIO_CONFIG_H */

View File

@@ -46,16 +46,15 @@ static inline void virtio_wmb(bool weak_barriers)
dma_wmb(); dma_wmb();
} }
static inline void virtio_store_mb(bool weak_barriers, #define virtio_store_mb(weak_barriers, p, v) \
__virtio16 *p, __virtio16 v) do { \
{ if (weak_barriers) { \
if (weak_barriers) { virt_store_mb(*p, v); \
virt_store_mb(*p, v); } else { \
} else { WRITE_ONCE(*p, v); \
WRITE_ONCE(*p, v); mb(); \
mb(); } \
} } while (0) \
}
struct virtio_device; struct virtio_device;
struct virtqueue; struct virtqueue;

View File

@@ -91,6 +91,8 @@
/* Use message type V2 */ /* Use message type V2 */
#define VHOST_BACKEND_F_IOTLB_MSG_V2 0x1 #define VHOST_BACKEND_F_IOTLB_MSG_V2 0x1
/* IOTLB can accept batching hints */
#define VHOST_BACKEND_F_IOTLB_BATCH 0x2
#define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64) #define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64)
#define VHOST_GET_BACKEND_FEATURES _IOR(VHOST_VIRTIO, 0x26, __u64) #define VHOST_GET_BACKEND_FEATURES _IOR(VHOST_VIRTIO, 0x26, __u64)

View File

@@ -60,6 +60,17 @@ struct vhost_iotlb_msg {
#define VHOST_IOTLB_UPDATE 2 #define VHOST_IOTLB_UPDATE 2
#define VHOST_IOTLB_INVALIDATE 3 #define VHOST_IOTLB_INVALIDATE 3
#define VHOST_IOTLB_ACCESS_FAIL 4 #define VHOST_IOTLB_ACCESS_FAIL 4
/*
* VHOST_IOTLB_BATCH_BEGIN and VHOST_IOTLB_BATCH_END allow modifying
* multiple mappings in one go: beginning with
* VHOST_IOTLB_BATCH_BEGIN, followed by any number of
* VHOST_IOTLB_UPDATE messages, and ending with VHOST_IOTLB_BATCH_END.
* When one of these two values is used as the message type, the rest
* of the fields in the message are ignored. There's no guarantee that
* these changes take place automatically in the device.
*/
#define VHOST_IOTLB_BATCH_BEGIN 5
#define VHOST_IOTLB_BATCH_END 6
__u8 type; __u8 type;
}; };

View File

@@ -25,7 +25,7 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. */ * SUCH DAMAGE. */
#include <linux/types.h> #include <linux/virtio_types.h>
#include <linux/virtio_ids.h> #include <linux/virtio_ids.h>
#include <linux/virtio_config.h> #include <linux/virtio_config.h>
@@ -36,7 +36,7 @@
struct virtio_9p_config { struct virtio_9p_config {
/* length of the tag name */ /* length of the tag name */
__u16 tag_len; __virtio16 tag_len;
/* non-NULL terminated tag name */ /* non-NULL terminated tag name */
__u8 tag[0]; __u8 tag[0];
} __attribute__((packed)); } __attribute__((packed));

View File

@@ -45,20 +45,20 @@
#define VIRTIO_BALLOON_CMD_ID_DONE 1 #define VIRTIO_BALLOON_CMD_ID_DONE 1
struct virtio_balloon_config { struct virtio_balloon_config {
/* Number of pages host wants Guest to give up. */ /* Number of pages host wants Guest to give up. */
__u32 num_pages; __le32 num_pages;
/* Number of pages we've actually got in balloon. */ /* Number of pages we've actually got in balloon. */
__u32 actual; __le32 actual;
/* /*
* Free page hint command id, readonly by guest. * Free page hint command id, readonly by guest.
* Was previously named free_page_report_cmd_id so we * Was previously named free_page_report_cmd_id so we
* need to carry that name for legacy support. * need to carry that name for legacy support.
*/ */
union { union {
__u32 free_page_hint_cmd_id; __le32 free_page_hint_cmd_id;
__u32 free_page_report_cmd_id; /* deprecated */ __le32 free_page_report_cmd_id; /* deprecated */
}; };
/* Stores PAGE_POISON if page poisoning is in use */ /* Stores PAGE_POISON if page poisoning is in use */
__u32 poison_val; __le32 poison_val;
}; };
#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ #define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */

View File

@@ -57,20 +57,20 @@
struct virtio_blk_config { struct virtio_blk_config {
/* The capacity (in 512-byte sectors). */ /* The capacity (in 512-byte sectors). */
__u64 capacity; __virtio64 capacity;
/* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */ /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */
__u32 size_max; __virtio32 size_max;
/* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */ /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */
__u32 seg_max; __virtio32 seg_max;
/* geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */ /* geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */
struct virtio_blk_geometry { struct virtio_blk_geometry {
__u16 cylinders; __virtio16 cylinders;
__u8 heads; __u8 heads;
__u8 sectors; __u8 sectors;
} geometry; } geometry;
/* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
__u32 blk_size; __virtio32 blk_size;
/* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */ /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */
/* exponent for physical block per logical block. */ /* exponent for physical block per logical block. */
@@ -78,42 +78,42 @@ struct virtio_blk_config {
/* alignment offset in logical blocks. */ /* alignment offset in logical blocks. */
__u8 alignment_offset; __u8 alignment_offset;
/* minimum I/O size without performance penalty in logical blocks. */ /* minimum I/O size without performance penalty in logical blocks. */
__u16 min_io_size; __virtio16 min_io_size;
/* optimal sustained I/O size in logical blocks. */ /* optimal sustained I/O size in logical blocks. */
__u32 opt_io_size; __virtio32 opt_io_size;
/* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */
__u8 wce; __u8 wce;
__u8 unused; __u8 unused;
/* number of vqs, only available when VIRTIO_BLK_F_MQ is set */ /* number of vqs, only available when VIRTIO_BLK_F_MQ is set */
__u16 num_queues; __virtio16 num_queues;
/* the next 3 entries are guarded by VIRTIO_BLK_F_DISCARD */ /* the next 3 entries are guarded by VIRTIO_BLK_F_DISCARD */
/* /*
* The maximum discard sectors (in 512-byte sectors) for * The maximum discard sectors (in 512-byte sectors) for
* one segment. * one segment.
*/ */
__u32 max_discard_sectors; __virtio32 max_discard_sectors;
/* /*
* The maximum number of discard segments in a * The maximum number of discard segments in a
* discard command. * discard command.
*/ */
__u32 max_discard_seg; __virtio32 max_discard_seg;
/* Discard commands must be aligned to this number of sectors. */ /* Discard commands must be aligned to this number of sectors. */
__u32 discard_sector_alignment; __virtio32 discard_sector_alignment;
/* the next 3 entries are guarded by VIRTIO_BLK_F_WRITE_ZEROES */ /* the next 3 entries are guarded by VIRTIO_BLK_F_WRITE_ZEROES */
/* /*
* The maximum number of write zeroes sectors (in 512-byte sectors) in * The maximum number of write zeroes sectors (in 512-byte sectors) in
* one segment. * one segment.
*/ */
__u32 max_write_zeroes_sectors; __virtio32 max_write_zeroes_sectors;
/* /*
* The maximum number of segments in a write zeroes * The maximum number of segments in a write zeroes
* command. * command.
*/ */
__u32 max_write_zeroes_seg; __virtio32 max_write_zeroes_seg;
/* /*
* Set if a VIRTIO_BLK_T_WRITE_ZEROES request may result in the * Set if a VIRTIO_BLK_T_WRITE_ZEROES request may result in the
* deallocation of one or more of the sectors. * deallocation of one or more of the sectors.

View File

@@ -67,13 +67,17 @@
#define VIRTIO_F_VERSION_1 32 #define VIRTIO_F_VERSION_1 32
/* /*
* If clear - device has the IOMMU bypass quirk feature. * If clear - device has the platform DMA (e.g. IOMMU) bypass quirk feature.
* If set - use platform tools to detect the IOMMU. * If set - use platform DMA tools to access the memory.
* *
* Note the reverse polarity (compared to most other features), * Note the reverse polarity (compared to most other features),
* this is for compatibility with legacy systems. * this is for compatibility with legacy systems.
*/ */
#define VIRTIO_F_IOMMU_PLATFORM 33 #define VIRTIO_F_ACCESS_PLATFORM 33
#ifndef __KERNEL__
/* Legacy name for VIRTIO_F_ACCESS_PLATFORM (for compatibility with old userspace) */
#define VIRTIO_F_IOMMU_PLATFORM VIRTIO_F_ACCESS_PLATFORM
#endif /* __KERNEL__ */
/* This feature indicates support for the packed virtqueue layout. */ /* This feature indicates support for the packed virtqueue layout. */
#define VIRTIO_F_RING_PACKED 34 #define VIRTIO_F_RING_PACKED 34

View File

@@ -45,13 +45,13 @@
struct virtio_console_config { struct virtio_console_config {
/* colums of the screens */ /* colums of the screens */
__u16 cols; __virtio16 cols;
/* rows of the screens */ /* rows of the screens */
__u16 rows; __virtio16 rows;
/* max. number of ports this device can hold */ /* max. number of ports this device can hold */
__u32 max_nr_ports; __virtio32 max_nr_ports;
/* emergency write register */ /* emergency write register */
__u32 emerg_wr; __virtio32 emerg_wr;
} __attribute__((packed)); } __attribute__((packed));
/* /*

View File

@@ -414,33 +414,33 @@ struct virtio_crypto_op_data_req {
struct virtio_crypto_config { struct virtio_crypto_config {
/* See VIRTIO_CRYPTO_OP_* above */ /* See VIRTIO_CRYPTO_OP_* above */
__u32 status; __le32 status;
/* /*
* Maximum number of data queue * Maximum number of data queue
*/ */
__u32 max_dataqueues; __le32 max_dataqueues;
/* /*
* Specifies the services mask which the device support, * Specifies the services mask which the device support,
* see VIRTIO_CRYPTO_SERVICE_* above * see VIRTIO_CRYPTO_SERVICE_* above
*/ */
__u32 crypto_services; __le32 crypto_services;
/* Detailed algorithms mask */ /* Detailed algorithms mask */
__u32 cipher_algo_l; __le32 cipher_algo_l;
__u32 cipher_algo_h; __le32 cipher_algo_h;
__u32 hash_algo; __le32 hash_algo;
__u32 mac_algo_l; __le32 mac_algo_l;
__u32 mac_algo_h; __le32 mac_algo_h;
__u32 aead_algo; __le32 aead_algo;
/* Maximum length of cipher key */ /* Maximum length of cipher key */
__u32 max_cipher_key_len; __le32 max_cipher_key_len;
/* Maximum length of authenticated key */ /* Maximum length of authenticated key */
__u32 max_auth_key_len; __le32 max_auth_key_len;
__u32 reserve; __le32 reserve;
/* Maximum size of each crypto request's content */ /* Maximum size of each crypto request's content */
__u64 max_size; __le64 max_size;
}; };
struct virtio_crypto_inhdr { struct virtio_crypto_inhdr {

View File

@@ -13,7 +13,7 @@ struct virtio_fs_config {
__u8 tag[36]; __u8 tag[36];
/* Number of request queues */ /* Number of request queues */
__u32 num_request_queues; __le32 num_request_queues;
} __attribute__((packed)); } __attribute__((packed));
#endif /* _UAPI_LINUX_VIRTIO_FS_H */ #endif /* _UAPI_LINUX_VIRTIO_FS_H */

View File

@@ -320,10 +320,10 @@ struct virtio_gpu_resp_edid {
#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) #define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
struct virtio_gpu_config { struct virtio_gpu_config {
__u32 events_read; __le32 events_read;
__u32 events_clear; __le32 events_clear;
__u32 num_scanouts; __le32 num_scanouts;
__u32 num_capsets; __le32 num_capsets;
}; };
/* simple formats for fbcon/X use */ /* simple formats for fbcon/X use */

View File

@@ -40,18 +40,18 @@ enum virtio_input_config_select {
}; };
struct virtio_input_absinfo { struct virtio_input_absinfo {
__u32 min; __le32 min;
__u32 max; __le32 max;
__u32 fuzz; __le32 fuzz;
__u32 flat; __le32 flat;
__u32 res; __le32 res;
}; };
struct virtio_input_devids { struct virtio_input_devids {
__u16 bustype; __le16 bustype;
__u16 vendor; __le16 vendor;
__u16 product; __le16 product;
__u16 version; __le16 version;
}; };
struct virtio_input_config { struct virtio_input_config {

View File

@@ -18,24 +18,24 @@
#define VIRTIO_IOMMU_F_MMIO 5 #define VIRTIO_IOMMU_F_MMIO 5
struct virtio_iommu_range_64 { struct virtio_iommu_range_64 {
__u64 start; __le64 start;
__u64 end; __le64 end;
}; };
struct virtio_iommu_range_32 { struct virtio_iommu_range_32 {
__u32 start; __le32 start;
__u32 end; __le32 end;
}; };
struct virtio_iommu_config { struct virtio_iommu_config {
/* Supported page sizes */ /* Supported page sizes */
__u64 page_size_mask; __le64 page_size_mask;
/* Supported IOVA range */ /* Supported IOVA range */
struct virtio_iommu_range_64 input_range; struct virtio_iommu_range_64 input_range;
/* Max domain ID size */ /* Max domain ID size */
struct virtio_iommu_range_32 domain_range; struct virtio_iommu_range_32 domain_range;
/* Probe buffer size */ /* Probe buffer size */
__u32 probe_size; __le32 probe_size;
}; };
/* Request types */ /* Request types */

View File

@@ -185,27 +185,27 @@ struct virtio_mem_resp {
struct virtio_mem_config { struct virtio_mem_config {
/* Block size and alignment. Cannot change. */ /* Block size and alignment. Cannot change. */
__u64 block_size; __le64 block_size;
/* Valid with VIRTIO_MEM_F_ACPI_PXM. Cannot change. */ /* Valid with VIRTIO_MEM_F_ACPI_PXM. Cannot change. */
__u16 node_id; __le16 node_id;
__u8 padding[6]; __u8 padding[6];
/* Start address of the memory region. Cannot change. */ /* Start address of the memory region. Cannot change. */
__u64 addr; __le64 addr;
/* Region size (maximum). Cannot change. */ /* Region size (maximum). Cannot change. */
__u64 region_size; __le64 region_size;
/* /*
* Currently usable region size. Can grow up to region_size. Can * Currently usable region size. Can grow up to region_size. Can
* shrink due to VIRTIO_MEM_REQ_UNPLUG_ALL (in which case no config * shrink due to VIRTIO_MEM_REQ_UNPLUG_ALL (in which case no config
* update will be sent). * update will be sent).
*/ */
__u64 usable_region_size; __le64 usable_region_size;
/* /*
* Currently used size. Changes due to plug/unplug requests, but no * Currently used size. Changes due to plug/unplug requests, but no
* config updates will be sent. * config updates will be sent.
*/ */
__u64 plugged_size; __le64 plugged_size;
/* Requested size. New plug requests cannot exceed it. Can change. */ /* Requested size. New plug requests cannot exceed it. Can change. */
__u64 requested_size; __le64 requested_size;
}; };
#endif /* _LINUX_VIRTIO_MEM_H */ #endif /* _LINUX_VIRTIO_MEM_H */

View File

@@ -87,19 +87,19 @@ struct virtio_net_config {
/* The config defining mac address (if VIRTIO_NET_F_MAC) */ /* The config defining mac address (if VIRTIO_NET_F_MAC) */
__u8 mac[ETH_ALEN]; __u8 mac[ETH_ALEN];
/* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */
__u16 status; __virtio16 status;
/* Maximum number of each of transmit and receive queues; /* Maximum number of each of transmit and receive queues;
* see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ. * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ.
* Legal values are between 1 and 0x8000 * Legal values are between 1 and 0x8000
*/ */
__u16 max_virtqueue_pairs; __virtio16 max_virtqueue_pairs;
/* Default maximum transmit unit advice */ /* Default maximum transmit unit advice */
__u16 mtu; __virtio16 mtu;
/* /*
* speed, in units of 1Mb. All values 0 to INT_MAX are legal. * speed, in units of 1Mb. All values 0 to INT_MAX are legal.
* Any other value stands for unknown. * Any other value stands for unknown.
*/ */
__u32 speed; __le32 speed;
/* /*
* 0x00 - half duplex * 0x00 - half duplex
* 0x01 - full duplex * 0x01 - full duplex

View File

@@ -15,8 +15,8 @@
#include <linux/virtio_config.h> #include <linux/virtio_config.h>
struct virtio_pmem_config { struct virtio_pmem_config {
__u64 start; __le64 start;
__u64 size; __le64 size;
}; };
#define VIRTIO_PMEM_REQ_TYPE_FLUSH 0 #define VIRTIO_PMEM_REQ_TYPE_FLUSH 0

View File

@@ -103,16 +103,16 @@ struct virtio_scsi_event {
} __attribute__((packed)); } __attribute__((packed));
struct virtio_scsi_config { struct virtio_scsi_config {
__u32 num_queues; __virtio32 num_queues;
__u32 seg_max; __virtio32 seg_max;
__u32 max_sectors; __virtio32 max_sectors;
__u32 cmd_per_lun; __virtio32 cmd_per_lun;
__u32 event_info_size; __virtio32 event_info_size;
__u32 sense_size; __virtio32 sense_size;
__u32 cdb_size; __virtio32 cdb_size;
__u16 max_channel; __virtio16 max_channel;
__u16 max_target; __virtio16 max_target;
__u32 max_lun; __virtio32 max_lun;
} __attribute__((packed)); } __attribute__((packed));
/* Feature Bits */ /* Feature Bits */

View File

@@ -42,16 +42,16 @@ static inline void __virtio_clear_bit(struct virtio_device *vdev,
(__virtio_test_bit((dev), feature)) (__virtio_test_bit((dev), feature))
/** /**
* virtio_has_iommu_quirk - determine whether this device has the iommu quirk * virtio_has_dma_quirk - determine whether this device has the DMA quirk
* @vdev: the device * @vdev: the device
*/ */
static inline bool virtio_has_iommu_quirk(const struct virtio_device *vdev) static inline bool virtio_has_dma_quirk(const struct virtio_device *vdev)
{ {
/* /*
* Note the reverse polarity of the quirk feature (compared to most * Note the reverse polarity of the quirk feature (compared to most
* other features), this is for compatibility with legacy systems. * other features), this is for compatibility with legacy systems.
*/ */
return !virtio_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); return !virtio_has_feature(vdev, VIRTIO_F_ACCESS_PLATFORM);
} }
static inline bool virtio_is_little_endian(struct virtio_device *vdev) static inline bool virtio_is_little_endian(struct virtio_device *vdev)

View File

@@ -40,17 +40,21 @@ static int __connect(struct irq_bypass_producer *prod,
if (prod->add_consumer) if (prod->add_consumer)
ret = prod->add_consumer(prod, cons); ret = prod->add_consumer(prod, cons);
if (!ret) { if (ret)
ret = cons->add_producer(cons, prod); goto err_add_consumer;
if (ret && prod->del_consumer)
prod->del_consumer(prod, cons); ret = cons->add_producer(cons, prod);
} if (ret)
goto err_add_producer;
if (cons->start) if (cons->start)
cons->start(cons); cons->start(cons);
if (prod->start) if (prod->start)
prod->start(prod); prod->start(prod);
err_add_producer:
if (prod->del_consumer)
prod->del_consumer(prod, cons);
err_add_consumer:
return ret; return ret;
} }