#include <stdio.h>
#include <errno.h>
#include <unistd.h>
+#include <dirent.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
#include <rte_ethdev.h>
#include <rte_memcpy.h>
#include <rte_atomic.h>
#include <rte_branch_prediction.h>
#include <rte_ether.h>
-#include <rte_ethdev_driver.h>
+#include <ethdev_driver.h>
#include <rte_cycles.h>
#include <rte_errno.h>
#include <rte_memory.h>
#include <rte_eal.h>
#include <rte_dev.h>
#include <rte_bus_vmbus.h>
+#include <rte_alarm.h>
#include "hn_logs.h"
#include "hn_var.h"
#define NETVSC_ARG_TXBREAK "tx_copybreak"
#define NETVSC_ARG_RX_EXTMBUF_ENABLE "rx_extmbuf_enable"
+/* The max number of retry when hot adding a VF device */
+#define NETVSC_MAX_HOTADD_RETRY 10
+
struct hn_xstats_name_off {
char name[RTE_ETH_XSTATS_NAME_SIZE];
unsigned int offset;
return err;
}
+static void netvsc_hotplug_retry(void *args)
+{
+ int ret;
+ struct hn_data *hv = args;
+ struct rte_eth_dev *dev = &rte_eth_devices[hv->port_id];
+ struct rte_devargs *d = &hv->devargs;
+ char buf[256];
+
+ DIR *di;
+ struct dirent *dir;
+ struct ifreq req;
+ struct rte_ether_addr eth_addr;
+ int s;
+
+ PMD_DRV_LOG(DEBUG, "%s: retry count %d",
+ __func__, hv->eal_hot_plug_retry);
+
+ if (hv->eal_hot_plug_retry++ > NETVSC_MAX_HOTADD_RETRY)
+ return;
+
+ snprintf(buf, sizeof(buf), "/sys/bus/pci/devices/%s/net", d->name);
+ di = opendir(buf);
+ if (!di) {
+ PMD_DRV_LOG(DEBUG, "%s: can't open directory %s, "
+ "retrying in 1 second", __func__, buf);
+ goto retry;
+ }
+
+ while ((dir = readdir(di))) {
+ /* Skip . and .. directories */
+ if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))
+ continue;
+
+ /* trying to get mac address if this is a network device*/
+ s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (s == -1) {
+ PMD_DRV_LOG(ERR, "Failed to create socket errno %d",
+ errno);
+ break;
+ }
+ strlcpy(req.ifr_name, dir->d_name, sizeof(req.ifr_name));
+ ret = ioctl(s, SIOCGIFHWADDR, &req);
+ close(s);
+ if (ret == -1) {
+ PMD_DRV_LOG(ERR,
+ "Failed to send SIOCGIFHWADDR for device %s",
+ dir->d_name);
+ break;
+ }
+ if (req.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+ closedir(di);
+ return;
+ }
+ memcpy(eth_addr.addr_bytes, req.ifr_hwaddr.sa_data,
+ RTE_DIM(eth_addr.addr_bytes));
+
+ if (rte_is_same_ether_addr(ð_addr, dev->data->mac_addrs)) {
+ PMD_DRV_LOG(NOTICE,
+ "Found matching MAC address, adding device %s network name %s",
+ d->name, dir->d_name);
+ ret = rte_eal_hotplug_add(d->bus->name, d->name,
+ d->args);
+ if (ret) {
+ PMD_DRV_LOG(ERR,
+ "Failed to add PCI device %s",
+ d->name);
+ break;
+ }
+ }
+ /* When the code reaches here, we either have already added
+ * the device, or its MAC address did not match.
+ */
+ closedir(di);
+ return;
+ }
+ closedir(di);
+retry:
+ /* The device is still being initialized, retry after 1 second */
+ rte_eal_alarm_set(1000000, netvsc_hotplug_retry, hv);
+}
+
+static void
+netvsc_hotadd_callback(const char *device_name, enum rte_dev_event_type type,
+ void *arg)
+{
+ struct hn_data *hv = arg;
+ struct rte_devargs *d = &hv->devargs;
+ int ret;
+
+ PMD_DRV_LOG(INFO, "Device notification type=%d device_name=%s",
+ type, device_name);
+
+ switch (type) {
+ case RTE_DEV_EVENT_ADD:
+ /* if we already has a VF, don't check on hot add */
+ if (hv->vf_ctx.vf_state > vf_removed)
+ break;
+
+ ret = rte_devargs_parse(d, device_name);
+ if (ret) {
+ PMD_DRV_LOG(ERR,
+ "devargs parsing failed ret=%d", ret);
+ return;
+ }
+
+ if (!strcmp(d->bus->name, "pci")) {
+ /* Start the process of figuring out if this
+ * PCI device is a VF device
+ */
+ hv->eal_hot_plug_retry = 0;
+ rte_eal_alarm_set(1000000, netvsc_hotplug_retry, hv);
+ }
+
+ /* We will switch to VF on RDNIS configure message
+ * sent from VSP
+ */
+
+ break;
+ default:
+ break;
+ }
+}
+
static int hn_dev_configure(struct rte_eth_dev *dev)
{
struct rte_eth_conf *dev_conf = &dev->data->dev_conf;
}
}
- return hn_vf_configure(dev, dev_conf);
+ return hn_vf_configure_locked(dev, dev_conf);
}
static int hn_dev_stats_get(struct rte_eth_dev *dev,
PMD_INIT_FUNC_TRACE();
+ /* Register to monitor hot plug events */
+ error = rte_dev_event_callback_register(NULL, netvsc_hotadd_callback,
+ hv);
+ if (error) {
+ PMD_DRV_LOG(ERR, "failed to register device event callback");
+ return error;
+ }
+
error = hn_rndis_set_rxfilter(hv,
NDIS_PACKET_TYPE_BROADCAST |
NDIS_PACKET_TYPE_ALL_MULTICAST |
PMD_INIT_FUNC_TRACE();
dev->data->dev_started = 0;
+ rte_dev_event_callback_unregister(NULL, netvsc_hotadd_callback, hv);
hn_rndis_set_rxfilter(hv, 0);
return hn_vf_stop(dev);
}
hn_dev_close(struct rte_eth_dev *dev)
{
int ret;
+ struct hn_data *hv = dev->data->dev_private;
PMD_INIT_FUNC_TRACE();
if (rte_eal_process_type() != RTE_PROC_PRIMARY)
return 0;
+ rte_eal_alarm_cancel(netvsc_hotplug_retry, &hv->devargs);
+
ret = hn_vf_close(dev);
hn_dev_free_queues(dev);
hv->max_queues = 1;
rte_rwlock_init(&hv->vf_lock);
- hv->vf_port = HN_INVALID_PORT;
+ hv->vf_ctx.vf_vsc_switched = false;
+ hv->vf_ctx.vf_vsp_reported = false;
+ hv->vf_ctx.vf_attached = false;
+ hv->vf_ctx.vf_state = vf_unknown;
err = hn_parse_args(eth_dev);
if (err)
hv->max_queues = RTE_MIN(rxr_cnt, (unsigned int)max_chan);
/* If VF was reported but not added, do it now */
- if (hv->vf_present && !hn_vf_attached(hv)) {
+ if (hv->vf_ctx.vf_vsp_reported && !hv->vf_ctx.vf_vsc_switched) {
PMD_INIT_LOG(DEBUG, "Adding VF device");
err = hn_vf_add(eth_dev, hv);
- if (err)
- hv->vf_present = 0;
}
return 0;
PMD_INIT_FUNC_TRACE();
+ ret = rte_dev_event_monitor_start();
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to start device event monitoring");
+ return ret;
+ }
+
eth_dev = eth_dev_vmbus_allocate(dev, sizeof(struct hn_data));
if (!eth_dev)
return -ENOMEM;
ret = eth_hn_dev_init(eth_dev);
- if (ret)
+ if (ret) {
eth_dev_vmbus_release(eth_dev);
- else
+ rte_dev_event_monitor_stop();
+ } else {
rte_eth_dev_probing_finish(eth_dev);
+ }
return ret;
}
return ret;
eth_dev_vmbus_release(eth_dev);
+ rte_dev_event_monitor_stop();
return 0;
}