Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vhost-net: allow configuring extended features

Use the extended feature type for 'acked_features' and implement
two new ioctls operation allowing the user-space to set/query an
unbounded amount of features.

The actual number of processed features is limited by VIRTIO_FEATURES_MAX
and attempts to set features above such limit fail with
EOPNOTSUPP.

Note that: the legacy ioctls implicitly truncate the negotiated
features to the lower 64 bits range and the 'acked_backend_features'
field don't need conversion, as the only negotiated feature there
is in the low 64 bit range.

Acked-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+82 -23
+67 -20
drivers/vhost/net.c
··· 69 69 70 70 #define VHOST_DMA_IS_DONE(len) ((__force u32)(len) >= (__force u32)VHOST_DMA_DONE_LEN) 71 71 72 - enum { 73 - VHOST_NET_FEATURES = VHOST_FEATURES | 74 - (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) | 75 - (1ULL << VIRTIO_NET_F_MRG_RXBUF) | 76 - (1ULL << VIRTIO_F_ACCESS_PLATFORM) | 77 - (1ULL << VIRTIO_F_RING_RESET) 72 + static const u64 vhost_net_features[VIRTIO_FEATURES_DWORDS] = { 73 + VHOST_FEATURES | 74 + (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) | 75 + (1ULL << VIRTIO_NET_F_MRG_RXBUF) | 76 + (1ULL << VIRTIO_F_ACCESS_PLATFORM) | 77 + (1ULL << VIRTIO_F_RING_RESET), 78 78 }; 79 79 80 80 enum { ··· 1614 1614 return err; 1615 1615 } 1616 1616 1617 - static int vhost_net_set_features(struct vhost_net *n, u64 features) 1617 + static int vhost_net_set_features(struct vhost_net *n, const u64 *features) 1618 1618 { 1619 1619 size_t vhost_hlen, sock_hlen, hdr_len; 1620 1620 int i; 1621 1621 1622 - hdr_len = (features & ((1ULL << VIRTIO_NET_F_MRG_RXBUF) | 1623 - (1ULL << VIRTIO_F_VERSION_1))) ? 1624 - sizeof(struct virtio_net_hdr_mrg_rxbuf) : 1625 - sizeof(struct virtio_net_hdr); 1626 - if (features & (1 << VHOST_NET_F_VIRTIO_NET_HDR)) { 1622 + hdr_len = virtio_features_test_bit(features, VIRTIO_NET_F_MRG_RXBUF) || 1623 + virtio_features_test_bit(features, VIRTIO_F_VERSION_1) ? 1624 + sizeof(struct virtio_net_hdr_mrg_rxbuf) : 1625 + sizeof(struct virtio_net_hdr); 1626 + 1627 + if (virtio_features_test_bit(features, VHOST_NET_F_VIRTIO_NET_HDR)) { 1627 1628 /* vhost provides vnet_hdr */ 1628 1629 vhost_hlen = hdr_len; 1629 1630 sock_hlen = 0; ··· 1634 1633 sock_hlen = hdr_len; 1635 1634 } 1636 1635 mutex_lock(&n->dev.mutex); 1637 - if ((features & (1 << VHOST_F_LOG_ALL)) && 1636 + if (virtio_features_test_bit(features, VHOST_F_LOG_ALL) && 1638 1637 !vhost_log_access_ok(&n->dev)) 1639 1638 goto out_unlock; 1640 1639 1641 - if ((features & (1ULL << VIRTIO_F_ACCESS_PLATFORM))) { 1640 + if (virtio_features_test_bit(features, VIRTIO_F_ACCESS_PLATFORM)) { 1642 1641 if (vhost_init_device_iotlb(&n->dev)) 1643 1642 goto out_unlock; 1644 1643 } 1645 1644 1646 1645 for (i = 0; i < VHOST_NET_VQ_MAX; ++i) { 1647 1646 mutex_lock(&n->vqs[i].vq.mutex); 1648 - n->vqs[i].vq.acked_features = features; 1647 + virtio_features_copy(n->vqs[i].vq.acked_features_array, 1648 + features); 1649 1649 n->vqs[i].vhost_hlen = vhost_hlen; 1650 1650 n->vqs[i].sock_hlen = sock_hlen; 1651 1651 mutex_unlock(&n->vqs[i].vq.mutex); ··· 1683 1681 static long vhost_net_ioctl(struct file *f, unsigned int ioctl, 1684 1682 unsigned long arg) 1685 1683 { 1684 + u64 all_features[VIRTIO_FEATURES_DWORDS]; 1686 1685 struct vhost_net *n = f->private_data; 1687 1686 void __user *argp = (void __user *)arg; 1688 1687 u64 __user *featurep = argp; 1689 1688 struct vhost_vring_file backend; 1690 - u64 features; 1691 - int r; 1689 + u64 features, count, copied; 1690 + int r, i; 1692 1691 1693 1692 switch (ioctl) { 1694 1693 case VHOST_NET_SET_BACKEND: ··· 1697 1694 return -EFAULT; 1698 1695 return vhost_net_set_backend(n, backend.index, backend.fd); 1699 1696 case VHOST_GET_FEATURES: 1700 - features = VHOST_NET_FEATURES; 1697 + features = vhost_net_features[0]; 1701 1698 if (copy_to_user(featurep, &features, sizeof features)) 1702 1699 return -EFAULT; 1703 1700 return 0; 1704 1701 case VHOST_SET_FEATURES: 1705 1702 if (copy_from_user(&features, featurep, sizeof features)) 1706 1703 return -EFAULT; 1707 - if (features & ~VHOST_NET_FEATURES) 1704 + if (features & ~vhost_net_features[0]) 1708 1705 return -EOPNOTSUPP; 1709 - return vhost_net_set_features(n, features); 1706 + 1707 + virtio_features_from_u64(all_features, features); 1708 + return vhost_net_set_features(n, all_features); 1709 + case VHOST_GET_FEATURES_ARRAY: 1710 + if (copy_from_user(&count, featurep, sizeof(count))) 1711 + return -EFAULT; 1712 + 1713 + /* Copy the net features, up to the user-provided buffer size */ 1714 + argp += sizeof(u64); 1715 + copied = min(count, VIRTIO_FEATURES_DWORDS); 1716 + if (copy_to_user(argp, vhost_net_features, 1717 + copied * sizeof(u64))) 1718 + return -EFAULT; 1719 + 1720 + /* Zero the trailing space provided by user-space, if any */ 1721 + if (clear_user(argp, size_mul(count - copied, sizeof(u64)))) 1722 + return -EFAULT; 1723 + return 0; 1724 + case VHOST_SET_FEATURES_ARRAY: 1725 + if (copy_from_user(&count, featurep, sizeof(count))) 1726 + return -EFAULT; 1727 + 1728 + virtio_features_zero(all_features); 1729 + argp += sizeof(u64); 1730 + copied = min(count, VIRTIO_FEATURES_DWORDS); 1731 + if (copy_from_user(all_features, argp, copied * sizeof(u64))) 1732 + return -EFAULT; 1733 + 1734 + /* 1735 + * Any feature specified by user-space above 1736 + * VIRTIO_FEATURES_MAX is not supported by definition. 1737 + */ 1738 + for (i = copied; i < count; ++i) { 1739 + if (copy_from_user(&features, featurep + 1 + i, 1740 + sizeof(features))) 1741 + return -EFAULT; 1742 + if (features) 1743 + return -EOPNOTSUPP; 1744 + } 1745 + 1746 + for (i = 0; i < VIRTIO_FEATURES_DWORDS; i++) 1747 + if (all_features[i] & ~vhost_net_features[i]) 1748 + return -EOPNOTSUPP; 1749 + 1750 + return vhost_net_set_features(n, all_features); 1710 1751 case VHOST_GET_BACKEND_FEATURES: 1711 1752 features = VHOST_NET_BACKEND_FEATURES; 1712 1753 if (copy_to_user(featurep, &features, sizeof(features)))
+1 -1
drivers/vhost/vhost.c
··· 372 372 vq->log_used = false; 373 373 vq->log_addr = -1ull; 374 374 vq->private_data = NULL; 375 - vq->acked_features = 0; 375 + virtio_features_zero(vq->acked_features_array); 376 376 vq->acked_backend_features = 0; 377 377 vq->log_base = NULL; 378 378 vq->error_ctx = NULL;
+2 -2
drivers/vhost/vhost.h
··· 133 133 struct vhost_iotlb *umem; 134 134 struct vhost_iotlb *iotlb; 135 135 void *private_data; 136 - u64 acked_features; 136 + VIRTIO_DECLARE_FEATURES(acked_features); 137 137 u64 acked_backend_features; 138 138 /* Log write descriptors */ 139 139 void __user *log_base; ··· 291 291 292 292 static inline bool vhost_has_feature(struct vhost_virtqueue *vq, int bit) 293 293 { 294 - return vq->acked_features & (1ULL << bit); 294 + return virtio_features_test_bit(vq->acked_features_array, bit); 295 295 } 296 296 297 297 static inline bool vhost_backend_has_feature(struct vhost_virtqueue *vq, int bit)
+7
include/uapi/linux/vhost.h
··· 235 235 */ 236 236 #define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x82, \ 237 237 struct vhost_vring_state) 238 + 239 + /* Extended features manipulation */ 240 + #define VHOST_GET_FEATURES_ARRAY _IOR(VHOST_VIRTIO, 0x83, \ 241 + struct vhost_features_array) 242 + #define VHOST_SET_FEATURES_ARRAY _IOW(VHOST_VIRTIO, 0x83, \ 243 + struct vhost_features_array) 244 + 238 245 #endif
+5
include/uapi/linux/vhost_types.h
··· 110 110 }; 111 111 }; 112 112 113 + struct vhost_features_array { 114 + __u64 count; /* number of entries present in features array */ 115 + __u64 features[] __counted_by(count); 116 + }; 117 + 113 118 struct vhost_memory_region { 114 119 __u64 guest_phys_addr; 115 120 __u64 memory_size; /* bytes */