Re: [RFC PATCH v3] Bluetooth: hci_qca: Collect controller memory dump during SSR

2019-04-23 Thread Marcel Holtmann
Hi Balakrishna,

> We will collect the ramdump of BT controller when hardware error event
> received before rebooting the HCI layer. Before restarting a subsystem
> or a process running on a subsystem, it is often required to request
> either a subsystem or a process to perform proper cache dump and
> software failure reason into a memory buffer which application
> processor can retrieve afterwards. SW developers can often provide
> initial investigation by looking into that debugging information.
> 
> Signed-off-by: Balakrishna Godavarthi 
> ---
> changes v3:
>  * used wakeup helper instead of polling while collecting dump.
>  * directly coping the crash command to the skb instead of local buffer.
>  * update review comments.
> ---
> drivers/bluetooth/hci_qca.c | 291 +++-
> 1 file changed, 287 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> index 237aea34b69f..2b9bcf811776 100644
> --- a/drivers/bluetooth/hci_qca.c
> +++ b/drivers/bluetooth/hci_qca.c
> @@ -32,6 +32,7 @@
> #include 
> #include 
> #include 
> +#include 
> #include 
> #include 
> #include 
> @@ -56,10 +57,12 @@
> 
> /* Controller states */
> #define STATE_IN_BAND_SLEEP_ENABLED   1
> +#define STATE_MEMDUMP_COLLECTION 2
> 
> #define IBS_WAKE_RETRANS_TIMEOUT_MS   100
> #define IBS_TX_IDLE_TIMEOUT_MS2000
> #define CMD_TRANS_TIMEOUT_MS  100
> +#define MEMDUMP_COLLECTION_TIMEOUT_MS8000
> 
> /* susclk rate */
> #define SUSCLK_RATE_32KHZ 32768
> @@ -67,6 +70,13 @@
> /* Controller debug log header */
> #define QCA_DEBUG_HANDLE  0x2EDC
> 
> +/* Controller dump header */
> +#define QCA_SSR_DUMP_HANDLE  0x0108
> +#define QCA_DUMP_PACKET_SIZE 255
> +#define QCA_LAST_SEQUENCE_NUM0x
> +#define QCA_CRASHBYTE_PACKET_LEN 1100
> +#define QCA_MEMDUMP_BYTE 0xFB
> +
> /* HCI_IBS transmit side sleep protocol states */
> enum tx_ibs_states {
>   HCI_IBS_TX_ASLEEP,
> @@ -89,12 +99,41 @@ enum hci_ibs_clock_state_vote {
>   HCI_IBS_RX_VOTE_CLOCK_OFF,
> };
> 
> +/* Controller memory dump states */
> +enum qca_memdump_states {
> + QCA_MEMDUMP_IDLE,
> + QCA_MEMDUMP_COLLECTING,
> + QCA_MEMDUMP_COLLECTED,
> + QCA_MEMDUMP_TIMEOUT,
> +};
> +
> +struct qca_memdump_data {
> + char *memdump_buf_head;
> + char *memdump_buf_tail;
> + u32 current_seq_no;
> + u32 received_dump;
> +};
> +
> +struct qca_memdump_event_hdr {
> + __u8evt;
> + __u8plen;
> + __u16   opcode;
> + __u16   seq_no;
> + __u8reserved;
> +} __packed;
> +
> +
> +struct qca_dump_size {
> + u32 dump_size;
> +} __packed;
> +
> struct qca_data {
>   struct hci_uart *hu;
>   struct sk_buff *rx_skb;
>   struct sk_buff_head txq;
> - struct sk_buff_head tx_wait_q;  /* HCI_IBS wait queue   */
> - spinlock_t hci_ibs_lock;/* HCI_IBS state lock   */
> + struct sk_buff_head tx_wait_q;  /* HCI_IBS wait queue   */
> + struct sk_buff_head rx_memdump_q;   /* Memdump wait queue   */
> + spinlock_t hci_ibs_lock;/* HCI_IBS state lock   */
>   u8 tx_ibs_state;/* HCI_IBS transmit side power state*/
>   u8 rx_ibs_state;/* HCI_IBS receive side power state */
>   bool tx_vote;   /* Clock must be on for TX */
> @@ -103,12 +142,17 @@ struct qca_data {
>   u32 tx_idle_delay;
>   struct timer_list wake_retrans_timer;
>   u32 wake_retrans;
> + struct timer_list memdump_timer;
> + u32 memdump_delay;
>   struct workqueue_struct *workqueue;
>   struct work_struct ws_awake_rx;
>   struct work_struct ws_awake_device;
>   struct work_struct ws_rx_vote_off;
>   struct work_struct ws_tx_vote_off;
> + struct work_struct ctrl_memdump_evt;
> + struct qca_memdump_data *qca_memdump;
>   unsigned long flags;
> + enum qca_memdump_states memdump_state;
> 
>   /* For debugging purpose */
>   u64 ibs_sent_wacks;
> @@ -173,6 +217,7 @@ struct qca_serdev {
> static int qca_power_setup(struct hci_uart *hu, bool on);
> static void qca_power_shutdown(struct hci_uart *hu);
> static int qca_power_off(struct hci_dev *hdev);
> +static void qca_controller_memdump(struct work_struct *work);
> 
> static void __serial_clock_on(struct tty_struct *tty)
> {
> @@ -446,6 +491,21 @@ static void hci_ibs_wake_retrans_timeout(struct 
> timer_list *t)
>   hci_uart_tx_wakeup(hu);
> }
> 
> +static void hci_memdump_timeout(struct timer_list *t)
> +{
> + struct qca_data *qca = from_timer(qca, t, tx_idle_timer);
> + struct hci_uart *hu = qca->hu;
> + struct qca_memdump_data *qca_memdump = qca->qca_memdump;
> + char *memdump_buf = qca_memdump->memdump_buf_tail;
> +
> + bt_dev_err(hu->hdev, "clearing allocated memory due to memdump 
> timeout");
> + kfree(memdump_buf);
> + kfree(qca_memdump);
> + qca->memdump_state = 

[RFC PATCH v3] Bluetooth: hci_qca: Collect controller memory dump during SSR

2019-04-16 Thread Balakrishna Godavarthi
We will collect the ramdump of BT controller when hardware error event
received before rebooting the HCI layer. Before restarting a subsystem
or a process running on a subsystem, it is often required to request
either a subsystem or a process to perform proper cache dump and
software failure reason into a memory buffer which application
processor can retrieve afterwards. SW developers can often provide
initial investigation by looking into that debugging information.

Signed-off-by: Balakrishna Godavarthi 
---
changes v3:
  * used wakeup helper instead of polling while collecting dump.
  * directly coping the crash command to the skb instead of local buffer.
  * update review comments.
---
 drivers/bluetooth/hci_qca.c | 291 +++-
 1 file changed, 287 insertions(+), 4 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 237aea34b69f..2b9bcf811776 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -32,6 +32,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -56,10 +57,12 @@
 
 /* Controller states */
 #define STATE_IN_BAND_SLEEP_ENABLED1
+#define STATE_MEMDUMP_COLLECTION   2
 
 #define IBS_WAKE_RETRANS_TIMEOUT_MS100
 #define IBS_TX_IDLE_TIMEOUT_MS 2000
 #define CMD_TRANS_TIMEOUT_MS   100
+#define MEMDUMP_COLLECTION_TIMEOUT_MS  8000
 
 /* susclk rate */
 #define SUSCLK_RATE_32KHZ  32768
@@ -67,6 +70,13 @@
 /* Controller debug log header */
 #define QCA_DEBUG_HANDLE   0x2EDC
 
+/* Controller dump header */
+#define QCA_SSR_DUMP_HANDLE0x0108
+#define QCA_DUMP_PACKET_SIZE   255
+#define QCA_LAST_SEQUENCE_NUM  0x
+#define QCA_CRASHBYTE_PACKET_LEN   1100
+#define QCA_MEMDUMP_BYTE   0xFB
+
 /* HCI_IBS transmit side sleep protocol states */
 enum tx_ibs_states {
HCI_IBS_TX_ASLEEP,
@@ -89,12 +99,41 @@ enum hci_ibs_clock_state_vote {
HCI_IBS_RX_VOTE_CLOCK_OFF,
 };
 
+/* Controller memory dump states */
+enum qca_memdump_states {
+   QCA_MEMDUMP_IDLE,
+   QCA_MEMDUMP_COLLECTING,
+   QCA_MEMDUMP_COLLECTED,
+   QCA_MEMDUMP_TIMEOUT,
+};
+
+struct qca_memdump_data {
+   char *memdump_buf_head;
+   char *memdump_buf_tail;
+   u32 current_seq_no;
+   u32 received_dump;
+};
+
+struct qca_memdump_event_hdr {
+   __u8evt;
+   __u8plen;
+   __u16   opcode;
+   __u16   seq_no;
+   __u8reserved;
+} __packed;
+
+
+struct qca_dump_size {
+   u32 dump_size;
+} __packed;
+
 struct qca_data {
struct hci_uart *hu;
struct sk_buff *rx_skb;
struct sk_buff_head txq;
-   struct sk_buff_head tx_wait_q;  /* HCI_IBS wait queue   */
-   spinlock_t hci_ibs_lock;/* HCI_IBS state lock   */
+   struct sk_buff_head tx_wait_q;  /* HCI_IBS wait queue   */
+   struct sk_buff_head rx_memdump_q;   /* Memdump wait queue   */
+   spinlock_t hci_ibs_lock;/* HCI_IBS state lock   */
u8 tx_ibs_state;/* HCI_IBS transmit side power state*/
u8 rx_ibs_state;/* HCI_IBS receive side power state */
bool tx_vote;   /* Clock must be on for TX */
@@ -103,12 +142,17 @@ struct qca_data {
u32 tx_idle_delay;
struct timer_list wake_retrans_timer;
u32 wake_retrans;
+   struct timer_list memdump_timer;
+   u32 memdump_delay;
struct workqueue_struct *workqueue;
struct work_struct ws_awake_rx;
struct work_struct ws_awake_device;
struct work_struct ws_rx_vote_off;
struct work_struct ws_tx_vote_off;
+   struct work_struct ctrl_memdump_evt;
+   struct qca_memdump_data *qca_memdump;
unsigned long flags;
+   enum qca_memdump_states memdump_state;
 
/* For debugging purpose */
u64 ibs_sent_wacks;
@@ -173,6 +217,7 @@ struct qca_serdev {
 static int qca_power_setup(struct hci_uart *hu, bool on);
 static void qca_power_shutdown(struct hci_uart *hu);
 static int qca_power_off(struct hci_dev *hdev);
+static void qca_controller_memdump(struct work_struct *work);
 
 static void __serial_clock_on(struct tty_struct *tty)
 {
@@ -446,6 +491,21 @@ static void hci_ibs_wake_retrans_timeout(struct timer_list 
*t)
hci_uart_tx_wakeup(hu);
 }
 
+static void hci_memdump_timeout(struct timer_list *t)
+{
+   struct qca_data *qca = from_timer(qca, t, tx_idle_timer);
+   struct hci_uart *hu = qca->hu;
+   struct qca_memdump_data *qca_memdump = qca->qca_memdump;
+   char *memdump_buf = qca_memdump->memdump_buf_tail;
+
+   bt_dev_err(hu->hdev, "clearing allocated memory due to memdump 
timeout");
+   kfree(memdump_buf);
+   kfree(qca_memdump);
+   qca->memdump_state = QCA_MEMDUMP_TIMEOUT;
+   del_timer(>memdump_timer);
+   cancel_work_sync(>ctrl_memdump_evt);
+}
+
 /* Initialize protocol */
 static int qca_open(struct hci_uart