static int netvsc_open(struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); struct driver_context *driver_ctx = driver_to_driver_context(net_device_ctx->device_ctx->device.driver); struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context *)driver_ctx; struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj; struct hv_device *device_obj = &net_device_ctx->device_ctx->device_obj; int ret = 0; DPRINT_ENTER(NETVSC_DRV); if (netif_carrier_ok(net)) { memset(&net_device_ctx->stats, 0, sizeof(struct net_device_stats)); /* Open up the device */ ret = net_drv_obj->OnOpen(device_obj); if (ret != 0) { DPRINT_ERR(NETVSC_DRV, "unable to open device (ret %d).", ret); return ret; } netif_start_queue(net); } else { DPRINT_ERR(NETVSC_DRV, "unable to open device...link is down."); } DPRINT_EXIT(NETVSC_DRV); return ret; }
static int netvsc_remove(struct device *device) { struct driver_context *driver_ctx = driver_to_driver_context(device->driver); struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context *)driver_ctx; struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj; struct vm_device *device_ctx = device_to_vm_device(device); struct net_device *net = dev_get_drvdata(&device_ctx->device); struct hv_device *device_obj = &device_ctx->device_obj; int ret; DPRINT_ENTER(NETVSC_DRV); if (net == NULL) { DPRINT_INFO(NETVSC, "no net device to remove"); DPRINT_EXIT(NETVSC_DRV); return 0; } if (!net_drv_obj->Base.OnDeviceRemove) { DPRINT_EXIT(NETVSC_DRV); return -1; } /* Stop outbound asap */ netif_stop_queue(net); /* netif_carrier_off(net); */ unregister_netdev(net); /* * Call to the vsc driver to let it know that the device is being * removed */ ret = net_drv_obj->Base.OnDeviceRemove(device_obj); if (ret != 0) { /* TODO: */ DPRINT_ERR(NETVSC, "unable to remove vsc device (ret %d)", ret); } free_netdev(net); DPRINT_EXIT(NETVSC_DRV); return ret; }
static int netvsc_close(struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); struct driver_context *driver_ctx = driver_to_driver_context(net_device_ctx->device_ctx->device.driver); struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context *)driver_ctx; struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj; struct hv_device *device_obj = &net_device_ctx->device_ctx->device_obj; int ret; DPRINT_ENTER(NETVSC_DRV); netif_stop_queue(net); ret = net_drv_obj->OnClose(device_obj); if (ret != 0) DPRINT_ERR(NETVSC_DRV, "unable to close device (ret %d).", ret); DPRINT_EXIT(NETVSC_DRV); return ret; }
static int netvsc_probe(struct device *device) { struct driver_context *driver_ctx = driver_to_driver_context(device->driver); struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context *)driver_ctx; struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj; struct vm_device *device_ctx = device_to_vm_device(device); struct hv_device *device_obj = &device_ctx->device_obj; struct net_device *net = NULL; struct net_device_context *net_device_ctx; struct netvsc_device_info device_info; int ret; DPRINT_ENTER(NETVSC_DRV); if (!net_drv_obj->Base.OnDeviceAdd) return -1; net = alloc_etherdev(sizeof(struct net_device_context)); if (!net) return -1; /* Set initial state */ netif_carrier_off(net); netif_stop_queue(net); net_device_ctx = netdev_priv(net); net_device_ctx->device_ctx = device_ctx; net_device_ctx->avail = ring_size; dev_set_drvdata(device, net); /* Notify the netvsc driver of the new device */ ret = net_drv_obj->Base.OnDeviceAdd(device_obj, &device_info); if (ret != 0) { free_netdev(net); dev_set_drvdata(device, NULL); DPRINT_ERR(NETVSC_DRV, "unable to add netvsc device (ret %d)", ret); return ret; } /* * If carrier is still off ie we did not get a link status callback, * update it if necessary */ /* * FIXME: We should use a atomic or test/set instead to avoid getting * out of sync with the device's link status */ if (!netif_carrier_ok(net)) if (!device_info.LinkState) netif_carrier_on(net); memcpy(net->dev_addr, device_info.MacAddr, ETH_ALEN); net->netdev_ops = &device_ops; /* TODO: Add GSO and Checksum offload */ net->features = NETIF_F_SG; SET_ETHTOOL_OPS(net, ðtool_ops); SET_NETDEV_DEV(net, device); ret = register_netdev(net); if (ret != 0) { /* Remove the device and release the resource */ net_drv_obj->Base.OnDeviceRemove(device_obj); free_netdev(net); } DPRINT_EXIT(NETVSC_DRV); return ret; }
static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); struct driver_context *driver_ctx = driver_to_driver_context(net_device_ctx->device_ctx->device.driver); struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context *)driver_ctx; struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj; struct hv_netvsc_packet *packet; int ret; unsigned int i, num_pages; DPRINT_ENTER(NETVSC_DRV); DPRINT_DBG(NETVSC_DRV, "xmit packet - len %d data_len %d", skb->len, skb->data_len); /* Add 1 for skb->data and additional one for RNDIS */ num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; if (num_pages > net_device_ctx->avail) return NETDEV_TX_BUSY; /* Allocate a netvsc packet based on # of frags. */ packet = kzalloc(sizeof(struct hv_netvsc_packet) + (num_pages * sizeof(struct hv_page_buffer)) + net_drv_obj->RequestExtSize, GFP_ATOMIC); if (!packet) { /* out of memory, silently drop packet */ DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet"); dev_kfree_skb(skb); net->stats.tx_dropped++; return NETDEV_TX_OK; } packet->Extension = (void *)(unsigned long)packet + sizeof(struct hv_netvsc_packet) + (num_pages * sizeof(struct hv_page_buffer)); /* Setup the rndis header */ packet->PageBufferCount = num_pages; /* TODO: Flush all write buffers/ memory fence ??? */ /* wmb(); */ /* Initialize it from the skb */ packet->TotalDataBufferLength = skb->len; /* Start filling in the page buffers starting after RNDIS buffer. */ packet->PageBuffers[1].Pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; packet->PageBuffers[1].Offset = (unsigned long)skb->data & (PAGE_SIZE - 1); packet->PageBuffers[1].Length = skb_headlen(skb); /* Additional fragments are after SKB data */ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_frag_t *f = &skb_shinfo(skb)->frags[i]; packet->PageBuffers[i+2].Pfn = page_to_pfn(f->page); packet->PageBuffers[i+2].Offset = f->page_offset; packet->PageBuffers[i+2].Length = f->size; } /* Set the completion routine */ packet->Completion.Send.OnSendCompletion = netvsc_xmit_completion; packet->Completion.Send.SendCompletionContext = packet; packet->Completion.Send.SendCompletionTid = (unsigned long)skb; ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj, packet); if (ret == 0) { net->stats.tx_bytes += skb->len; net->stats.tx_packets++; DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu", net->stats.tx_packets, net->stats.tx_bytes); if ((net_device_ctx->avail -= num_pages) < PACKET_PAGES_LOWATER) netif_stop_queue(net); } else { /* we are shutting down or bus overloaded, just drop packet */ net->stats.tx_dropped++; netvsc_xmit_completion(packet); } DPRINT_EXIT(NETVSC_DRV); return NETDEV_TX_OK; }
static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); struct driver_context *driver_ctx = driver_to_driver_context(net_device_ctx->device_ctx->device.driver); struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context *)driver_ctx; struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj; struct hv_netvsc_packet *packet; int i; int ret; int num_frags; int retries = 0; DPRINT_ENTER(NETVSC_DRV); /* Support only 1 chain of frags */ ASSERT(skb_shinfo(skb)->frag_list == NULL); ASSERT(skb->dev == net); DPRINT_DBG(NETVSC_DRV, "xmit packet - len %d data_len %d", skb->len, skb->data_len); /* Add 1 for skb->data and any additional ones requested */ num_frags = skb_shinfo(skb)->nr_frags + 1 + net_drv_obj->AdditionalRequestPageBufferCount; /* Allocate a netvsc packet based on # of frags. */ packet = kzalloc(sizeof(struct hv_netvsc_packet) + (num_frags * sizeof(struct hv_page_buffer)) + net_drv_obj->RequestExtSize, GFP_ATOMIC); if (!packet) { DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet"); return -1; } packet->Extension = (void *)(unsigned long)packet + sizeof(struct hv_netvsc_packet) + (num_frags * sizeof(struct hv_page_buffer)); /* Setup the rndis header */ packet->PageBufferCount = num_frags; /* TODO: Flush all write buffers/ memory fence ??? */ /* wmb(); */ /* Initialize it from the skb */ ASSERT(skb->data); packet->TotalDataBufferLength = skb->len; /* * Start filling in the page buffers starting at * AdditionalRequestPageBufferCount offset */ packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Offset = (unsigned long)skb->data & (PAGE_SIZE - 1); packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Length = skb->len - skb->data_len; ASSERT((skb->len - skb->data_len) <= PAGE_SIZE); for (i = net_drv_obj->AdditionalRequestPageBufferCount + 1; i < num_frags; i++) { packet->PageBuffers[i].Pfn = page_to_pfn(skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].page); packet->PageBuffers[i].Offset = skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].page_offset; packet->PageBuffers[i].Length = skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].size; } /* Set the completion routine */ packet->Completion.Send.OnSendCompletion = netvsc_xmit_completion; packet->Completion.Send.SendCompletionContext = packet; packet->Completion.Send.SendCompletionTid = (unsigned long)skb; retry_send: ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj, packet); if (ret == 0) { ret = NETDEV_TX_OK; net_device_ctx->stats.tx_bytes += skb->len; net_device_ctx->stats.tx_packets++; } else { retries++; if (retries < 4) { DPRINT_ERR(NETVSC_DRV, "unable to send..." "retrying %d...", retries); udelay(100); goto retry_send; } /* no more room or we are shutting down */ DPRINT_ERR(NETVSC_DRV, "unable to send (%d)..." "marking net device (%p) busy", ret, net); DPRINT_INFO(NETVSC_DRV, "net device (%p) stopping", net); ret = NETDEV_TX_BUSY; net_device_ctx->stats.tx_dropped++; netif_stop_queue(net); /* * Null it since the caller will free it instead of the * completion routine */ packet->Completion.Send.SendCompletionTid = 0; /* * Release the resources since we will not get any send * completion */ netvsc_xmit_completion((void *)packet); } DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu", net_device_ctx->stats.tx_packets, net_device_ctx->stats.tx_bytes); DPRINT_EXIT(NETVSC_DRV); return ret; }