Commit 9695ef87 authored by Abhishek Pandit-Subedi's avatar Abhishek Pandit-Subedi Committed by Luiz Augusto von Dentz

Bluetooth: Add support for hci devcoredump

Add devcoredump APIs to hci core so that drivers only have to provide
the dump skbs instead of managing the synchronization and timeouts.

The devcoredump APIs should be used in the following manner:
 - hci_devcoredump_init is called to allocate the dump.
 - hci_devcoredump_append is called to append any skbs with dump data
   OR hci_devcoredump_append_pattern is called to insert a pattern.
 - hci_devcoredump_complete is called when all dump packets have been
   sent OR hci_devcoredump_abort is called to indicate an error and
   cancel an ongoing dump collection.

The high level APIs just prepare some skbs with the appropriate data and
queue it for the dump to process. Packets part of the crashdump can be
intercepted in the driver in interrupt context and forwarded directly to
the devcoredump APIs.

Internally, there are 5 states for the dump: idle, active, complete,
abort and timeout. A devcoredump will only be in active state after it
has been initialized. Once active, it accepts data to be appended,
patterns to be inserted (i.e. memset) and a completion event or an abort
event to generate a devcoredump. The timeout is initialized at the same
time the dump is initialized (defaulting to 10s) and will be cleared
either when the timeout occurs or the dump is complete or aborted.
Signed-off-by: default avatarAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Signed-off-by: default avatarManish Mandlik <mmandlik@google.com>
Reviewed-by: default avatarAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 77f542b1
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2022 Google Corporation
*/
#ifndef __COREDUMP_H
#define __COREDUMP_H
#define DEVCOREDUMP_TIMEOUT msecs_to_jiffies(10000) /* 10 sec */
typedef void (*coredump_t)(struct hci_dev *hdev);
typedef void (*dmp_hdr_t)(struct hci_dev *hdev, struct sk_buff *skb);
typedef void (*notify_change_t)(struct hci_dev *hdev, int state);
/* struct hci_devcoredump - Devcoredump state
*
* @supported: Indicates if FW dump collection is supported by driver
* @state: Current state of dump collection
* @timeout: Indicates a timeout for collecting the devcoredump
*
* @alloc_size: Total size of the dump
* @head: Start of the dump
* @tail: Pointer to current end of dump
* @end: head + alloc_size for easy comparisons
*
* @dump_q: Dump queue for state machine to process
* @dump_rx: Devcoredump state machine work
* @dump_timeout: Devcoredump timeout work
*
* @coredump: Called from the driver's .coredump() function.
* @dmp_hdr: Create a dump header to identify controller/fw/driver info
* @notify_change: Notify driver when devcoredump state has changed
*/
struct hci_devcoredump {
bool supported;
enum devcoredump_state {
HCI_DEVCOREDUMP_IDLE,
HCI_DEVCOREDUMP_ACTIVE,
HCI_DEVCOREDUMP_DONE,
HCI_DEVCOREDUMP_ABORT,
HCI_DEVCOREDUMP_TIMEOUT,
} state;
unsigned long timeout;
size_t alloc_size;
char *head;
char *tail;
char *end;
struct sk_buff_head dump_q;
struct work_struct dump_rx;
struct delayed_work dump_timeout;
coredump_t coredump;
dmp_hdr_t dmp_hdr;
notify_change_t notify_change;
};
#ifdef CONFIG_DEV_COREDUMP
void hci_devcd_reset(struct hci_dev *hdev);
void hci_devcd_rx(struct work_struct *work);
void hci_devcd_timeout(struct work_struct *work);
int hci_devcd_register(struct hci_dev *hdev, coredump_t coredump,
dmp_hdr_t dmp_hdr, notify_change_t notify_change);
int hci_devcd_init(struct hci_dev *hdev, u32 dump_size);
int hci_devcd_append(struct hci_dev *hdev, struct sk_buff *skb);
int hci_devcd_append_pattern(struct hci_dev *hdev, u8 pattern, u32 len);
int hci_devcd_complete(struct hci_dev *hdev);
int hci_devcd_abort(struct hci_dev *hdev);
#else
static inline void hci_devcd_reset(struct hci_dev *hdev) {}
static inline void hci_devcd_rx(struct work_struct *work) {}
static inline void hci_devcd_timeout(struct work_struct *work) {}
static inline int hci_devcd_register(struct hci_dev *hdev, coredump_t coredump,
dmp_hdr_t dmp_hdr,
notify_change_t notify_change)
{
return -EOPNOTSUPP;
}
static inline int hci_devcd_init(struct hci_dev *hdev, u32 dump_size)
{
return -EOPNOTSUPP;
}
static inline int hci_devcd_append(struct hci_dev *hdev, struct sk_buff *skb)
{
return -EOPNOTSUPP;
}
static inline int hci_devcd_append_pattern(struct hci_dev *hdev,
u8 pattern, u32 len)
{
return -EOPNOTSUPP;
}
static inline int hci_devcd_complete(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
static inline int hci_devcd_abort(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
#endif /* CONFIG_DEV_COREDUMP */
#endif /* __COREDUMP_H */
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <net/bluetooth/hci.h> #include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_sync.h> #include <net/bluetooth/hci_sync.h>
#include <net/bluetooth/hci_sock.h> #include <net/bluetooth/hci_sock.h>
#include <net/bluetooth/coredump.h>
/* HCI priority */ /* HCI priority */
#define HCI_PRIO_MAX 7 #define HCI_PRIO_MAX 7
...@@ -590,6 +591,10 @@ struct hci_dev { ...@@ -590,6 +591,10 @@ struct hci_dev {
const char *fw_info; const char *fw_info;
struct dentry *debugfs; struct dentry *debugfs;
#ifdef CONFIG_DEV_COREDUMP
struct hci_devcoredump dump;
#endif
struct device dev; struct device dev;
struct rfkill *rfkill; struct rfkill *rfkill;
...@@ -1497,6 +1502,15 @@ static inline void hci_set_aosp_capable(struct hci_dev *hdev) ...@@ -1497,6 +1502,15 @@ static inline void hci_set_aosp_capable(struct hci_dev *hdev)
#endif #endif
} }
static inline void hci_devcd_setup(struct hci_dev *hdev)
{
#ifdef CONFIG_DEV_COREDUMP
INIT_WORK(&hdev->dump.dump_rx, hci_devcd_rx);
INIT_DELAYED_WORK(&hdev->dump.dump_timeout, hci_devcd_timeout);
skb_queue_head_init(&hdev->dump.dump_q);
#endif
}
int hci_dev_open(__u16 dev); int hci_dev_open(__u16 dev);
int hci_dev_close(__u16 dev); int hci_dev_close(__u16 dev);
int hci_dev_do_close(struct hci_dev *hdev); int hci_dev_do_close(struct hci_dev *hdev);
......
...@@ -17,6 +17,8 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ ...@@ -17,6 +17,8 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
ecdh_helper.o hci_request.o mgmt_util.o mgmt_config.o hci_codec.o \ ecdh_helper.o hci_request.o mgmt_util.o mgmt_config.o hci_codec.o \
eir.o hci_sync.o eir.o hci_sync.o
bluetooth-$(CONFIG_DEV_COREDUMP) += coredump.o
bluetooth-$(CONFIG_BT_BREDR) += sco.o bluetooth-$(CONFIG_BT_BREDR) += sco.o
bluetooth-$(CONFIG_BT_LE) += iso.o bluetooth-$(CONFIG_BT_LE) += iso.o
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
......
This diff is collapsed.
...@@ -2544,6 +2544,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv) ...@@ -2544,6 +2544,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout); INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout);
INIT_DELAYED_WORK(&hdev->ncmd_timer, hci_ncmd_timeout); INIT_DELAYED_WORK(&hdev->ncmd_timer, hci_ncmd_timeout);
hci_devcd_setup(hdev);
hci_request_setup(hdev); hci_request_setup(hdev);
hci_init_sysfs(hdev); hci_init_sysfs(hdev);
......
...@@ -4727,6 +4727,8 @@ int hci_dev_open_sync(struct hci_dev *hdev) ...@@ -4727,6 +4727,8 @@ int hci_dev_open_sync(struct hci_dev *hdev)
goto done; goto done;
} }
hci_devcd_reset(hdev);
set_bit(HCI_RUNNING, &hdev->flags); set_bit(HCI_RUNNING, &hdev->flags);
hci_sock_dev_event(hdev, HCI_DEV_OPEN); hci_sock_dev_event(hdev, HCI_DEV_OPEN);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment