hyperv: Add handler for RNDIS_STATUS_NETWORK_CHANGE event

The RNDIS_STATUS_NETWORK_CHANGE event is received after the Hyper-V host
sleep or hibernation. We refresh network at this time.
MS-TFS: 135162

Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Haiyang Zhang 2014-06-19 18:34:36 -07:00 коммит произвёл David S. Miller
Родитель e0f802fbca
Коммит 3a494e7103
4 изменённых файлов: 30 добавлений и 25 удалений

Просмотреть файл

@ -170,6 +170,7 @@ struct rndis_device {
enum rndis_device_state state; enum rndis_device_state state;
bool link_state; bool link_state;
bool link_change;
atomic_t new_req_id; atomic_t new_req_id;
spinlock_t request_lock; spinlock_t request_lock;
@ -185,7 +186,7 @@ int netvsc_device_remove(struct hv_device *device);
int netvsc_send(struct hv_device *device, int netvsc_send(struct hv_device *device,
struct hv_netvsc_packet *packet); struct hv_netvsc_packet *packet);
void netvsc_linkstatus_callback(struct hv_device *device_obj, void netvsc_linkstatus_callback(struct hv_device *device_obj,
unsigned int status); struct rndis_message *resp);
int netvsc_recv_callback(struct hv_device *device_obj, int netvsc_recv_callback(struct hv_device *device_obj,
struct hv_netvsc_packet *packet, struct hv_netvsc_packet *packet,
struct ndis_tcp_ip_checksum_info *csum_info); struct ndis_tcp_ip_checksum_info *csum_info);

Просмотреть файл

@ -579,8 +579,9 @@ drop:
* netvsc_linkstatus_callback - Link up/down notification * netvsc_linkstatus_callback - Link up/down notification
*/ */
void netvsc_linkstatus_callback(struct hv_device *device_obj, void netvsc_linkstatus_callback(struct hv_device *device_obj,
unsigned int status) struct rndis_message *resp)
{ {
struct rndis_indicate_status *indicate = &resp->msg.indicate_status;
struct net_device *net; struct net_device *net;
struct net_device_context *ndev_ctx; struct net_device_context *ndev_ctx;
struct netvsc_device *net_device; struct netvsc_device *net_device;
@ -589,7 +590,19 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
net_device = hv_get_drvdata(device_obj); net_device = hv_get_drvdata(device_obj);
rdev = net_device->extension; rdev = net_device->extension;
rdev->link_state = status != 1; switch (indicate->status) {
case RNDIS_STATUS_MEDIA_CONNECT:
rdev->link_state = false;
break;
case RNDIS_STATUS_MEDIA_DISCONNECT:
rdev->link_state = true;
break;
case RNDIS_STATUS_NETWORK_CHANGE:
rdev->link_change = true;
break;
default:
return;
}
net = net_device->ndev; net = net_device->ndev;
@ -597,7 +610,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
return; return;
ndev_ctx = netdev_priv(net); ndev_ctx = netdev_priv(net);
if (status == 1) { if (!rdev->link_state) {
schedule_delayed_work(&ndev_ctx->dwork, 0); schedule_delayed_work(&ndev_ctx->dwork, 0);
schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20));
} else { } else {
@ -767,7 +780,9 @@ static void netvsc_link_change(struct work_struct *w)
struct net_device *net; struct net_device *net;
struct netvsc_device *net_device; struct netvsc_device *net_device;
struct rndis_device *rdev; struct rndis_device *rdev;
bool notify; 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(); rtnl_lock();
@ -782,10 +797,17 @@ static void netvsc_link_change(struct work_struct *w)
} else { } else {
netif_carrier_on(net); netif_carrier_on(net);
notify = true; notify = true;
if (rdev->link_change) {
rdev->link_change = false;
refresh = true;
}
} }
rtnl_unlock(); rtnl_unlock();
if (refresh)
call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
if (notify) if (notify)
netdev_notify_peers(net); netdev_notify_peers(net);
} }

Просмотреть файл

@ -320,25 +320,6 @@ static void rndis_filter_receive_response(struct rndis_device *dev,
} }
} }
static void rndis_filter_receive_indicate_status(struct rndis_device *dev,
struct rndis_message *resp)
{
struct rndis_indicate_status *indicate =
&resp->msg.indicate_status;
if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) {
netvsc_linkstatus_callback(
dev->net_dev->dev, 1);
} else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) {
netvsc_linkstatus_callback(
dev->net_dev->dev, 0);
} else {
/*
* TODO:
*/
}
}
/* /*
* Get the Per-Packet-Info with the specified type * Get the Per-Packet-Info with the specified type
* return NULL if not found. * return NULL if not found.
@ -464,7 +445,7 @@ int rndis_filter_receive(struct hv_device *dev,
case RNDIS_MSG_INDICATE: case RNDIS_MSG_INDICATE:
/* notification msgs */ /* notification msgs */
rndis_filter_receive_indicate_status(rndis_dev, rndis_msg); netvsc_linkstatus_callback(dev, rndis_msg);
break; break;
default: default:
netdev_err(ndev, netdev_err(ndev,

Просмотреть файл

@ -65,6 +65,7 @@
#define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 #define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012
#define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION #define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION
#define RNDIS_STATUS_LINK_SPEED_CHANGE 0x40010013L #define RNDIS_STATUS_LINK_SPEED_CHANGE 0x40010013L
#define RNDIS_STATUS_NETWORK_CHANGE 0x40010018
#define RNDIS_STATUS_NOT_RESETTABLE 0x80010001 #define RNDIS_STATUS_NOT_RESETTABLE 0x80010001
#define RNDIS_STATUS_SOFT_ERRORS 0x80010003 #define RNDIS_STATUS_SOFT_ERRORS 0x80010003