From 18aefc7c0f14a1ba35b057f687b4abefef4110e4 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Wed, 6 Jul 2016 11:47:29 -0700 Subject: [PATCH] usb: gadget: gsi: Optimize TRB's buffer allocation functionality TRB's buffer related memory is allocated as part of gsi_bind() for each USB GSI interface if it is part of USB compsition (i.e. USB multi config composition). This memory is never de-allocated (i.e. USB cable disconnect case) until USB composition switch. Also only one USB tethered GSI interface is used out of mult-config based on selection of USB config by USB host. Hence allocate TRB's buffer related memory after set_alt(1) from ipa_work_handler() and de-allocate same from set_alt(0) OR USB composition switch OR USB bus suspend with remote wakeup disable case. Change-Id: I1bdfe16273186b594f83fd03936a461895701996 Signed-off-by: Mayank Rana --- drivers/usb/gadget/function/f_gsi.c | 176 ++++++++++++++++------------ 1 file changed, 104 insertions(+), 72 deletions(-) diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 05a4bbec5d02..a629723d19cb 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -39,6 +39,8 @@ static struct workqueue_struct *ipa_usb_wq; static void ipa_disconnect_handler(struct gsi_data_port *d_port); static int gsi_ctrl_send_notification(struct f_gsi *gsi, enum gsi_ctrl_notify_state); +static int gsi_alloc_trb_buffer(struct f_gsi *gsi); +static void gsi_free_trb_buffer(struct f_gsi *gsi); void post_event(struct gsi_data_port *port, u8 event) { @@ -474,6 +476,9 @@ static void ipa_disconnect_work_handler(struct gsi_data_port *d_port) if (gsi->d_port.out_ep) usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_FREE_TRBS); + + /* free buffers allocated with each TRB */ + gsi_free_trb_buffer(gsi); } static int ipa_suspend_work_handler(struct gsi_data_port *d_port) @@ -547,6 +552,7 @@ static void ipa_work_handler(struct work_struct *w) struct usb_gadget *gadget = d_port->gadget; struct device *dev; struct device *gad_dev; + struct f_gsi *gsi; event = read_event(d_port); @@ -566,6 +572,8 @@ static void ipa_work_handler(struct work_struct *w) return; } + gsi = d_port_to_gsi(d_port); + switch (d_port->sm_state) { case STATE_UNINITIALIZED: break; @@ -574,29 +582,17 @@ static void ipa_work_handler(struct work_struct *w) usb_gadget_autopm_get(d_port->gadget); log_event_dbg("%s: get = %d", __func__, atomic_read(&gad_dev->power.usage_count)); + /* allocate buffers used with each TRB */ + ret = gsi_alloc_trb_buffer(gsi); + if (ret) { + log_event_err("%s: gsi_alloc_trb_failed\n", + __func__); + break; + } ipa_connect_channels(d_port); d_port->sm_state = STATE_CONNECT_IN_PROGRESS; log_event_dbg("%s: ST_INIT_EVT_CONN_IN_PROG", __func__); - } else if (event == EVT_HOST_READY) { - /* - * When in a composition such as RNDIS + ADB, - * RNDIS host sends a GEN_CURRENT_PACKET_FILTER msg - * to enable/disable flow control eg. during RNDIS - * adaptor disable/enable from device manager. - * In the case of the msg to disable flow control, - * connect IPA channels and enable data path. - * EVT_HOST_READY is posted to the state machine - * in the handler for this msg. - */ - usb_gadget_autopm_get(d_port->gadget); - log_event_dbg("%s: get = %d", __func__, - atomic_read(&gad_dev->power.usage_count)); - ipa_connect_channels(d_port); - ipa_data_path_enable(d_port); - d_port->sm_state = STATE_CONNECTED; - log_event_dbg("%s: ST_INIT_EVT_HOST_READY", - __func__); } break; case STATE_CONNECT_IN_PROGRESS: @@ -1714,6 +1710,92 @@ static int gsi_get_alt(struct usb_function *f, unsigned intf) return -EINVAL; } +static int gsi_alloc_trb_buffer(struct f_gsi *gsi) +{ + u32 len_in = 0, len_out = 0; + int ret = 0; + + log_event_dbg("allocate trb's buffer\n"); + + if (gsi->d_port.in_ep && !gsi->d_port.in_request.buf_base_addr) { + log_event_dbg("IN: num_bufs:=%zu, buf_len=%zu\n", + gsi->d_port.in_request.num_bufs, + gsi->d_port.in_request.buf_len); + + len_in = gsi->d_port.in_request.buf_len * + gsi->d_port.in_request.num_bufs; + gsi->d_port.in_request.buf_base_addr = + dma_zalloc_coherent(gsi->d_port.gadget->dev.parent, + len_in, &gsi->d_port.in_request.dma, GFP_KERNEL); + if (!gsi->d_port.in_request.buf_base_addr) { + dev_err(&gsi->d_port.gadget->dev, + "IN buf_base_addr allocate failed %s\n", + gsi->function.name); + ret = -ENOMEM; + goto fail1; + } + } + + if (gsi->d_port.out_ep && !gsi->d_port.out_request.buf_base_addr) { + log_event_dbg("OUT: num_bufs:=%zu, buf_len=%zu\n", + gsi->d_port.out_request.num_bufs, + gsi->d_port.out_request.buf_len); + + len_out = gsi->d_port.out_request.buf_len * + gsi->d_port.out_request.num_bufs; + gsi->d_port.out_request.buf_base_addr = + dma_zalloc_coherent(gsi->d_port.gadget->dev.parent, + len_out, &gsi->d_port.out_request.dma, GFP_KERNEL); + if (!gsi->d_port.out_request.buf_base_addr) { + dev_err(&gsi->d_port.gadget->dev, + "OUT buf_base_addr allocate failed %s\n", + gsi->function.name); + ret = -ENOMEM; + goto fail; + } + } + + log_event_dbg("finished allocating trb's buffer\n"); + return ret; + +fail: + if (len_in && gsi->d_port.in_request.buf_base_addr) { + dma_free_coherent(gsi->d_port.gadget->dev.parent, len_in, + gsi->d_port.in_request.buf_base_addr, + gsi->d_port.in_request.dma); + gsi->d_port.in_request.buf_base_addr = NULL; + } +fail1: + return ret; +} + +static void gsi_free_trb_buffer(struct f_gsi *gsi) +{ + u32 len; + + log_event_dbg("freeing trb's buffer\n"); + + if (gsi->d_port.out_ep && + gsi->d_port.out_request.buf_base_addr) { + len = gsi->d_port.out_request.buf_len * + gsi->d_port.out_request.num_bufs; + dma_free_coherent(gsi->d_port.gadget->dev.parent, len, + gsi->d_port.out_request.buf_base_addr, + gsi->d_port.out_request.dma); + gsi->d_port.out_request.buf_base_addr = NULL; + } + + if (gsi->d_port.in_ep && + gsi->d_port.in_request.buf_base_addr) { + len = gsi->d_port.in_request.buf_len * + gsi->d_port.in_request.num_bufs; + dma_free_coherent(gsi->d_port.gadget->dev.parent, len, + gsi->d_port.in_request.buf_base_addr, + gsi->d_port.in_request.dma); + gsi->d_port.in_request.buf_base_addr = NULL; + } +} + static int gsi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_gsi *gsi = func_to_gsi(f); @@ -1826,14 +1908,15 @@ static int gsi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (gsi->prot_id == IPA_USB_ECM) gsi->d_port.cdc_filter = DEFAULT_FILTER; + + post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS); /* * For RNDIS the event is posted from the flow control * handler which is invoked when the host sends the * GEN_CURRENT_PACKET_FILTER message. */ if (gsi->prot_id != IPA_USB_RNDIS) - post_event(&gsi->d_port, - EVT_CONNECT_IN_PROGRESS); + post_event(&gsi->d_port, EVT_HOST_READY); queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); } @@ -2041,7 +2124,6 @@ static int gsi_update_function_bind_params(struct f_gsi *gsi, struct usb_ep *ep; struct usb_cdc_notification *event; struct usb_function *f = &gsi->function; - u32 len = 0; int status; /* maybe allocate device-global string IDs */ @@ -2161,37 +2243,9 @@ skip_string_id_alloc: gsi->d_port.in_request.buf_len = info->in_req_buf_len; gsi->d_port.in_request.num_bufs = info->in_req_num_buf; - len = gsi->d_port.in_request.buf_len * gsi->d_port.in_request.num_bufs; - dev_dbg(&cdev->gadget->dev, "%zu %zu\n", gsi->d_port.in_request.buf_len, - gsi->d_port.in_request.num_bufs); - gsi->d_port.in_request.buf_base_addr = - dma_zalloc_coherent(cdev->gadget->dev.parent, len, - &gsi->d_port.in_request.dma, GFP_KERNEL); - if (!gsi->d_port.in_request.buf_base_addr) { - dev_err(&cdev->gadget->dev, - "IN buf_base_addr allocate failed %s\n", - gsi->function.name); - goto fail; - } - if (gsi->d_port.out_ep) { gsi->d_port.out_request.buf_len = info->out_req_buf_len; gsi->d_port.out_request.num_bufs = info->out_req_num_buf; - len = - gsi->d_port.out_request.buf_len * - gsi->d_port.out_request.num_bufs; - dev_dbg(&cdev->gadget->dev, "%zu %zu\n", - gsi->d_port.out_request.buf_len, - gsi->d_port.out_request.num_bufs); - gsi->d_port.out_request.buf_base_addr = - dma_zalloc_coherent(cdev->gadget->dev.parent, len, - &gsi->d_port.out_request.dma, GFP_KERNEL); - if (!gsi->d_port.out_request.buf_base_addr) { - dev_err(&cdev->gadget->dev, - "OUT buf_base_addr allocate failed %s\n", - gsi->function.name); - goto fail; - } } /* Initialize event queue */ @@ -2262,14 +2316,6 @@ fail: gsi->d_port.out_ep->driver_data = NULL; if (gsi->d_port.in_ep && gsi->d_port.in_ep->desc) gsi->d_port.in_ep->driver_data = NULL; - if (len && gsi->d_port.in_request.buf_base_addr) - dma_free_coherent(cdev->gadget->dev.parent, len, - gsi->d_port.in_request.buf_base_addr, - gsi->d_port.in_request.dma); - if (len && gsi->d_port.out_request.buf_base_addr) - dma_free_coherent(cdev->gadget->dev.parent, len, - gsi->d_port.out_request.buf_base_addr, - gsi->d_port.out_request.dma); log_event_err("%s: bind failed for %s", __func__, f->name); return -ENOMEM; } @@ -2563,8 +2609,6 @@ fail: static void gsi_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_gsi *gsi = func_to_gsi(f); - struct usb_composite_dev *cdev = c->cdev; - u32 len; /* * Use drain_workqueue to accomplish below conditions: @@ -2599,19 +2643,7 @@ static void gsi_unbind(struct usb_configuration *c, struct usb_function *f) if (gsi->c_port.notify) { kfree(gsi->c_port.notify_req->buf); usb_ep_free_request(gsi->c_port.notify, gsi->c_port.notify_req); - - len = - gsi->d_port.out_request.buf_len * - gsi->d_port.out_request.num_bufs; - dma_free_coherent(&cdev->gadget->dev, len, - gsi->d_port.out_request.buf_base_addr, - gsi->d_port.out_request.dma); } - - len = gsi->d_port.in_request.buf_len * gsi->d_port.in_request.num_bufs; - dma_free_coherent(&cdev->gadget->dev, len, - gsi->d_port.in_request.buf_base_addr, - gsi->d_port.in_request.dma); }