[PATCH] staging: gdm72xx: protect access of rx / tx structs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch applies spinlock to protect access to rx / tx structs in
certain call sites, which fixes the following crash in gdm_suspend.
It also fixes usb_set_intfdata() in gdm_usb_probe to avoid setting an
already freed phy_dev.

<5>[ 4996.815018] [<7f0074b0>] (gdm_suspend+0x1c/0x2b4 [gdmwm]) from [<803020a4>] (usb_suspend_both+0x80/0x1a0)
<5>[ 4996.815055] [<803020a4>] (usb_suspend_both+0x80/0x1a0) from [<80302c84>] (usb_runtime_suspend+0x38/0x64)
<5>[ 4996.815089] [<80302c84>] (usb_runtime_suspend+0x38/0x64) from [<802becc0>] (__rpm_callback+0x48/0x78)
<5>[ 4996.815118] [<802becc0>] (__rpm_callback+0x48/0x78) from [<802bf8dc>] (rpm_suspend+0x394/0x5ec)
<5>[ 4996.815145] [<802bf8dc>] (rpm_suspend+0x394/0x5ec) from [<802c0550>] (pm_runtime_work+0x8c/0xa4)
<5>[ 4996.815177] [<802c0550>] (pm_runtime_work+0x8c/0xa4) from [<800456cc>] (process_one_work+0x264/0x438)
<5>[ 4996.815209] [<800456cc>] (process_one_work+0x264/0x438) from [<80045acc>] (worker_thread+0x22c/0x3b8)
<5>[ 4996.815239] [<80045acc>] (worker_thread+0x22c/0x3b8) from [<8004a43c>] (kthread+0x9c/0xa8)
<5>[ 4996.815270] [<8004a43c>] (kthread+0x9c/0xa8) from [<8000f160>] (kernel_thread_exit+0x0/0x8)
<0>[ 4996.815295] Code: e92d4000 e8bd4000 e2800020 eb4ab9a1 (e5905000)

Signed-off-by: Ben Chan <benchan@xxxxxxxxxxxx>
Signed-off-by: Sameer Nanda <snanda@xxxxxxxxxxxx>
---
 drivers/staging/gdm72xx/gdm_usb.c |   52 ++++++++++++++++++++++++++++++++-----
 1 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/drivers/staging/gdm72xx/gdm_usb.c b/drivers/staging/gdm72xx/gdm_usb.c
index 0cc6317..4426941 100644
--- a/drivers/staging/gdm72xx/gdm_usb.c
+++ b/drivers/staging/gdm72xx/gdm_usb.c
@@ -186,6 +186,7 @@ static int init_usb(struct usbwm_dev *udev)
 	struct rx_cxt	*rx = &udev->rx;
 	struct usb_tx	*t;
 	struct usb_rx	*r;
+	unsigned long flags;
 
 	INIT_LIST_HEAD(&tx->free_list);
 	INIT_LIST_HEAD(&tx->sdu_list);
@@ -200,6 +201,7 @@ static int init_usb(struct usbwm_dev *udev)
 	spin_lock_init(&tx->lock);
 	spin_lock_init(&rx->lock);
 
+	spin_lock_irqsave(&tx->lock, flags);
 	for (i = 0; i < MAX_NR_SDU_BUF; i++) {
 		t = alloc_tx_struct(tx);
 		if (t == NULL) {
@@ -208,6 +210,7 @@ static int init_usb(struct usbwm_dev *udev)
 		}
 		list_add(&t->list, &tx->free_list);
 	}
+	spin_unlock_irqrestore(&tx->lock, flags);
 
 	r = alloc_rx_struct(rx);
 	if (r == NULL) {
@@ -215,7 +218,9 @@ static int init_usb(struct usbwm_dev *udev)
 		goto fail;
 	}
 
+	spin_lock_irqsave(&rx->lock, flags);
 	list_add(&r->list, &rx->free_list);
+	spin_unlock_irqrestore(&rx->lock, flags);
 	return ret;
 
 fail:
@@ -229,6 +234,9 @@ static void release_usb(struct usbwm_dev *udev)
 	struct rx_cxt	*rx = &udev->rx;
 	struct usb_tx	*t, *t_next;
 	struct usb_rx	*r, *r_next;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tx->lock, flags);
 
 	list_for_each_entry_safe(t, t_next, &tx->sdu_list, list) {
 		list_del(&t->list);
@@ -245,6 +253,10 @@ static void release_usb(struct usbwm_dev *udev)
 		free_tx_struct(t);
 	}
 
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	spin_lock_irqsave(&rx->lock, flags);
+
 	list_for_each_entry_safe(r, r_next, &rx->free_list, list) {
 		list_del(&r->list);
 		free_rx_struct(r);
@@ -254,6 +266,8 @@ static void release_usb(struct usbwm_dev *udev)
 		list_del(&r->list);
 		free_rx_struct(r);
 	}
