Re: [RFC PATCH v3] Bluetooth: hci_qca: Collect controller memory dump during SSR
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
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