[libvirt] [PATCH] SRIOV NIC offload feature discovery
Ján Tomko
jtomko at redhat.com
Tue Feb 17 12:13:45 UTC 2015
On Mon, Feb 16, 2015 at 10:18:32AM +0000, James Chapman wrote:
> Adding functionality to libvirt that will allow it
> query the ethtool interface for the availability
> of certain NIC HW offload features
> ---
> src/conf/device_conf.h | 6 ++
> src/conf/node_device_conf.c | 7 ++
> src/conf/node_device_conf.h | 2 +
> src/libvirt_private.syms | 1 +
> src/node_device/node_device_udev.c | 4 ++
> src/util/virnetdev.c | 134 +++++++++++++++++++++++++++++++++++++
> src/util/virnetdev.h | 12 +++-
> 7 files changed, 165 insertions(+), 1 deletion(-)
>
> diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h
> index 7256cdc..091f2f0 100644
> --- a/src/conf/device_conf.h
> +++ b/src/conf/device_conf.h
> @@ -62,6 +62,12 @@ struct _virInterfaceLink {
> unsigned int speed; /* link speed in Mbits per second */
> };
>
> +typedef struct _virDevFeature virDevFeature;
> +typedef virDevFeature *virDevFeaturePtr;
> +struct _virDevFeature {
> + char *name; /* device feature */
> +};
> +
> int virDevicePCIAddressIsValid(virDevicePCIAddressPtr addr);
>
> int virDevicePCIAddressParseXML(xmlNodePtr node,
> diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
> index a728a00..7f4dbfe 100644
> --- a/src/conf/node_device_conf.c
> +++ b/src/conf/node_device_conf.c
> @@ -437,6 +437,12 @@ char *virNodeDeviceDefFormat(const virNodeDeviceDef *def)
> virBufferEscapeString(&buf, "<address>%s</address>\n",
> data->net.address);
> virInterfaceLinkFormat(&buf, &data->net.lnk);
> + if (data->net.features) {
> + for (i = 0; i < data->net.nfeatures; i++) {
> + virBufferAsprintf(&buf, "<feature name='%s'/>\n",
> + data->net.features[i].name);
Storing these as an enum (for example virNodeNetDevFeature) in a
virBitmap would be nicer, in my opinion.
> + }
> + }
> if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) {
> const char *subtyp =
> virNodeDevNetCapTypeToString(data->net.subtype);
> @@ -1679,6 +1685,7 @@ void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
> case VIR_NODE_DEV_CAP_NET:
> VIR_FREE(data->net.ifname);
> VIR_FREE(data->net.address);
> + VIR_FREE(data->net.features);
> break;
> case VIR_NODE_DEV_CAP_SCSI_HOST:
> VIR_FREE(data->scsi_host.wwnn);
> diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
> index fd5d179..918523a 100644
> --- a/src/conf/node_device_conf.h
> +++ b/src/conf/node_device_conf.h
> @@ -141,6 +141,8 @@ struct _virNodeDevCapsDef {
> char *ifname;
> virInterfaceLink lnk;
> virNodeDevNetCapType subtype; /* LAST -> no subtype */
> + size_t nfeatures;
> + virDevFeaturePtr features;
> } net;
> struct {
> unsigned int host;
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index c07a561..1d165a9 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1659,6 +1659,7 @@ virNetDevAddRoute;
> virNetDevClearIPAddress;
> virNetDevDelMulti;
> virNetDevExists;
> +virNetDevGetFeatures;
> virNetDevGetIndex;
> virNetDevGetIPv4Address;
> virNetDevGetLinkInfo;
> diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
> index 03c7a0b..349733f 100644
> --- a/src/node_device/node_device_udev.c
> +++ b/src/node_device/node_device_udev.c
> @@ -719,6 +719,10 @@ static int udevProcessNetworkInterface(struct udev_device *device,
> if (virNetDevGetLinkInfo(data->net.ifname, &data->net.lnk) < 0)
> goto out;
>
> + if (virNetDevGetFeatures(data->net.ifname, &data->net.features,
> + &data->net.nfeatures) < 0)
> + goto out;
> +
> ret = 0;
>
> out:
> diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
> index 2a0eb43..e513c85 100644
> --- a/src/util/virnetdev.c
> +++ b/src/util/virnetdev.c
> @@ -2728,3 +2728,137 @@ int virNetDevGetRxFilter(const char *ifname,
> *filter = fil;
> return ret;
> }
> +
> +#if defined(SIOCETHTOOL) && defined(HAVE_STRUCT_IFREQ)
> +
> +/**
> + * _virNetDevFeatureAvailable
> + * This function checks for the availability of a network device feature
> + *
> + * @ifname: name of the interface
> + * @cmd: reference to an ethtool command structure
> + *
> + * Returns 0 on success, -1 on failure.
> + */
> +int
> +_virNetDevFeatureAvailable(const char *ifname, struct ethtool_value *cmd)
We don't use a special prefix for internal function, we just define them
as static.
> +{
> + int ret = -1;
> + int sock = -1;
> + virIfreq ifr;
> +
> + sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
Can 'sock' be reused just like cmd?
> + if (sock < 0) {
> + virReportSystemError(errno, "%s", _("Cannot open control socket"));
> + goto cleanup;
> + }
> +
> + strcpy(ifr.ifr_name, ifname);
> + ifr.ifr_data = (void*) cmd;
> +
> + if (ioctl(sock, SIOCETHTOOL, &ifr) != 0) {
> + /* Privileged command, no error */
> + if (errno == EPERM || errno == EINVAL) {
> + virReportSystemError(errno, "%s", _("ioctl"));
> + /* Some kernels dont support named feature, no error */
If the failure is ignored, reporting an error spams the log, a VIR_DEBUG
would be nicer.
Also, the messages aren't very descriptive.
> + } else if (errno == EOPNOTSUPP) {
> + virReportSystemError(errno, "%s", _("Warning"));
> + } else {
> + virReportSystemError(errno, "%s", _("Error"));
> + goto cleanup;
> + }
> + }
> +
> + ret = cmd->data > 0 ? 1: 0;
> + cleanup:
> + if (sock)
> + VIR_FORCE_CLOSE(sock);
> +
> + return ret;
> +}
> +
> +
> +/**
> + * virNetDevGetFeatures:
> + * This function gets the nic offloads features available for ifname
> + *
> + * @ifname: name of the interface
> + * @features: network device feature structures
> + * @nfeatures: number of features available
> + *
> + * Returns 0 on success, -1 on failure.
> + */
> +int
> +virNetDevGetFeatures(const char *ifname,
> + virDevFeaturePtr *features,
> + size_t *nfeatures)
> +{
> + int ret = -1;
> + size_t i = -1;
> + size_t j = -1;
> + struct ethtool_value cmd;
> +
> + struct elem{
> + const char *name;
> + const int cmd;
> + };
> + /* legacy ethtool getters */
> + struct elem cmds[] = {
> + {"rx", ETHTOOL_GRXCSUM},
> + {"tx", ETHTOOL_GTXCSUM },
> + {"sg", ETHTOOL_GSG},
> + {"tso", ETHTOOL_GTSO},
> + {"gso", ETHTOOL_GGSO},
> + {"gro", ETHTOOL_GGRO},
> + };
> + /* ethtool masks */
> + struct elem flags[] = {
> + {"lro", ETH_FLAG_LRO},
> + {"rxvlan", ETH_FLAG_RXVLAN},
> + {"txvlan", ETH_FLAG_TXVLAN},
> + {"ntuple", ETH_FLAG_NTUPLE},
> + {"rxhash", ETH_FLAG_RXHASH},
> + };
> +
> + for (i = 0; i < (sizeof(cmds)/sizeof(struct elem)); i++) {
> + cmd.cmd = cmds[i].cmd;
> + if (_virNetDevFeatureAvailable(ifname, &cmd)) {
> + if (VIR_EXPAND_N(*features, *nfeatures, 1) < 0)
> + goto cleanup;
> + if ((ret = VIR_STRDUP((*features)[i].name, cmds[i].name)) != 1)
Here, the value of i is unrelated to the size of the allocated array.
The daemon crashes on startup on an interface that only has the "gro"
feature.
The VIR_APPEND_ELEMENT can be used here. (Or virBitmapSetBit to match
the suggestion I made above)
> + goto cleanup;
> + }
> + }
> +
> + cmd.cmd = ETHTOOL_GFLAGS;
> + for (j = 0; j < (sizeof(flags)/sizeof(struct elem)); j++) {
We have the ARRAY_CARDINALITY macro for this.
Jan
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20150217/c2dc5528/attachment-0001.sig>
More information about the libvir-list
mailing list