Commit 3a2b808e authored by Elina Pasheva's avatar Elina Pasheva Committed by Greg Kroah-Hartman

USB: serial: sierra driver memory reduction

This patch deals with reducing the memory footprint for sierra driver.
This optimization is aimed for embedded software customers.

Some sierra modems can expose upwards of 7 USB interfaces, each possibly
offering different services. In general, interfaces used for the
exchange of wireless data require much higher throughput, hence require
more memory (i.e. more URBs) than lower performance interfaces. URBs
used for the IN direction are pre-allocated by the driver and this patch
introduces a way to configure the number of IN URBs allocated on a
per-interface basis. Interfaces with lower throughput requirements
receive fewer URBs, thereby reducing the RAM memory consumed by the
driver.

NOTE1: This driver has always pre-allocated URBs for the IN direction.

NOTE2: The number of URBs pre-allocated for the low-performance
interfaces has already been extensively tested in previous versions of
this driver.

We also added the capability to log function calls by adding DEBUG flag.
Please note that  this flag is commented out because this is the default
state
for it.
Signed-off-by: default avatarElina Pasheva <epasheva@sierrawireless.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent b97503ff
...@@ -16,8 +16,9 @@ ...@@ -16,8 +16,9 @@
Portions based on the option driver by Matthias Urlichs <smurf@smurf.noris.de> Portions based on the option driver by Matthias Urlichs <smurf@smurf.noris.de>
Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org> Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org>
*/ */
/* Uncomment to log function calls */
#define DRIVER_VERSION "v.1.3.8" /* #define DEBUG */
#define DRIVER_VERSION "v.1.7.16"
#define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer" #define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer"
#define DRIVER_DESC "USB Driver for Sierra Wireless USB modems" #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
...@@ -33,8 +34,10 @@ ...@@ -33,8 +34,10 @@
#define SWIMS_USB_REQUEST_SetPower 0x00 #define SWIMS_USB_REQUEST_SetPower 0x00
#define SWIMS_USB_REQUEST_SetNmea 0x07 #define SWIMS_USB_REQUEST_SetNmea 0x07
#define N_IN_URB 8 #define N_IN_URB_HM 8
#define N_OUT_URB 64 #define N_OUT_URB_HM 64
#define N_IN_URB 4
#define N_OUT_URB 4
#define IN_BUFLEN 4096 #define IN_BUFLEN 4096
#define MAX_TRANSFER (PAGE_SIZE - 512) #define MAX_TRANSFER (PAGE_SIZE - 512)
...@@ -124,6 +127,23 @@ static int is_blacklisted(const u8 ifnum, ...@@ -124,6 +127,23 @@ static int is_blacklisted(const u8 ifnum,
return 0; return 0;
} }
static int is_himemory(const u8 ifnum,
const struct sierra_iface_info *himemorylist)
{
const u8 *info;
int i;
if (himemorylist) {
info = himemorylist->ifaceinfo;
for (i=0; i < himemorylist->infolen; i++) {
if (info[i] == ifnum)
return 1;
}
}
return 0;
}
static int sierra_calc_interface(struct usb_serial *serial) static int sierra_calc_interface(struct usb_serial *serial)
{ {
int interface; int interface;
...@@ -186,6 +206,20 @@ static int sierra_probe(struct usb_serial *serial, ...@@ -186,6 +206,20 @@ static int sierra_probe(struct usb_serial *serial,
return result; return result;
} }
/* interfaces with higher memory requirements */
static const u8 hi_memory_typeA_ifaces[] = { 0, 2 };
static const struct sierra_iface_info typeA_interface_list = {
.infolen = ARRAY_SIZE(hi_memory_typeA_ifaces),
.ifaceinfo = hi_memory_typeA_ifaces,
};
static const u8 hi_memory_typeB_ifaces[] = { 3, 4, 5, 6 };
static const struct sierra_iface_info typeB_interface_list = {
.infolen = ARRAY_SIZE(hi_memory_typeB_ifaces),
.ifaceinfo = hi_memory_typeB_ifaces,
};
/* 'blacklist' of interfaces not served by this driver */
static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11 }; static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11 };
static const struct sierra_iface_info direct_ip_interface_blacklist = { static const struct sierra_iface_info direct_ip_interface_blacklist = {
.infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces), .infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces),
...@@ -286,8 +320,10 @@ struct sierra_port_private { ...@@ -286,8 +320,10 @@ struct sierra_port_private {
struct usb_anchor active; struct usb_anchor active;
struct usb_anchor delayed; struct usb_anchor delayed;
int num_out_urbs;
int num_in_urbs;
/* Input endpoints and buffers for this port */ /* Input endpoints and buffers for this port */
struct urb *in_urbs[N_IN_URB]; struct urb *in_urbs[N_IN_URB_HM];
/* Settings for the port */ /* Settings for the port */
int rts_state; /* Handshaking pins (outputs) */ int rts_state; /* Handshaking pins (outputs) */
...@@ -460,7 +496,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, ...@@ -460,7 +496,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,
spin_lock_irqsave(&portdata->lock, flags); spin_lock_irqsave(&portdata->lock, flags);
dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__, dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__,
portdata->outstanding_urbs); portdata->outstanding_urbs);
if (portdata->outstanding_urbs > N_OUT_URB) { if (portdata->outstanding_urbs > portdata->num_out_urbs) {
spin_unlock_irqrestore(&portdata->lock, flags); spin_unlock_irqrestore(&portdata->lock, flags);
dev_dbg(&port->dev, "%s - write limit hit\n", __func__); dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
return 0; return 0;
...@@ -665,7 +701,7 @@ static int sierra_write_room(struct tty_struct *tty) ...@@ -665,7 +701,7 @@ static int sierra_write_room(struct tty_struct *tty)
/* try to give a good number back based on if we have any free urbs at /* try to give a good number back based on if we have any free urbs at
* this point in time */ * this point in time */
spin_lock_irqsave(&portdata->lock, flags); spin_lock_irqsave(&portdata->lock, flags);
if (portdata->outstanding_urbs > N_OUT_URB * 2 / 3) { if (portdata->outstanding_urbs > (portdata->num_out_urbs * 2) / 3) {
spin_unlock_irqrestore(&portdata->lock, flags); spin_unlock_irqrestore(&portdata->lock, flags);
dev_dbg(&port->dev, "%s - write limit hit\n", __func__); dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
return 0; return 0;
...@@ -680,7 +716,7 @@ static void sierra_stop_rx_urbs(struct usb_serial_port *port) ...@@ -680,7 +716,7 @@ static void sierra_stop_rx_urbs(struct usb_serial_port *port)
int i; int i;
struct sierra_port_private *portdata = usb_get_serial_port_data(port); struct sierra_port_private *portdata = usb_get_serial_port_data(port);
for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) for (i = 0; i < portdata->num_in_urbs; i++)
usb_kill_urb(portdata->in_urbs[i]); usb_kill_urb(portdata->in_urbs[i]);
usb_kill_urb(port->interrupt_in_urb); usb_kill_urb(port->interrupt_in_urb);
...@@ -695,7 +731,7 @@ static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags) ...@@ -695,7 +731,7 @@ static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags)
struct sierra_port_private *portdata = usb_get_serial_port_data(port); struct sierra_port_private *portdata = usb_get_serial_port_data(port);
ok_cnt = 0; ok_cnt = 0;
for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) { for (i = 0; i < portdata->num_in_urbs; i++) {
urb = portdata->in_urbs[i]; urb = portdata->in_urbs[i];
if (!urb) if (!urb)
continue; continue;
...@@ -791,7 +827,7 @@ static void sierra_close(struct usb_serial_port *port) ...@@ -791,7 +827,7 @@ static void sierra_close(struct usb_serial_port *port)
/* Stop reading urbs */ /* Stop reading urbs */
sierra_stop_rx_urbs(port); sierra_stop_rx_urbs(port);
/* .. and release them */ /* .. and release them */
for (i = 0; i < N_IN_URB; i++) { for (i = 0; i < portdata->num_in_urbs; i++) {
sierra_release_urb(portdata->in_urbs[i]); sierra_release_urb(portdata->in_urbs[i]);
portdata->in_urbs[i] = NULL; portdata->in_urbs[i] = NULL;
} }
...@@ -818,7 +854,7 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port) ...@@ -818,7 +854,7 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
endpoint = port->bulk_in_endpointAddress; endpoint = port->bulk_in_endpointAddress;
for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) { for (i = 0; i < portdata->num_in_urbs; i++) {
urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port, urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port,
IN_BUFLEN, GFP_KERNEL, IN_BUFLEN, GFP_KERNEL,
sierra_indat_callback); sierra_indat_callback);
...@@ -869,7 +905,9 @@ static int sierra_startup(struct usb_serial *serial) ...@@ -869,7 +905,9 @@ static int sierra_startup(struct usb_serial *serial)
{ {
struct usb_serial_port *port; struct usb_serial_port *port;
struct sierra_port_private *portdata; struct sierra_port_private *portdata;
struct sierra_iface_info *himemoryp = NULL;
int i; int i;
u8 ifnum;
dev_dbg(&serial->dev->dev, "%s\n", __func__); dev_dbg(&serial->dev->dev, "%s\n", __func__);
...@@ -886,13 +924,40 @@ static int sierra_startup(struct usb_serial *serial) ...@@ -886,13 +924,40 @@ static int sierra_startup(struct usb_serial *serial)
portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
if (!portdata) { if (!portdata) {
dev_dbg(&port->dev, "%s: kmalloc for " dev_dbg(&port->dev, "%s: kmalloc for "
"sierra_port_private (%d) failed!.\n", "sierra_port_private (%d) failed!\n",
__func__, i); __func__, i);
return -ENOMEM; return -ENOMEM;
} }
spin_lock_init(&portdata->lock); spin_lock_init(&portdata->lock);
init_usb_anchor(&portdata->active); init_usb_anchor(&portdata->active);
init_usb_anchor(&portdata->delayed); init_usb_anchor(&portdata->delayed);
ifnum = i;
/* Assume low memory requirements */
portdata->num_out_urbs = N_OUT_URB;
portdata->num_in_urbs = N_IN_URB;
/* Determine actual memory requirements */
if (serial->num_ports == 1) {
/* Get interface number for composite device */
ifnum = sierra_calc_interface(serial);
himemoryp =
(struct sierra_iface_info *)&typeB_interface_list;
if (is_himemory(ifnum, himemoryp)) {
portdata->num_out_urbs = N_OUT_URB_HM;
portdata->num_in_urbs = N_IN_URB_HM;
}
}
else {
himemoryp =
(struct sierra_iface_info *)&typeA_interface_list;
if (is_himemory(i, himemoryp)) {
portdata->num_out_urbs = N_OUT_URB_HM;
portdata->num_in_urbs = N_IN_URB_HM;
}
}
dev_dbg(&serial->dev->dev,
"Memory usage (urbs) interface #%d, in=%d, out=%d\n",
ifnum,portdata->num_in_urbs, portdata->num_out_urbs );
/* Set the port private data pointer */ /* Set the port private data pointer */
usb_set_serial_port_data(port, portdata); usb_set_serial_port_data(port, portdata);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment