Commit b220f5f9 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by Greg Kroah-Hartman
Browse files

Staging: hv: add transmit flow control



Keep track of the number of pages sent over transmit and stop
before going over.

Signed-off-by: default avatarStephen Hemminger <shemminger@vyatta.com>
Acked-by: default avatarHank Janssen <hjanssen@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6048718d
Loading
Loading
Loading
Loading
+27 −45
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
struct net_device_context {
	/* point back to our device context */
	struct vm_device *device_ctx;
	unsigned long avail;
};

struct netvsc_driver_context {
@@ -52,8 +53,10 @@ struct netvsc_driver_context {
	struct netvsc_driver drv_obj;
};

/* Need at least MAX_SKB_FRAGS (18) + 1
   to handle worst case fragmented packet */
#define PACKET_PAGES_LOWATER  8
/* Need this many pages to handle worst case fragmented packet */
#define PACKET_PAGES_HIWATER  (MAX_SKB_FRAGS + 2)

static int ring_size = roundup_pow_of_two(2*MAX_SKB_FRAGS+1);
module_param(ring_size, int, S_IRUGO);
MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
@@ -122,15 +125,14 @@ static void netvsc_xmit_completion(void *context)

	if (skb) {
		struct net_device *net = skb->dev;
		dev_kfree_skb_any(skb);
		struct net_device_context *net_device_ctx = netdev_priv(net);
		unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2;

		if (netif_queue_stopped(net)) {
			DPRINT_INFO(NETVSC_DRV, "net device (%p) waking up...",
				    net);
		dev_kfree_skb_any(skb);

		if ((net_device_ctx->avail += num_pages) >= PACKET_PAGES_HIWATER)
 			netif_wake_queue(net);
	}
	}

	DPRINT_EXIT(NETVSC_DRV);
}
@@ -146,7 +148,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
	struct hv_netvsc_packet *packet;
	int ret;
	unsigned int i, num_pages;
	int retries = 0;

	DPRINT_ENTER(NETVSC_DRV);

@@ -155,14 +156,20 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)

	/* 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");
		return -1;

		dev_kfree_skb(skb);
		net->stats.tx_dropped++;
		return NETDEV_TX_OK;
	}

	packet->Extension = (void *)(unsigned long)packet +
@@ -198,52 +205,26 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
	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->stats.tx_bytes += skb->len;
		net->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->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->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 ret;
	return NETDEV_TX_OK;
}

/*
@@ -382,6 +363,7 @@ static int netvsc_probe(struct device *device)

	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 */