USB: gadget: diag: Take spin lock while freeing requests

Freeing USB requests and retiring pending requests can happen in
parallel.  Take spin lock while freeing requests to prevent list
corruption.  Ensure all stale requests are freed before allocating
new requests upon next cable connection.

CRs-Fixed: 436769
(cherry picked from commit eb1ed0ec838f616677918974fa667ee0752e5bf3)

Change-Id: I162bfd72da7a205e80caf9ae53db12f0eb335388
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c
index 3b9480e..a2ee182 100644
--- a/drivers/usb/gadget/f_diag.c
+++ b/drivers/usb/gadget/f_diag.c
@@ -2,7 +2,7 @@
  * Diag Function Device - Route ARM9 and ARM11 DIAG messages
  * between HOST and DEVICE.
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2013, Linux Foundation. All rights reserved.
  * Author: Brian Swetland <swetland@google.com>
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -291,22 +291,10 @@
 }
 EXPORT_SYMBOL(usb_diag_close);
 
-/**
- * usb_diag_free_req() - Free USB requests
- * @ch: Channel handler
- *
- * This function free read and write USB requests for the interface
- * associated with this channel.
- *
- */
-void usb_diag_free_req(struct usb_diag_ch *ch)
+static void free_reqs(struct diag_context *ctxt)
 {
-	struct diag_context *ctxt = ch->priv_usb;
-	struct usb_request *req;
 	struct list_head *act, *tmp;
-
-	if (!ctxt)
-		return;
+	struct usb_request *req;
 
 	list_for_each_safe(act, tmp, &ctxt->write_pool) {
 		req = list_entry(act, struct usb_request, list);
@@ -320,6 +308,27 @@
 		usb_ep_free_request(ctxt->out, req);
 	}
 }
+
+/**
+ * usb_diag_free_req() - Free USB requests
+ * @ch: Channel handler
+ *
+ * This function free read and write USB requests for the interface
+ * associated with this channel.
+ *
+ */
+void usb_diag_free_req(struct usb_diag_ch *ch)
+{
+	struct diag_context *ctxt = ch->priv_usb;
+	unsigned long flags;
+
+	if (ctxt) {
+		spin_lock_irqsave(&ctxt->lock, flags);
+		free_reqs(ctxt);
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+	}
+
+}
 EXPORT_SYMBOL(usb_diag_free_req);
 
 /**
@@ -338,10 +347,14 @@
 	struct diag_context *ctxt = ch->priv_usb;
 	struct usb_request *req;
 	int i;
+	unsigned long flags;
 
 	if (!ctxt)
 		return -ENODEV;
 
+	spin_lock_irqsave(&ctxt->lock, flags);
+	/* Free previous session's stale requests */
+	free_reqs(ctxt);
 	for (i = 0; i < n_write; i++) {
 		req = usb_ep_alloc_request(ctxt->in, GFP_ATOMIC);
 		if (!req)
@@ -357,11 +370,11 @@
 		req->complete = diag_read_complete;
 		list_add_tail(&req->list, &ctxt->read_pool);
 	}
-
+	spin_unlock_irqrestore(&ctxt->lock, flags);
 	return 0;
-
 fail:
-	usb_diag_free_req(ch);
+	free_reqs(ctxt);
+	spin_unlock_irqrestore(&ctxt->lock, flags);
 	return -ENOMEM;
 
 }