+
+	spin_unlock_irqrestore(&rx->lock, flags);
 }
 
 static void __gdm_usb_send_complete(struct urb *urb)
@@ -302,7 +316,7 @@ static int gdm_usb_send(void *priv_dev, void *data, int len,
 	int no_spc = 0, ret;
 	u8 *pkt = data;
 	u16 cmd_evt;
-	unsigned long flags;
+	unsigned long flags, flags2;
 
 	if (!udev->usbdev) {
 		dev_err(&usbdev->dev, "%s: No such device\n", __func__);
@@ -371,13 +385,16 @@ static int gdm_usb_send(void *priv_dev, void *data, int len,
 
 		rx = &udev->rx;
 
+		spin_lock_irqsave(&rx->lock, flags2);
 		list_for_each_entry(r, &rx->used_list, list)
 			usb_unlink_urb(r->urb);
+		spin_unlock_irqrestore(&rx->lock, flags2);
+
 		udev->bw_switch = 1;
 
-		spin_lock(&k_lock);
+		spin_lock_irqsave(&k_lock, flags2);
 		list_add_tail(&udev->list, &k_list);
-		spin_unlock(&k_lock);
+		spin_unlock_irqrestore(&k_lock, flags2);
 
 		wake_up(&k_wait);
 	}
@@ -416,7 +433,7 @@ static void gdm_usb_rcv_complete(struct urb *urb)
 	struct tx_cxt *tx = &udev->tx;
 	struct usb_tx *t;
 	u16 cmd_evt;
-	unsigned long flags;
+	unsigned long flags, flags2;
 
 #ifdef CONFIG_WIMAX_GDM72XX_USB_PM
 	struct usb_device *dev = urb->dev;
@@ -462,9 +479,9 @@ static void gdm_usb_rcv_complete(struct urb *urb)
 	if (!urb->status && r->callback)
 		r->callback(r->cb_data, r->buf, urb->actual_length);
 
-	spin_lock(&rx->lock);
+	spin_lock_irqsave(&rx->lock, flags2);
 	put_rx_struct(rx, r);
-	spin_unlock(&rx->lock);
+	spin_unlock_irqrestore(&rx->lock, flags2);
 
 	spin_unlock_irqrestore(&tx->lock, flags);
 
@@ -619,8 +636,9 @@ out:
 	if (ret) {
 		kfree(phy_dev);
 		kfree(udev);
+	} else {
+		usb_set_intfdata(intf, phy_dev);
 	}
-	usb_set_intfdata(intf, phy_dev);
 	return ret;
 }
 
@@ -660,14 +678,22 @@ static int gdm_suspend(struct usb_interface *intf, pm_message_t pm_msg)
 	struct usbwm_dev *udev;
 	struct rx_cxt *rx;
 	struct usb_rx *r;
+	unsigned long flags;
 
 	phy_dev = usb_get_intfdata(intf);
+	if (!phy_dev)
+		return 0;
+
 	udev = phy_dev->priv_dev;
 	rx = &udev->rx;
 
+	spin_lock_irqsave(&rx->lock, flags);
+
 	list_for_each_entry(r, &rx->used_list, list)
 		usb_unlink_urb(r->urb);
 
+	spin_unlock_irqrestore(&rx->lock, flags);
+
 	return 0;
 }
 
@@ -677,14 +703,22 @@ static int gdm_resume(struct usb_interface *intf)
 	struct usbwm_dev *udev;
 	struct rx_cxt *rx;
 	struct usb_rx *r;
+	unsigned long flags;
 
 	phy_dev = usb_get_intfdata(intf);
+	if (!phy_dev)
+		return 0;
+
 	udev = phy_dev->priv_dev;
 	rx = &udev->rx;
 
+	spin_lock_irqsave(&rx->lock, flags);
+
 	list_for_each_entry(r, &rx->used_list, list)
 		usb_submit_urb(r->urb, GFP_ATOMIC);
 
+	spin_unlock_irqrestore(&rx->lock, flags);
+
 	return 0;
 }
 
@@ -719,9 +753,13 @@ static int k_mode_thread(void *arg)
 			while (jiffies < expire)
 				schedule_timeout(K_WAIT_TIME);
 
+			spin_lock_irqsave(&rx->lock, flags);
+
 			list_for_each_entry(r, &rx->used_list, list)
 				usb_submit_urb(r->urb, GFP_ATOMIC);
 
+			spin_unlock_irqrestore(&rx->lock, flags);
+
 			spin_lock_irqsave(&tx->lock, flags);
 
 			list_for_each_entry_safe(t, temp, &tx->pending_list,
-- 
1.7.7.3

_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/devel


[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux