Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef529943 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "net: usbnet: Perform IPA initialization after IPA is ready"

parents 118a4fd8 8c949509
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1394,8 +1394,8 @@ static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)

		if (pkt_cnt == 0) {
			/* Skip IP alignment psudo header */
			skb_pull(skb, 2);
			skb->len = pkt_len;
			skb_pull(skb, NET_IP_ALIGN);
			skb_set_tail_pointer(skb, pkt_len);
			skb->truesize = pkt_len + sizeof(struct sk_buff);
			ax88179_rx_checksum(skb, pkt_hdr);
@@ -1405,7 +1405,7 @@ static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
		ax_skb = skb_clone(skb, GFP_ATOMIC);
		if (ax_skb) {
			ax_skb->len = pkt_len;
			ax_skb->data = skb->data + 2;
			skb_pull(ax_skb, NET_IP_ALIGN);
			skb_set_tail_pointer(ax_skb, pkt_len);
			ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
			ax88179_rx_checksum(ax_skb, pkt_hdr);
+286 −16
Original line number Diff line number Diff line
@@ -45,7 +45,9 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/pm_runtime.h>

#include <linux/debugfs.h>
#include <linux/types.h>
#include <linux/ipa.h>
#define DRIVER_VERSION		"22-Aug-2005"


@@ -62,7 +64,7 @@
 * more before an irq is required, under load.  Jumbograms change
 * the equation.
 */
#define	MAX_QUEUE_MEMORY	(60 * 1518)
#define	MAX_QUEUE_MEMORY	(600 * 1518)
#define	RX_QLEN(dev)		((dev)->rx_qlen)
#define	TX_QLEN(dev)		((dev)->tx_qlen)

@@ -77,6 +79,9 @@
// between wakeups
#define UNLINK_TIMEOUT_MS	3

/* timeout value for odu bridge resources */
#define IPA_ODU_RM_TIMEOUT_MSEC 10000

/*-------------------------------------------------------------------------*/

// randomly generated ethernet address
@@ -325,14 +330,60 @@ static void __usbnet_status_stop_force(struct usbnet *dev)
void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
{
	int	status;
	struct sk_buff *pend_skb;
	struct usbnet_ipa_ctx	*punet_ipa = dev->pusbnet_ipa;
	struct	ipa_tx_meta ipa_meta = {0x0};
	u32	pkts_to_send, ret, qlen = 0;
	u8	protocol_type = (skb->data[ETH_HLEN] & 0xf0);

	/* Pass IPv4 and IPv6 packets to IPA by peeking into the IP header */
	if (enable_ipa_bridge && (protocol_type == 0x40 ||
	    protocol_type == 0x60)) {
		spin_lock(&dev->ipa_pendq.lock);
		qlen = skb_queue_len(&dev->ipa_pendq);
		/* drop pkts */
		if (!dev->ipa_free_desc_cnt &&
		    qlen > (2 * USBNET_IPA_SYS_PIPE_DNE_PKTS)) {
			pr_debug_ratelimited("drop pkt ipa pending qlen = %d\n",
					     qlen);
			dev_kfree_skb(skb);
			goto unlock_and_schedule;
		}

		__skb_queue_tail(&dev->ipa_pendq, skb);
		if (dev->ipa_free_desc_cnt) {
			/* send fixed number of packets to ODU bridge driver
			 * now. Number should be smaller than total desc count
			 * to keep both usbnet and odu driver busy and prevent
			 * pending packet counts to quickly reach to drop count
			 * limit when descs are available without waiting for
			 * work to get a chance to run.
			 */
			pkts_to_send = dev->ipa_free_desc_cnt;
			pkts_to_send = (pkts_to_send > 50) ? 50 : pkts_to_send;
			while (pkts_to_send &&
			       (pend_skb = __skb_dequeue(&dev->ipa_pendq))) {
				ipa_meta.dma_address_valid = false;
				/* Send Packet to ODU bridge Driver */
				spin_unlock(&dev->ipa_pendq.lock);
				ret = odu_bridge_tx_dp(pend_skb, &ipa_meta);
				spin_lock(&dev->ipa_pendq.lock);
				if (ret) {
					pr_err_ratelimited("%s: ret %d\n",
							   __func__, ret);
					dev_kfree_skb(pend_skb);
					punet_ipa->stats.rx_ipa_send_fail++;
					goto unlock_and_schedule;
				} else {
					dev->pusbnet_ipa->stats.rx_ipa_send++;
		odu_bridge_tx_dp(skb, &ipa_meta);
					dev->ipa_free_desc_cnt--;
				}
				pkts_to_send--;
			}
		}
unlock_and_schedule:
		spin_unlock(&dev->ipa_pendq.lock);
		if (dev->ipa_pendq.qlen)
			queue_work(usbnet_wq, &dev->ipa_send_task);
		return;
	}

@@ -657,7 +708,11 @@ block:
	state = defer_bh(dev, skb, &dev->rxq, state);

	if (urb) {
		if (netif_running (dev->net) &&
		/* observing memory allocation failure in atomic context for
		 * high throughput use cases of ipa bridge. Avoid recycling
		 * of rx urb for ipa bridge, let usbnet_bh submit the rx urb.
		 */
		if (!enable_ipa_bridge && netif_running(dev->net) &&
		    !test_bit (EVENT_RX_HALT, &dev->flags) &&
		    state != unlink_start) {
			rx_submit (dev, urb, GFP_ATOMIC);
@@ -1415,7 +1470,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
	case 0:
		net->trans_start = jiffies;
		__usbnet_queue_skb(&dev->txq, skb, tx_start);
		if (dev->txq.qlen >= TX_QLEN (dev))
		if (!enable_ipa_bridge && dev->txq.qlen >= TX_QLEN(dev))
			netif_stop_queue (net);
	}
	spin_unlock_irqrestore (&dev->txq.lock, flags);
@@ -1510,7 +1565,7 @@ static void usbnet_bh (unsigned long param)
		int	temp = dev->rxq.qlen;

		if (temp < RX_QLEN(dev)) {
			if (rx_alloc_submit(dev, GFP_ATOMIC) == -ENOLINK)
			if (rx_alloc_submit(dev, GFP_KERNEL) == -ENOLINK)
				return;
			if (temp != dev->rxq.qlen)
				netif_dbg(dev, link, dev->net,
@@ -1519,7 +1574,7 @@ static void usbnet_bh (unsigned long param)
			if (dev->rxq.qlen < RX_QLEN(dev))
				tasklet_schedule (&dev->bh);
		}
		if (dev->txq.qlen < TX_QLEN (dev))
		if (!enable_ipa_bridge && dev->txq.qlen < TX_QLEN(dev))
			netif_wake_queue (dev->net);
	}
}
@@ -1530,10 +1585,105 @@ static void usbnet_bh (unsigned long param)
 * USB Device Driver support
 *
 *-------------------------------------------------------------------------*/
static void usbnet_ipa_cleanup_rm(void)
static ssize_t usbnet_ipa_debugfs_read_stats(struct file *file,
					     char __user *user_buf,
					     size_t count, loff_t *ppos)
{
	struct usbnet *dev = file->private_data;
	struct usbnet_ipa_ctx *usbnet_ipa = dev->pusbnet_ipa;
	char *buf;
	unsigned int len = 0, buf_len = 1000;
	ssize_t ret_cnt;

	if (unlikely(!usbnet_ipa)) {
		pr_err("%s NULL Pointer\n", __func__);
		return -EINVAL;
	}

	buf = kzalloc(buf_len, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	len += scnprintf(buf + len, buf_len - len, "%25s\n",
	"USBNET IPA stats");
	len += scnprintf(buf + len, buf_len - len, "%25s\n",
	"==================================================");
	len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n",
	"IPA RX Pkt Send: ", usbnet_ipa->stats.rx_ipa_send);
	len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n",
	"IPA RX IPA Send Fail: ", usbnet_ipa->stats.rx_ipa_send_fail);
	len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n",
	"IPA RX Write done: ", usbnet_ipa->stats.rx_ipa_write_done);
	len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n",
	"IPA RX Exception: ", usbnet_ipa->stats.rx_ipa_excep);
	len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n",
	"IPA TX Send: ", usbnet_ipa->stats.tx_ipa_send);
	len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n",
	"IPA TX Send Err: ", usbnet_ipa->stats.tx_ipa_send_err);
	len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n",
	"IPA RX Packet Drops: ", usbnet_ipa->stats.flow_control_pkt_drop);
	len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n",
	"IPA flow ctrl pkt drop ", usbnet_ipa->stats.flow_control_pkt_drop);
	len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n",
	"IPA low watermark cnt ", usbnet_ipa->stats.ipa_low_watermark_cnt);
	len += scnprintf(buf + len, buf_len - len, "%25s %10d\n",
	"IPA free desc cnt ", dev->ipa_free_desc_cnt);
	len += scnprintf(buf + len, buf_len - len, "%25s %10d\n",
	"IPA send qlen ", dev->ipa_pendq.qlen);

	if (len > buf_len)
		len = buf_len;

	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
	kfree(buf);

	return ret_cnt;
}

static const struct file_operations fops_usbnet_ipa_stats = {
		.read = usbnet_ipa_debugfs_read_stats,
		.open = simple_open,
		.owner = THIS_MODULE,
		.llseek = default_llseek,
};

static int usbnet_debugfs_init(struct usbnet *dev)
{
	dev->pusbnet_ipa->debugfs_dir = debugfs_create_dir("usbnet", 0);
	if (!dev->pusbnet_ipa->debugfs_dir)
		return -ENOMEM;

	debugfs_create_file("stats", S_IRUSR, dev->pusbnet_ipa->debugfs_dir,
			    dev, &fops_usbnet_ipa_stats);

	return 0;
}

void usbnet_debugfs_exit(struct usbnet *dev)
{
	debugfs_remove_recursive(dev->pusbnet_ipa->debugfs_dir);
}

static void usbnet_ipa_cleanup_rm(struct usbnet *dev)
{
	int ret;

	init_completion(&dev->rm_prod_release_comp);

	ret =  ipa_rm_release_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
	if (ret) {
		if (ret != EINPROGRESS)
			dev_err(&dev->udev->dev,
				"Release ODU PROD resource failed:%d\n", ret);

		ret = wait_for_completion_timeout(&dev->rm_prod_release_comp,
						  msecs_to_jiffies(
						  IPA_ODU_RM_TIMEOUT_MSEC));
		if (ret == 0)
			dev_err(&dev->udev->dev,
				"Timeout releasing ODU prod resource\n");
	}

	ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
	if (ret)
		pr_warn("Resource:IPA_RM_RESOURCE_ODU_ADAPT_PROD del fail %d\n",
@@ -1572,6 +1722,7 @@ void usbnet_disconnect (struct usb_interface *intf)
	cancel_work_sync(&dev->kevent);

	if (enable_ipa_bridge) {
		skb_queue_purge(&dev->ipa_pendq);
		retval = odu_bridge_disconnect();
		if (retval)
			dev_dbg(&dev->udev->dev,
@@ -1582,7 +1733,8 @@ void usbnet_disconnect (struct usb_interface *intf)
			dev_dbg(&dev->udev->dev,
				"%s ODU bridge cleanup failed.\n",
				__func__);
		usbnet_ipa_cleanup_rm();
		usbnet_ipa_cleanup_rm(dev);
		usbnet_debugfs_exit(dev);
		kfree(dev->pusbnet_ipa);
	}
	usb_scuttle_anchored_urbs(&dev->deferred);
@@ -1624,7 +1776,22 @@ static struct device_type wwan_type = {
static void usbnet_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
				 unsigned long data)
{
	struct usbnet *dev = (struct usbnet *)user_data;

	pr_debug(" %s IPA RM Evt: %d\n", __func__, event);

	switch (event) {
	case IPA_RM_RESOURCE_GRANTED:
		complete(&dev->rm_prod_granted_comp);
		break;
	case  IPA_RM_RESOURCE_RELEASED:
		complete(&dev->rm_prod_release_comp);
		break;
	default:
		dev_dbg(&dev->udev->dev,
			"Un-expected event %d\n", event);
		break;
	}
}

static int usbnet_ipa_rm_cons_request(void)
@@ -1651,7 +1818,7 @@ static int usbnet_ipa_setup_rm(struct usbnet *dev)

	ret = ipa_rm_create_resource(&create_params);
	if (ret) {
		dev_dbg(&dev->udev->dev,
		dev_err(&dev->udev->dev,
			"Create ODU PROD RM resource failed: %d\n", ret);
		goto prod_fail;
	}
@@ -1664,16 +1831,37 @@ static int usbnet_ipa_setup_rm(struct usbnet *dev)

	ret = ipa_rm_create_resource(&create_params);
	if (ret) {
		dev_dbg(&dev->udev->dev,
		dev_err(&dev->udev->dev,
			"Create ODU CONC RM resource failed: %d\n", ret);
		goto delete_prod;
	}

	init_completion(&dev->rm_prod_granted_comp);

	ret =  ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
	if (ret) {
		if (ret != EINPROGRESS) {
			dev_err(&dev->udev->dev,
				"Request ODU PROD resource failed: %d\n", ret);
			goto delete_cons;
		}
		ret = wait_for_completion_timeout(&dev->rm_prod_granted_comp,
						  msecs_to_jiffies(
						  IPA_ODU_RM_TIMEOUT_MSEC));
		if (ret == 0) {
			dev_err(&dev->udev->dev,
				"timeout requesting ODU prod resource\n");
			ret = -ETIMEDOUT;
			goto delete_cons;
		}
	}

	return ret;

delete_cons:
	ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS);
delete_prod:
	ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);

prod_fail:
	return ret;
}
@@ -1685,6 +1873,7 @@ static void usbnet_ipa_tx_dp_cb(void *priv, enum ipa_dp_evt_type evt,
	struct usbnet_ipa_ctx *usbnet_ipa = dev->pusbnet_ipa;
	struct sk_buff *skb = (struct sk_buff *)data;
	int status;
	u32 qlen = 0;

	switch (evt) {
	case IPA_RECEIVE:
@@ -1703,6 +1892,15 @@ static void usbnet_ipa_tx_dp_cb(void *priv, enum ipa_dp_evt_type evt,
		dev->net->stats.rx_packets++;
		dev->net->stats.rx_bytes += skb->len;
		dev_kfree_skb(skb);
		spin_lock(&dev->ipa_pendq.lock);
		qlen = skb_queue_len(&dev->ipa_pendq);
		dev->ipa_free_desc_cnt++;
		if (qlen && dev->ipa_free_desc_cnt < dev->ipa_low_watermark)
				usbnet_ipa->stats.ipa_low_watermark_cnt++;
		spin_unlock(&dev->ipa_pendq.lock);

		if (qlen)
			queue_work(usbnet_wq, &dev->ipa_send_task);
		break;

	default:
@@ -1760,6 +1958,56 @@ static int usbnet_ipa_set_perf_level(struct usbnet *dev)
	return ret;
}

/* usbnet_ipa_send_routine - Sends packets to IPA/ODU bridge Driver
 * Scheduled on RX of IPA_WRITE_DONE Event
 */
static void usbnet_ipa_send_routine(struct work_struct *work)
{
	struct usbnet *dev = container_of(work,
				struct usbnet, ipa_send_task);
	struct sk_buff *skb;
	struct ipa_tx_meta ipa_meta = {0x0};
	int ret = 0;

	/* Send all pending packets to IPA.
	 * Compute the number of desc left for HW and send packets accordingly
	 */
	spin_lock(&dev->ipa_pendq.lock);
	if (dev->ipa_free_desc_cnt < dev->ipa_low_watermark) {
		dev->pusbnet_ipa->stats.ipa_low_watermark_cnt++;
		spin_unlock(&dev->ipa_pendq.lock);
		return;
	}

	while (dev->ipa_free_desc_cnt &&
	       (skb = __skb_dequeue(&dev->ipa_pendq))) {
		ipa_meta.dma_address_valid = false;
		/* Send Packet to ODU bridge Driver */
		spin_unlock(&dev->ipa_pendq.lock);
		ret = odu_bridge_tx_dp(skb, &ipa_meta);
		spin_lock(&dev->ipa_pendq.lock);
		if (ret) {
			pr_err("%s: ret %d\n", __func__, ret);
			dev_kfree_skb(skb);
			dev->pusbnet_ipa->stats.rx_ipa_send_fail++;
		} else {
			dev->pusbnet_ipa->stats.rx_ipa_send++;
			dev->ipa_free_desc_cnt--;
		}
	}
	spin_unlock(&dev->ipa_pendq.lock);
}

static void usbnet_ipa_ready_callback(void *user_data)
{
	struct usbnet *dev = user_data;

	pr_info("%s: ipa is ready\n", __func__);
	dev->ipa_ready = true;

	wake_up_interruptible(&dev->wait_for_ipa_ready);
}

int
usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
{
@@ -1812,6 +2060,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
	dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
				| NETIF_MSG_PROBE | NETIF_MSG_LINK);
	init_waitqueue_head(&dev->wait);
	init_waitqueue_head(&dev->wait_for_ipa_ready);
	skb_queue_head_init (&dev->rxq);
	skb_queue_head_init (&dev->txq);
	skb_queue_head_init (&dev->done);
@@ -1826,7 +2075,6 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
	mutex_init (&dev->phy_mutex);
	mutex_init(&dev->interrupt_mutex);
	dev->interrupt_count = 0;

	dev->net = net;
	strcpy (net->name, "usb%d");
	memcpy (net->dev_addr, node_id, sizeof node_id);
@@ -1940,12 +2188,20 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
		dev->ipa_high_watermark = USBNET_IPA_SYS_PIPE_MAX_PKTS_DESC;
		dev->ipa_low_watermark = USBNET_IPA_SYS_PIPE_MIN_PKTS_DESC;

		/* Initialize flow control variables */
		skb_queue_head_init(&dev->ipa_pendq);
		INIT_WORK(&dev->ipa_send_task, usbnet_ipa_send_routine);

		status = usbnet_ipa_setup_rm(dev);
		if (status) {
			pr_err("USBNET: IPA Setup RM Failed\n");
			goto out4;
		}

		status = usbnet_debugfs_init(dev);
		if (status)
			pr_err("USBNET: Debugfs Init Failed\n");

		/* Initialize the ODU bridge driver now: odu_bridge_init()*/
		params_ptr->netdev_name = net->name;
		params_ptr->priv = dev;
@@ -1960,6 +2216,20 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
			pr_err("Couldnt initialize ODU_Bridge Driver\n");
			goto out4;
		}

		status = ipa_register_ipa_ready_cb(usbnet_ipa_ready_callback,
						   dev);
		if (!status) {
			pr_info("%s: ipa is not ready\n", __func__);
			status = wait_event_interruptible_timeout(
					dev->wait_for_ipa_ready, dev->ipa_ready,
					msecs_to_jiffies
						(USBNET_IPA_READY_TIMEOUT));
			if (!status) {
				pr_err("%s: ipa ready timeout\n", __func__);
				return -ETIMEDOUT;
			}
		}
	}

	// ok, it's ready to go.
+16 −1
Original line number Diff line number Diff line
@@ -24,9 +24,10 @@

#include <linux/ipa.h>

#define USBNET_IPA_SYS_PIPE_MAX_PKTS_DESC 160
#define USBNET_IPA_SYS_PIPE_MAX_PKTS_DESC 200
#define USBNET_IPA_SYS_PIPE_MIN_PKTS_DESC 5
#define USBNET_IPA_SYS_PIPE_DNE_PKTS (USBNET_IPA_SYS_PIPE_MAX_PKTS_DESC*2)
#define USBNET_IPA_READY_TIMEOUT 5000

struct usbnet_ipa_stats {
	/* RX Side */
@@ -38,10 +39,15 @@ struct usbnet_ipa_stats {
	/* TX Side*/
	uint64_t tx_ipa_send;
	uint64_t tx_ipa_send_err;

	/* Flow Control stats */
	uint64_t flow_control_pkt_drop;
	uint64_t ipa_low_watermark_cnt;
};

struct usbnet_ipa_ctx {
	struct usbnet_ipa_stats stats;
	struct dentry *debugfs_dir;
};

/* interface from usbnet core to each USB networking link we handle */
@@ -103,9 +109,18 @@ struct usbnet {
#		define EVENT_LINK_CHANGE	11
#		define EVENT_SET_RX_MODE	12

	struct completion rm_prod_granted_comp;
	struct completion rm_prod_release_comp;

	u16 ipa_free_desc_cnt;
	u16 ipa_high_watermark;
	u16 ipa_low_watermark;

	struct sk_buff_head	ipa_pendq;
	/* work to send pending packets to ipa */
	struct work_struct ipa_send_task;
	bool	ipa_ready;
	wait_queue_head_t wait_for_ipa_ready;
};

static inline struct usb_driver *driver_of(struct usb_interface *intf)