kfree(nvdev);
}
-static struct netvsc_device *get_outbound_net_device(struct hv_device *device)
-{
- struct netvsc_device *net_device = hv_device_to_netvsc_device(device);
- if (net_device && net_device->destroy)
- net_device = NULL;
+static inline bool netvsc_channel_idle(const struct netvsc_device *net_device,
+ u16 q_idx)
+{
+ const struct netvsc_channel *nvchan = &net_device->chan_table[q_idx];
- return net_device;
+ return atomic_read(&net_device->num_outstanding_recvs) == 0 &&
+ atomic_read(&nvchan->queue_sends) == 0;
}
-static struct netvsc_device *get_inbound_net_device(struct hv_device *device)
+static struct netvsc_device *get_outbound_net_device(struct hv_device *device)
{
struct netvsc_device *net_device = hv_device_to_netvsc_device(device);
- if (!net_device)
- goto get_in_err;
-
- if (net_device->destroy &&
- atomic_read(&net_device->num_outstanding_sends) == 0 &&
- atomic_read(&net_device->num_outstanding_recvs) == 0)
+ if (net_device && net_device->destroy)
net_device = NULL;
-get_in_err:
return net_device;
}
struct net_device *ndev = hv_get_drvdata(device);
struct net_device_context *net_device_ctx = netdev_priv(ndev);
struct vmbus_channel *channel = device->channel;
- int num_outstanding_sends;
u16 q_idx = 0;
int queue_sends;
dev_consume_skb_any(skb);
}
- num_outstanding_sends =
- atomic_dec_return(&net_device->num_outstanding_sends);
-
queue_sends =
atomic_dec_return(&net_device->chan_table[q_idx].queue_sends);
- if (net_device->destroy && num_outstanding_sends == 0)
+ if (net_device->destroy && queue_sends == 0)
wake_up(&net_device->wait_drain);
if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) &&
}
if (ret == 0) {
- atomic_inc(&net_device->num_outstanding_sends);
atomic_inc_return(&nvchan->queue_sends);
- if (ring_avail < RING_AVAIL_PERCENT_LOWATER) {
+ if (ring_avail < RING_AVAIL_PERCENT_LOWATER)
netif_tx_stop_queue(txq);
-
- if (atomic_read(&nvchan->queue_sends) < 1)
- netif_tx_wake_queue(txq);
- }
} else if (ret == -EAGAIN) {
netif_tx_stop_queue(txq);
if (atomic_read(&nvchan->queue_sends) < 1) {
else
device = channel->device_obj;
- net_device = get_inbound_net_device(device);
- if (!net_device)
+ ndev = hv_get_drvdata(device);
+ if (unlikely(!ndev))
return;
- ndev = hv_get_drvdata(device);
+ net_device = net_device_to_netvsc_device(ndev);
+ if (unlikely(net_device->destroy) &&
+ netvsc_channel_idle(net_device, q_idx))
+ return;
while ((desc = get_next_pkt_raw(channel)) != NULL) {
netvsc_process_raw_pkt(device, channel, net_device,
return ret;
}
+static bool netvsc_device_idle(const struct netvsc_device *nvdev)
+{
+ int i;
+
+ if (atomic_read(&nvdev->num_outstanding_recvs) > 0)
+ return false;
+
+ for (i = 0; i < nvdev->num_chn; i++) {
+ const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
+
+ if (atomic_read(&nvchan->queue_sends) > 0)
+ return false;
+ }
+
+ return true;
+}
+
static void rndis_filter_halt_device(struct rndis_device *dev)
{
struct rndis_request *request;
spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags);
/* Wait for all send completions */
- wait_event(nvdev->wait_drain,
- atomic_read(&nvdev->num_outstanding_sends) == 0 &&
- atomic_read(&nvdev->num_outstanding_recvs) == 0);
+ wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
if (request)
put_rndis_request(dev, request);