diff options
author | Lukasz Rymanowski <lukasz.rymanowski@tieto.com> | 2011-10-21 10:10:03 +0200 |
---|---|---|
committer | Jonas ABERG <jonas.aberg@stericsson.com> | 2011-10-25 07:43:13 +0200 |
commit | a1703f7085896a38684c3197279fc04fb6ed74ff (patch) | |
tree | 7605653f4c5e858e0f82b29b5066347975d12325 | |
parent | 5fbf8893def8405384754adc11de7d9bb7287440 (diff) |
cg2900: Fix for race condition
Fix for race condition which leads to double
kfree_skb.
ST-Ericsson Linux next: 365852
ST-Ericsson ID: 365852
ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10019
Change-Id: Ia951a283d9e3774b29a51280b0536838926c99f9
Signed-off-by: Lukasz Rymanowski <lukasz.rymanowski@tieto.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34863
Reviewed-by: Lukasz RYMANOWSKI <lukasz.rymanowski@stericsson.com>
Tested-by: Lukasz RYMANOWSKI <lukasz.rymanowski@stericsson.com>
Reviewed-by: Hemant GUPTA <hemant.gupta@stericsson.com>
Reviewed-by: SZYMON JANC <szymon.janc@tieto.com>
Reviewed-by: Nitin DHINGRA <nitin.dhingra@stericsson.com>
Reviewed-by: Ulrik LAUREN <ulrik.lauren@stericsson.com>
-rw-r--r-- | drivers/staging/cg2900/bluetooth/cg2900_uart.c | 36 |
1 files changed, 27 insertions, 9 deletions
diff --git a/drivers/staging/cg2900/bluetooth/cg2900_uart.c b/drivers/staging/cg2900/bluetooth/cg2900_uart.c index abc95743eaa..6569142b062 100644 --- a/drivers/staging/cg2900/bluetooth/cg2900_uart.c +++ b/drivers/staging/cg2900/bluetooth/cg2900_uart.c @@ -272,6 +272,7 @@ struct uart_delayed_work_struct { * @rx_count: Number of bytes left to receive. * @rx_skb: SK_buffer to store the received data into. * @tx_queue: TX queue for sending data to chip. + * @rx_skb_lock Spin lock to protect rx_skb. * @hu: Hci uart structure. * @wq: UART work queue. * @baud_rate_state: UART baud rate change state. @@ -300,6 +301,7 @@ struct uart_info { unsigned long rx_count; struct sk_buff *rx_skb; struct sk_buff_head tx_queue; + spinlock_t rx_skb_lock; struct hci_uart *hu; @@ -1443,21 +1445,28 @@ static void uart_set_chip_power(struct cg2900_chip_dev *dev, bool chip_on) uart_info->sleep_state = CHIP_POWERED_DOWN; } - /* - * Reset the uart_info state so that - * next packet can be handled - * correctly by driver. - */ - uart_info->rx_state = W4_PACKET_TYPE; - uart_info->rx_count = 0; - kfree_skb(uart_info->rx_skb); - uart_info->rx_skb = NULL; cg2900_disable_regulator(uart_info); /* * Setting baud rate to 0 will tell UART driver to shut off its * clocks. */ (void)hci_uart_set_baudrate(uart_info->hu, ZERO_BAUD_RATE); + + spin_lock_bh(&uart_info->rx_skb_lock); + if (uart_info->rx_skb) { + /* + * Reset the uart_info state so that + * next packet can be handled + * correctly by driver. + */ + dev_dbg(MAIN_DEV, "Power off in the middle of data receiving?" + "Reseting state machine.\n"); + kfree_skb(uart_info->rx_skb); + uart_info->rx_skb = NULL; + uart_info->rx_state = W4_PACKET_TYPE; + uart_info->rx_count = 0; + } + spin_unlock_bh(&uart_info->rx_skb_lock); } unlock: @@ -1697,6 +1706,8 @@ static int cg2900_hu_receive(struct hci_uart *hu, print_hex_dump_bytes(NAME " RX:\t", DUMP_PREFIX_NONE, data, count); + spin_lock_bh(&uart_info->rx_skb_lock); + /* Continue while there is data left to handle */ while (count) { /* @@ -1802,6 +1813,8 @@ check_h4_header: spin_lock_bh(&(uart_info->transmission_lock)); uart_info->rx_in_progress = false; spin_unlock_bh(&(uart_info->transmission_lock)); + + spin_unlock_bh(&uart_info->rx_skb_lock); return 0; } @@ -1816,6 +1829,7 @@ check_h4_header: (void)queue_work(uart_info->wq, &uart_info->restart_sleep_work.work); + spin_unlock_bh(&uart_info->rx_skb_lock); return count; } @@ -1876,10 +1890,13 @@ static int cg2900_hu_close(struct hci_uart *hu) /* Purge any stored sk_buffers */ skb_queue_purge(&uart_info->tx_queue); + + spin_lock_bh(&uart_info->rx_skb_lock); if (uart_info->rx_skb) { kfree_skb(uart_info->rx_skb); uart_info->rx_skb = NULL; } + spin_unlock_bh(&uart_info->rx_skb_lock); dev_info(MAIN_DEV, "UART closed\n"); err = create_work_item(uart_info, work_hw_deregistered); @@ -1987,6 +2004,7 @@ static int __devinit cg2900_uart_probe(struct platform_device *pdev) mutex_init(&(uart_info->sleep_state_lock)); spin_lock_init(&(uart_info->transmission_lock)); + spin_lock_init(&(uart_info->rx_skb_lock)); uart_info->chip_dev.t_cb.open = uart_open; uart_info->chip_dev.t_cb.close = uart_close; |