/* * netvsc_linkstatus_callback - Link up/down notification */ static void netvsc_linkstatus_callback(struct hv_device *device_obj, unsigned int status) { struct vm_device *device_ctx = to_vm_device(device_obj); struct net_device *net = dev_get_drvdata(&device_ctx->device); struct net_device_context *ndev_ctx; DPRINT_ENTER(NETVSC_DRV); if (!net) { DPRINT_ERR(NETVSC_DRV, "got link status but net device " "not initialized yet"); return; } if (status == 1) { netif_carrier_on(net); netif_wake_queue(net); netif_notify_peers(net); ndev_ctx = netdev_priv(net); schedule_work(&ndev_ctx->work); } else { netif_carrier_off(net); netif_stop_queue(net); } DPRINT_EXIT(NETVSC_DRV); }
/* * Send GARP packet to network peers after migrations. * After Quick Migration, the network is not immediately operational in the * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add * another netif_notify_peers() into a scheduled work, otherwise GARP packet * will not be sent after quick migration, and cause network disconnection. */ static void netvsc_send_garp(struct work_struct *w) { struct net_device_context *ndev_ctx; struct net_device *net; msleep(20); ndev_ctx = container_of(w, struct net_device_context, work); net = dev_get_drvdata(&ndev_ctx->device_ctx->device); netif_notify_peers(net); }
/* * Send GARP packet to network peers after migrations. * After Quick Migration, the network is not immediately operational in the * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add * another netif_notify_peers() into a delayed work, otherwise GARP packet * will not be sent after quick migration, and cause network disconnection. * Also, we update the carrier status here. */ static void netvsc_link_change(void *data) { struct delayed_work *w = (struct delayed_work *)data; struct net_device_context *ndev_ctx; struct net_device *net; struct netvsc_device *net_device; struct rndis_device *rdev; bool notify, refresh = false; char *argv[] = { "/etc/init.d/network", "restart", NULL }; char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; rtnl_lock(); ndev_ctx = container_of(w, struct net_device_context, dwork); net_device = hv_get_drvdata(ndev_ctx->device_ctx); rdev = net_device->extension; net = net_device->ndev; if (rdev->link_state) { netif_carrier_off(net); notify = false; } else { netif_carrier_on(net); notify = true; if (rdev->link_change) { rdev->link_change = false; refresh = true; } } rtnl_unlock(); if (refresh) call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); if (notify) netif_notify_peers(net); }