Loading drivers/net/usb/ax88179_178a.c +2 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); Loading drivers/net/usb/usbnet.c +286 −16 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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) Loading @@ -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 Loading Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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, Loading @@ -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); } } Loading @@ -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", Loading Loading @@ -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, Loading @@ -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); Loading Loading @@ -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) Loading @@ -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; } Loading @@ -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; } Loading @@ -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: Loading @@ -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: Loading Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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; Loading @@ -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. Loading include/linux/usb/usbnet.h +16 −1 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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 */ Loading Loading @@ -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) Loading Loading
drivers/net/usb/ax88179_178a.c +2 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); Loading
drivers/net/usb/usbnet.c +286 −16 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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) Loading @@ -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 Loading Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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, Loading @@ -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); } } Loading @@ -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", Loading Loading @@ -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, Loading @@ -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); Loading Loading @@ -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) Loading @@ -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; } Loading @@ -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; } Loading @@ -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: Loading @@ -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: Loading Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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; Loading @@ -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. Loading
include/linux/usb/usbnet.h +16 −1 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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 */ Loading Loading @@ -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) Loading