Fix high CPU usage in AP-mode
cover Fix high CPU usage in AP-mode
Series fix high CPU usage noticed in AP-mode. On Intel Atom AP-mode with 100 TU beacon interval was causing CPU usage of ~10%. With these patches CPU usage goes down to ~2%.
---
Jussi Kivilinna (4):
zd1211rw: correct use of usb_bulk_msg on interrupt endpoints
zd1211rw: use async urb for write command
zd1211rw: move async iowrite16v up to callers
zd1211rw: add unlikely to ZD_ASSERT
drivers/net/wireless/zd1211rw/zd_chip.c | 35 +++++- drivers/net/wireless/zd1211rw/zd_def.h | 2 drivers/net/wireless/zd1211rw/zd_usb.c | 177 +++++++++++++++++++++++++------ drivers/net/wireless/zd1211rw/zd_usb.h | 9 +- 4 files changed, 182 insertions(+), 41 deletions(-)
PATCH 1/4 zd1211rw: correct use of usb_bulk_msg on interrupt endpoints
zd1211rw is using usb_bulk_msg() with usb_sndbulkpipe() on interrupt endpoint. However usb_bulk_msg() internally corrects this and makes interrupt URB.
It's better to change usb_bulk_msgs in zd1211rw to usb_interrupt_msg for less confusion.
--- drivers/net/wireless/zd1211rw/zd_usb.c | 24 ++++++++++++------------ 1 files changed, 12 insertions(+), 12 deletions(-)
drivers/net/wireless/zd1211rw/zd_usb.c +12 -12
@@ -1634,15 +1634,15 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, udev =zd_usb_to_usbdev (usb);prepare_read_regs_int (usb); - r =usb_bulk_msg (udev,usb_sndbulkpipe (udev,EP_REGS_OUT ), - req, req_len, &actual_req_len,50 /* ms */ ); + r =usb_interrupt_msg (udev,usb_sndintpipe (udev,EP_REGS_OUT ), + req, req_len, &actual_req_len,50 /* ms */ );if (r) {dev_dbg_f (zd_usb_dev (usb), -"error in usb_bulk_msg(). Error number %d\n" , r); +"error in usb_interrupt_msg(). Error number %d\n" , r); goto error; }if (req_len != actual_req_len) { -dev_dbg_f (zd_usb_dev (usb),"error in usb_bulk_msg()\n" +dev_dbg_f (zd_usb_dev (usb),"error in usb_interrupt_msg()\n" " req_len %d != actual_req_len %d\n" , req_len, actual_req_len); r = -EIO ;
@@ -1705,16 +1705,16 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, } udev =zd_usb_to_usbdev (usb); - r =usb_bulk_msg (udev,usb_sndbulkpipe (udev,EP_REGS_OUT ), - req, req_len, &actual_req_len,50 /* ms */ ); + r =usb_interrupt_msg (udev,usb_sndintpipe (udev,EP_REGS_OUT ), + req, req_len, &actual_req_len,50 /* ms */ );if (r) {dev_dbg_f (zd_usb_dev (usb), -"error in usb_bulk_msg(). Error number %d\n" , r); +"error in usb_interrupt_msg(). Error number %d\n" , r); goto error; }if (req_len != actual_req_len) {dev_dbg_f (zd_usb_dev (usb), -"error in usb_bulk_msg()" +"error in usb_interrupt_msg()" " req_len %d != actual_req_len %d\n" , req_len, actual_req_len); r = -EIO ;
@@ -1794,15 +1794,15 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) } udev =zd_usb_to_usbdev (usb); - r =usb_bulk_msg (udev,usb_sndbulkpipe (udev,EP_REGS_OUT ), - req, req_len, &actual_req_len,50 /* ms */ ); + r =usb_interrupt_msg (udev,usb_sndintpipe (udev,EP_REGS_OUT ), + req, req_len, &actual_req_len,50 /* ms */ );if (r) {dev_dbg_f (zd_usb_dev (usb), -"error in usb_bulk_msg(). Error number %d\n" , r); +"error in usb_interrupt_msg(). Error number %d\n" , r); goto out; }if (req_len != actual_req_len) { -dev_dbg_f (zd_usb_dev (usb),"error in usb_bulk_msg()" +dev_dbg_f (zd_usb_dev (usb),"error in usb_interrupt_msg()" " req_len %d != actual_req_len %d\n" , req_len, actual_req_len); r = -EIO ;
PATCH 2/4 zd1211rw: use async urb for write command
In reply to Jussi KivilinnaWriting beacon to device happen through multiple write command calls. zd_usb_iowrite16v uses synchronous urb call and with multiple write commands in row causes high CPU usage.
This patch makes zd_usb_iowrite16v use asynchronous urb submit within zd_usb.c. zd_usb_iowrite16v_async_start is used to initiate writing multiple commands to device using zd_usb_iowrite16v_async. Each URB is delayed and submitted to device by next zd_usb_iowrite16v_async call or by call to zd_usb_iowrite16v_async_end. URBs submitted by zd_usb_iowrite16v_async have URB_NO_INTERRUPT set and last URB send by zd_usb_iowrite16v_async_end does not. This lower CPU usage when doing writes that require multiple URBs.
--- drivers/net/wireless/zd1211rw/zd_usb.c | 162 +++++++++++++++++++++++++++----- drivers/net/wireless/zd1211rw/zd_usb.h | 5 + 2 files changed, 142 insertions(+), 25 deletions(-)
drivers/net/wireless/zd1211rw/zd_usb.c +138 -24
@@ -1156,6 +1156,7 @@ void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw,memset (usb,0 ,sizeof (*usb)); usb->intf =usb_get_intf (intf);usb_set_intfdata (usb->intf, hw); +init_usb_anchor (&usb->submitted_cmds);init_usb_interrupt (usb);init_usb_tx (usb);init_usb_rx (usb);
@@ -1663,13 +1664,104 @@ error:return r; } -int zd_usb_iowrite16v (struct zd_usb *usb,const struct zd_ioreq16 *ioreqs, -unsigned int count) +static void iowrite16v_urb_complete (struct urb *urb) +{ +struct zd_usb *usb = urb->context; + +if (urb->status && !usb->cmd_error) + usb->cmd_error = urb->status; +} + +static int zd_submit_waiting_urb (struct zd_usb *usb,bool last) +{ +int r; +struct urb *urb = usb->urb_async_waiting; + +if (!urb) +return 0 ; + + usb->urb_async_waiting =NULL ; + +if (!last) + urb->transfer_flags |=URB_NO_INTERRUPT ; + +usb_anchor_urb (urb, &usb->submitted_cmds); + r =usb_submit_urb (urb,GFP_KERNEL ); +if (r) { +usb_unanchor_urb (urb); +dev_dbg_f (zd_usb_dev (usb), +"error in usb_submit_urb(). Error number %d\n" , r); + goto error; + } + +/* fall-through with r == 0 */ +error: +usb_free_urb (urb); +return r; +} + +static void zd_usb_iowrite16v_async_start (struct zd_usb *usb) +{ +ZD_ASSERT (usb_anchor_empty (&usb->submitted_cmds)); +ZD_ASSERT (usb->urb_async_waiting ==NULL ); +ZD_ASSERT (!usb->in_async); + +ZD_ASSERT (mutex_is_locked (&zd_usb_to_chip (usb)->mutex)); + + usb->in_async =1 ; + usb->cmd_error =0 ; + usb->urb_async_waiting =NULL ; +} + +static int zd_usb_iowrite16v_async_end (struct zd_usb *usb,unsigned int timeout) +{ +int r; + +ZD_ASSERT (mutex_is_locked (&zd_usb_to_chip (usb)->mutex)); +ZD_ASSERT (usb->in_async); + +/* Submit last iowrite16v URB */ + r =zd_submit_waiting_urb (usb, true); +if (r) { +dev_dbg_f (zd_usb_dev (usb), +"error in zd_submit_waiting_usb(). " +"Error number %d\n" , r); + +usb_kill_anchored_urbs (&usb->submitted_cmds); + goto error; + } + +if (timeout) + timeout =usb_wait_anchor_empty_timeout (&usb->submitted_cmds, + timeout); +if (!timeout) { +usb_kill_anchored_urbs (&usb->submitted_cmds); +if (usb->cmd_error == -ENOENT ) { +dev_dbg_f (zd_usb_dev (usb),"timed out" ); + r = -ETIMEDOUT ; + goto error; + } + } + + r = usb->cmd_error; +error: + usb->in_async =0 ; +return r; +} + +static int zd_usb_iowrite16v_async (struct zd_usb *usb, +const struct zd_ioreq16 *ioreqs, +unsigned int count) {int r;struct usb_device *udev;struct usb_req_write_regs *req =NULL ; -int i, req_len, actual_req_len; +int i, req_len; +struct urb *urb; +struct usb_host_endpoint *ep; + +ZD_ASSERT (mutex_is_locked (&zd_usb_to_chip (usb)->mutex)); +ZD_ASSERT (usb->in_async);if (count ==0 )return 0 ;
@@ -1685,17 +1777,23 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,return -EWOULDBLOCK ; } -ZD_ASSERT (mutex_is_locked (&zd_usb_to_chip (usb)->mutex)); -BUILD_BUG_ON (sizeof (struct usb_req_write_regs ) + -USB_MAX_IOWRITE16_COUNT *sizeof (struct reg_data ) > -sizeof (usb->req_buf)); -BUG_ON (sizeof (struct usb_req_write_regs ) + - count *sizeof (struct reg_data ) > -sizeof (usb->req_buf)); + udev =zd_usb_to_usbdev (usb); + + ep =usb_pipe_endpoint (udev,usb_sndintpipe (udev,EP_REGS_OUT )); +if (!ep) +return -ENOENT ; + + urb =usb_alloc_urb (0 ,GFP_KERNEL ); +if (!urb) +return -ENOMEM ; req_len =sizeof (struct usb_req_write_regs ) + count *sizeof (struct reg_data ); - req = (void *)usb->req_buf; + req =kmalloc (req_len,GFP_KERNEL ); +if (!req) { + r = -ENOMEM ; + goto error; + } req->id =cpu_to_le16 (USB_REQ_WRITE_REGS );for (i =0 ; i < count; i++) {
@@ -1704,28 +1802,44 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, rw->value =cpu_to_le16 (ioreqs[i].value); } - udev =zd_usb_to_usbdev (usb); - r =usb_interrupt_msg (udev,usb_sndintpipe (udev,EP_REGS_OUT ), - req, req_len, &actual_req_len,50 /* ms */ ); +usb_fill_int_urb (urb, udev,usb_sndintpipe (udev,EP_REGS_OUT ), + req, req_len, iowrite16v_urb_complete, usb, + ep->desc.bInterval); + urb->transfer_flags |=URB_FREE_BUFFER |URB_SHORT_NOT_OK ; + +/* Submit previous URB */ + r =zd_submit_waiting_urb (usb, false);if (r) {dev_dbg_f (zd_usb_dev (usb), -"error in usb_interrupt_msg(). Error number %d\n" , r); - goto error; - } -if (req_len != actual_req_len) { -dev_dbg_f (zd_usb_dev (usb), -"error in usb_interrupt_msg()" -" req_len %d != actual_req_len %d\n" , - req_len, actual_req_len); - r = -EIO ; +"error in zd_submit_waiting_usb(). " +"Error number %d\n" , r); goto error; } -/* FALL-THROUGH with r == 0 */ +/* Delay submit so that URB_NO_INTERRUPT flag can be set for all URBs +* of currect batch except for very last. +*/ + usb->urb_async_waiting = urb; +return 0 ; error: +usb_free_urb (urb);return r; } +int zd_usb_iowrite16v (struct zd_usb *usb,const struct zd_ioreq16 *ioreqs, +unsigned int count) +{ +int r; + +zd_usb_iowrite16v_async_start (usb); + r =zd_usb_iowrite16v_async (usb, ioreqs, count); +if (r) { +zd_usb_iowrite16v_async_end (usb,0 ); +return r; + } +return zd_usb_iowrite16v_async_end (usb,50 /* ms */ ); +} +int zd_usb_rfwrite (struct zd_usb *usb,u32 value,u8 bits) {int r;
drivers/net/wireless/zd1211rw/zd_usb.h +4 -1
@@ -217,8 +217,11 @@ struct zd_usb {struct zd_usb_rx rx;struct zd_usb_tx tx;struct usb_interface *intf; +struct usb_anchor submitted_cmds; +struct urb *urb_async_waiting; +int cmd_error;u8 req_buf[64 ];/* zd_usb_iowrite16v needs 62 bytes */ - u8 is_zd1211b:1, initialized:1, was_running:1; + u8 is_zd1211b:1, initialized:1, was_running:1, in_async:1; };#define zd_usb_dev (usb) (&usb->intf->dev)
PATCH 3/4 zd1211rw: move async iowrite16v up to callers
In reply to Jussi KivilinnaWriting beacon to device happen through multiple write command calls. zd_usb_iowrite16v uses synchronous urb call and with multiple write commands in row causes high CPU usage.
Make asynchronous zd_usb_iowrite16v_async available outside zd_usb.c and use where possible.
This lower CPU usage from ~10% to ~2% on Intel Atom when running AP-mode with 100 TU beacon interval.
--- drivers/net/wireless/zd1211rw/zd_chip.c | 35 +++++++++++++++++++++++++------ drivers/net/wireless/zd1211rw/zd_usb.c | 11 ++++------ drivers/net/wireless/zd1211rw/zd_usb.h | 4 ++++ 3 files changed, 37 insertions(+), 13 deletions(-)
drivers/net/wireless/zd1211rw/zd_chip.c +28 -7
@@ -142,8 +142,9 @@ int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addrreturn 0 ; } -int _zd_iowrite32v_locked (struct zd_chip *chip,const struct zd_ioreq32 *ioreqs, -unsigned int count) +static int _zd_iowrite32v_async_locked (struct zd_chip *chip, +const struct zd_ioreq32 *ioreqs, +unsigned int count) {int i, j, r;struct zd_ioreq16 ioreqs16[USB_MAX_IOWRITE32_COUNT *2 ];
@@ -170,7 +171,7 @@ int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, ioreqs16[j+1 ].addr = ioreqs[i].addr; } - r =zd_usb_iowrite16v (&chip->usb, ioreqs16, count16); + r =zd_usb_iowrite16v_async (&chip->usb, ioreqs16, count16);#ifdef DEBUG if (r) { dev_dbg_f(zd_chip_dev(chip),
@@ -180,6 +181,20 @@ int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,return r; } +int _zd_iowrite32v_locked (struct zd_chip *chip,const struct zd_ioreq32 *ioreqs, +unsigned int count) +{ +int r; + +zd_usb_iowrite16v_async_start (&chip->usb); + r =_zd_iowrite32v_async_locked (chip, ioreqs, count); +if (r) { +zd_usb_iowrite16v_async_end (&chip->usb,0 ); +return r; + } +return zd_usb_iowrite16v_async_end (&chip->usb,50 /* ms */ ); +} +int zd_iowrite16a_locked (struct zd_chip *chip,const struct zd_ioreq16 *ioreqs,unsigned int count) {
@@ -187,6 +202,8 @@ int zd_iowrite16a_locked(struct zd_chip *chip,unsigned int i, j, t, max;ZD_ASSERT (mutex_is_locked (&chip->mutex)); +zd_usb_iowrite16v_async_start (&chip->usb); +for (i =0 ; i < count; i += j + t) { t =0 ; max = count-i;
@@ -199,8 +216,9 @@ int zd_iowrite16a_locked(struct zd_chip *chip, } } - r =zd_usb_iowrite16v (&chip->usb, &ioreqs[i], j); + r =zd_usb_iowrite16v_async (&chip->usb, &ioreqs[i], j);if (r) { +zd_usb_iowrite16v_async_end (&chip->usb,0 );dev_dbg_f (zd_chip_dev (chip),"error zd_usb_iowrite16v. Error number %d\n" , r);
@@ -208,7 +226,7 @@ int zd_iowrite16a_locked(struct zd_chip *chip, } } -return 0 ; +return zd_usb_iowrite16v_async_end (&chip->usb,50 /* ms */ ); }/* Writes a variable number of 32 bit registers. The functions will split
@@ -221,6 +239,8 @@ int zd_iowrite32a_locked(struct zd_chip *chip,int r;unsigned int i, j, t, max; +zd_usb_iowrite16v_async_start (&chip->usb); +for (i =0 ; i < count; i += j + t) { t =0 ; max = count-i;
@@ -233,8 +253,9 @@ int zd_iowrite32a_locked(struct zd_chip *chip, } } - r =_zd_iowrite32v_locked (chip, &ioreqs[i], j); + r =_zd_iowrite32v_async_locked (chip, &ioreqs[i], j);if (r) { +zd_usb_iowrite16v_async_end (&chip->usb,0 );dev_dbg_f (zd_chip_dev (chip),"error _zd_iowrite32v_locked." " Error number %d\n" , r);
@@ -242,7 +263,7 @@ int zd_iowrite32a_locked(struct zd_chip *chip, } } -return 0 ; +return zd_usb_iowrite16v_async_end (&chip->usb,50 /* ms */ ); }int zd_ioread16 (struct zd_chip *chip,zd_addr_t addr,u16 *value)
drivers/net/wireless/zd1211rw/zd_usb.c +5 -6
@@ -1674,7 +1674,7 @@ static void iowrite16v_urb_complete(struct urb *urb)static int zd_submit_waiting_urb (struct zd_usb *usb,bool last) { -int r; +int r =0 ;struct urb *urb = usb->urb_async_waiting;if (!urb)
@@ -1700,7 +1700,7 @@ error:return r; } -static void zd_usb_iowrite16v_async_start (struct zd_usb *usb) +void zd_usb_iowrite16v_async_start (struct zd_usb *usb) {ZD_ASSERT (usb_anchor_empty (&usb->submitted_cmds));ZD_ASSERT (usb->urb_async_waiting ==NULL );
@@ -1713,7 +1713,7 @@ static void zd_usb_iowrite16v_async_start(struct zd_usb *usb) usb->urb_async_waiting =NULL ; } -static int zd_usb_iowrite16v_async_end (struct zd_usb *usb,unsigned int timeout) +int zd_usb_iowrite16v_async_end (struct zd_usb *usb,unsigned int timeout) {int r;
@@ -1749,9 +1749,8 @@ error:return r; } -static int zd_usb_iowrite16v_async (struct zd_usb *usb, -const struct zd_ioreq16 *ioreqs, -unsigned int count) +int zd_usb_iowrite16v_async (struct zd_usb *usb,const struct zd_ioreq16 *ioreqs, +unsigned int count) {int r;struct usb_device *udev;
drivers/net/wireless/zd1211rw/zd_usb.h +4 -0
@@ -273,6 +273,10 @@ static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value,return zd_usb_ioread16v (usb, value, (const zd_addr_t *)&addr,1 ); } +void zd_usb_iowrite16v_async_start (struct zd_usb *usb); +int zd_usb_iowrite16v_async_end (struct zd_usb *usb,unsigned int timeout); +int zd_usb_iowrite16v_async (struct zd_usb *usb,const struct zd_ioreq16 *ioreqs, +unsigned int count);int zd_usb_iowrite16v (struct zd_usb *usb,const struct zd_ioreq16 *ioreqs,unsigned int count);
PATCH 4/4 zd1211rw: add unlikely to ZD_ASSERT
In reply to Jussi KivilinnaCase assert is violated should be quite unlikely.
--- drivers/net/wireless/zd1211rw/zd_def.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/net/wireless/zd1211rw/zd_def.h +1 -1
@@ -45,7 +45,7 @@ typedef u16 __nocast zd_addr_t;#ifdef DEBUG # define ZD_ASSERT (x) \ do { \ - if (!(x)) { \ + if (unlikely(!(x))) { \ pr_debug("%s:%d ASSERT %s VIOLATED!\n", \ __FILE__, __LINE__, __stringify(x)); \ dump_stack(); \