lkml.org 
[lkml]   [2022]   [Jul]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
From
Date
SubjectRe: [PATCH v2 1/2] Bluetooth: Add support for devcoredump
Hi Manish,

On Thu, Jul 7, 2022 at 3:29 PM Manish Mandlik <mmandlik@google.com> wrote:
>
> Hi Luiz,
>
>
> On Wed, Jun 29, 2022 at 5:07 PM Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>>
>> Hi Manish,
>>
>> On Thu, Jun 23, 2022 at 12:38 PM Manish Mandlik <mmandlik@google.com> wrote:
>> >
>> > From: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
>> >
>> > 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: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
>> > Signed-off-by: Manish Mandlik <mmandlik@google.com>
>> > Reviewed-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
>> > ---
>> >
>> > Changes in v2:
>> > - Move hci devcoredump implementation to new files
>> > - Move dump queue and dump work to hci_devcoredump struct
>> > - Add CONFIG_DEV_COREDUMP conditional compile
>>
>> Looks like you didn't add an experimental UUID for enabling it, it
>> should be per index and we mark as supported when the driver supports
>> it then userspace can mark it to be used via main.conf so we can
>> properly experiment with it before marking it as stable.
>
> We want to keep bluetooth devcoredump implementation to kernel only and not have any dependency on the userspace bluez. But I agree that we should not have it enabled by default without experimenting. So, I have added another attribute to enable/disable hci devcoredump and the default state is set to disabled. This can be enabled via bluetooth sysfs entry `enable_coredump`. I have sent a v3 series with this new patch to add a sysfs entry. Please take a look. I was anyway working on a mechanism to enable/disable devcoredump and I feel this could be the right way. Please let me know your thoughts. Thanks!

Not sure I understand about not depending on userspace BlueZ, I mean
we might need a userspace interface to enable it or are you suggesting
to not use the MGMT interface for enabling it? If there is a is a
generic method for enabling devcoredumps then yes that is probably
preferable otherwise if is bluetooth specific that is what MGMT
interface is for.

>
>>
>> >
>> > include/net/bluetooth/coredump.h | 109 +++++++
>> > include/net/bluetooth/hci_core.h | 5 +
>> > net/bluetooth/Makefile | 2 +
>> > net/bluetooth/coredump.c | 504 +++++++++++++++++++++++++++++++
>> > net/bluetooth/hci_core.c | 9 +
>> > net/bluetooth/hci_sync.c | 2 +
>> > 6 files changed, 631 insertions(+)
>> > create mode 100644 include/net/bluetooth/coredump.h
>> > create mode 100644 net/bluetooth/coredump.c
>> >
>> > diff --git a/include/net/bluetooth/coredump.h b/include/net/bluetooth/coredump.h
>> > new file mode 100644
>> > index 000000000000..73601c409c6e
>> > --- /dev/null
>> > +++ b/include/net/bluetooth/coredump.h
>> > @@ -0,0 +1,109 @@
>> > +// 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 int (*dmp_hdr_t)(struct hci_dev *hdev, char *buf, size_t size);
>> > +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
>> > + * @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
>> > + *
>> > + * @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;
>> > +
>> > + u32 alloc_size;
>> > + char *head;
>> > + char *tail;
>> > + char *end;
>> > +
>> > + struct sk_buff_head dump_q;
>> > + struct work_struct dump_rx;
>> > + struct delayed_work dump_timeout;
>> > +
>> > + dmp_hdr_t dmp_hdr;
>> > + notify_change_t notify_change;
>> > +};
>> > +
>> > +#ifdef CONFIG_DEV_COREDUMP
>> > +
>> > +void hci_devcoredump_reset(struct hci_dev *hdev);
>> > +void hci_devcoredump_rx(struct work_struct *work);
>> > +void hci_devcoredump_timeout(struct work_struct *work);
>> > +int hci_devcoredump_register(struct hci_dev *hdev, dmp_hdr_t dmp_hdr,
>> > + notify_change_t notify_change);
>> > +int hci_devcoredump_init(struct hci_dev *hdev, u32 dmp_size);
>> > +int hci_devcoredump_append(struct hci_dev *hdev, struct sk_buff *skb);
>> > +int hci_devcoredump_append_pattern(struct hci_dev *hdev, u8 pattern, u32 len);
>> > +int hci_devcoredump_complete(struct hci_dev *hdev);
>> > +int hci_devcoredump_abort(struct hci_dev *hdev);
>> > +
>> > +#else
>> > +
>> > +static inline void hci_devcoredump_reset(struct hci_dev *hdev) {}
>> > +static inline void hci_devcoredump_rx(struct work_struct *work) {}
>> > +static inline void hci_devcoredump_timeout(struct work_struct *work) {}
>> > +
>> > +static inline int hci_devcoredump_register(struct hci_dev *hdev,
>> > + dmp_hdr_t dmp_hdr,
>> > + notify_change_t notify_change)
>> > +{
>> > + return -EOPNOTSUPP;
>> > +}
>> > +
>> > +static inline int hci_devcoredump_init(struct hci_dev *hdev, u32 dmp_size)
>> > +{
>> > + return -EOPNOTSUPP;
>> > +}
>> > +
>> > +static inline int hci_devcoredump_append(struct hci_dev *hdev,
>> > + struct sk_buff *skb)
>> > +{
>> > + return -EOPNOTSUPP;
>> > +}
>> > +
>> > +static inline int hci_devcoredump_append_pattern(struct hci_dev *hdev,
>> > + u8 pattern, u32 len)
>> > +{
>> > + return -EOPNOTSUPP;
>> > +}
>> > +
>> > +static inline int hci_devcoredump_complete(struct hci_dev *hdev)
>> > +{
>> > + return -EOPNOTSUPP;
>> > +}
>> > +
>> > +static inline int hci_devcoredump_abort(struct hci_dev *hdev)
>> > +{
>> > + return -EOPNOTSUPP;
>> > +}
>> > +
>> > +#endif /* CONFIG_DEV_COREDUMP */
>> > +
>> > +#endif /* __COREDUMP_H */
>> > diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
>> > index 15237ee5f761..9ccc034c8fde 100644
>> > --- a/include/net/bluetooth/hci_core.h
>> > +++ b/include/net/bluetooth/hci_core.h
>> > @@ -32,6 +32,7 @@
>> > #include <net/bluetooth/hci.h>
>> > #include <net/bluetooth/hci_sync.h>
>> > #include <net/bluetooth/hci_sock.h>
>> > +#include <net/bluetooth/coredump.h>
>> >
>> > /* HCI priority */
>> > #define HCI_PRIO_MAX 7
>> > @@ -582,6 +583,10 @@ struct hci_dev {
>> > const char *fw_info;
>> > struct dentry *debugfs;
>> >
>> > +#ifdef CONFIG_DEV_COREDUMP
>> > + struct hci_devcoredump dump;
>> > +#endif
>> > +
>> > struct device dev;
>> >
>> > struct rfkill *rfkill;
>> > diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
>> > index a52bba8500e1..4e894e452869 100644
>> > --- a/net/bluetooth/Makefile
>> > +++ b/net/bluetooth/Makefile
>> > @@ -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 \
>> > eir.o hci_sync.o
>> >
>> > +bluetooth-$(CONFIG_DEV_COREDUMP) += coredump.o
>> > +
>> > bluetooth-$(CONFIG_BT_BREDR) += sco.o
>> > bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
>> > bluetooth-$(CONFIG_BT_LEDS) += leds.o
>> > diff --git a/net/bluetooth/coredump.c b/net/bluetooth/coredump.c
>> > new file mode 100644
>> > index 000000000000..43c355cd7ad3
>> > --- /dev/null
>> > +++ b/net/bluetooth/coredump.c
>> > @@ -0,0 +1,504 @@
>> > +// SPDX-License-Identifier: GPL-2.0-only
>> > +/*
>> > + * Copyright (C) 2022 Google Corporation
>> > + */
>> > +
>> > +#include <linux/devcoredump.h>
>> > +
>> > +#include <net/bluetooth/bluetooth.h>
>> > +#include <net/bluetooth/hci_core.h>
>> > +
>> > +enum hci_devcoredump_pkt_type {
>> > + HCI_DEVCOREDUMP_PKT_INIT,
>> > + HCI_DEVCOREDUMP_PKT_SKB,
>> > + HCI_DEVCOREDUMP_PKT_PATTERN,
>> > + HCI_DEVCOREDUMP_PKT_COMPLETE,
>> > + HCI_DEVCOREDUMP_PKT_ABORT,
>> > +};
>> > +
>> > +struct hci_devcoredump_skb_cb {
>> > + u16 pkt_type;
>> > +};
>> > +
>> > +struct hci_devcoredump_skb_pattern {
>> > + u8 pattern;
>> > + u32 len;
>> > +} __packed;
>> > +
>> > +#define hci_dmp_cb(skb) ((struct hci_devcoredump_skb_cb *)((skb)->cb))
>> > +
>> > +#define MAX_DEVCOREDUMP_HDR_SIZE 512 /* bytes */
>> > +
>> > +static int hci_devcoredump_update_hdr_state(char *buf, size_t size, int state)
>> > +{
>> > + if (!buf)
>> > + return 0;
>> > +
>> > + return snprintf(buf, size, "Bluetooth devcoredump\nState: %d\n", state);
>> > +}
>> > +
>> > +/* Call with hci_dev_lock only. */
>> > +static int hci_devcoredump_update_state(struct hci_dev *hdev, int state)
>> > +{
>> > + hdev->dump.state = state;
>> > +
>> > + return hci_devcoredump_update_hdr_state(hdev->dump.head,
>> > + hdev->dump.alloc_size, state);
>> > +}
>> > +
>> > +static int hci_devcoredump_mkheader(struct hci_dev *hdev, char *buf,
>> > + size_t buf_size)
>> > +{
>> > + char *ptr = buf;
>> > + size_t rem = buf_size;
>> > + size_t read = 0;
>> > +
>> > + read = hci_devcoredump_update_hdr_state(ptr, rem, HCI_DEVCOREDUMP_IDLE);
>> > + read += 1; /* update_hdr_state adds \0 at the end upon state rewrite */
>> > + rem -= read;
>> > + ptr += read;
>> > +
>> > + if (hdev->dump.dmp_hdr) {
>> > + /* dmp_hdr() should return number of bytes written */
>> > + read = hdev->dump.dmp_hdr(hdev, ptr, rem);
>> > + rem -= read;
>> > + ptr += read;
>> > + }
>> > +
>> > + read = snprintf(ptr, rem, "--- Start dump ---\n");
>> > + rem -= read;
>> > + ptr += read;
>> > +
>> > + return buf_size - rem;
>> > +}
>> > +
>> > +/* Do not call with hci_dev_lock since this calls driver code. */
>> > +static void hci_devcoredump_notify(struct hci_dev *hdev, int state)
>> > +{
>> > + if (hdev->dump.notify_change)
>> > + hdev->dump.notify_change(hdev, state);
>> > +}
>> > +
>> > +/* Call with hci_dev_lock only. */
>> > +void hci_devcoredump_reset(struct hci_dev *hdev)
>> > +{
>> > + hdev->dump.head = NULL;
>> > + hdev->dump.tail = NULL;
>> > + hdev->dump.alloc_size = 0;
>> > +
>> > + hci_devcoredump_update_state(hdev, HCI_DEVCOREDUMP_IDLE);
>> > +
>> > + cancel_delayed_work(&hdev->dump.dump_timeout);
>> > + skb_queue_purge(&hdev->dump.dump_q);
>> > +}
>> > +
>> > +/* Call with hci_dev_lock only. */
>> > +static void hci_devcoredump_free(struct hci_dev *hdev)
>> > +{
>> > + if (hdev->dump.head)
>> > + vfree(hdev->dump.head);
>> > +
>> > + hci_devcoredump_reset(hdev);
>> > +}
>> > +
>> > +/* Call with hci_dev_lock only. */
>> > +static int hci_devcoredump_alloc(struct hci_dev *hdev, u32 size)
>> > +{
>> > + hdev->dump.head = vmalloc(size);
>> > + if (!hdev->dump.head)
>> > + return -ENOMEM;
>> > +
>> > + hdev->dump.alloc_size = size;
>> > + hdev->dump.tail = hdev->dump.head;
>> > + hdev->dump.end = hdev->dump.head + size;
>> > +
>> > + hci_devcoredump_update_state(hdev, HCI_DEVCOREDUMP_IDLE);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +/* Call with hci_dev_lock only. */
>> > +static bool hci_devcoredump_copy(struct hci_dev *hdev, char *buf, u32 size)
>> > +{
>> > + if (hdev->dump.tail + size > hdev->dump.end)
>> > + return false;
>> > +
>> > + memcpy(hdev->dump.tail, buf, size);
>> > + hdev->dump.tail += size;
>> > +
>> > + return true;
>> > +}
>> > +
>> > +/* Call with hci_dev_lock only. */
>> > +static bool hci_devcoredump_memset(struct hci_dev *hdev, u8 pattern, u32 len)
>> > +{
>> > + if (hdev->dump.tail + len > hdev->dump.end)
>> > + return false;
>> > +
>> > + memset(hdev->dump.tail, pattern, len);
>> > + hdev->dump.tail += len;
>> > +
>> > + return true;
>> > +}
>> > +
>> > +/* Call with hci_dev_lock only. */
>> > +static int hci_devcoredump_prepare(struct hci_dev *hdev, u32 dump_size)
>> > +{
>> > + char *dump_hdr;
>> > + int dump_hdr_size;
>> > + u32 size;
>> > + int err = 0;
>> > +
>> > + dump_hdr = vmalloc(MAX_DEVCOREDUMP_HDR_SIZE);
>> > + if (!dump_hdr) {
>> > + err = -ENOMEM;
>> > + goto hdr_free;
>> > + }
>> > +
>> > + dump_hdr_size = hci_devcoredump_mkheader(hdev, dump_hdr,
>> > + MAX_DEVCOREDUMP_HDR_SIZE);
>> > + size = dump_hdr_size + dump_size;
>> > +
>> > + if (hci_devcoredump_alloc(hdev, size)) {
>> > + err = -ENOMEM;
>> > + goto hdr_free;
>> > + }
>> > +
>> > + /* Insert the device header */
>> > + if (!hci_devcoredump_copy(hdev, dump_hdr, dump_hdr_size)) {
>> > + bt_dev_err(hdev, "Failed to insert header");
>> > + hci_devcoredump_free(hdev);
>> > +
>> > + err = -ENOMEM;
>> > + goto hdr_free;
>> > + }
>> > +
>> > +hdr_free:
>> > + if (dump_hdr)
>> > + vfree(dump_hdr);
>> > +
>> > + return err;
>> > +}
>> > +
>> > +/* Bluetooth devcoredump state machine.
>> > + *
>> > + * Devcoredump states:
>> > + *
>> > + * HCI_DEVCOREDUMP_IDLE: The default state.
>> > + *
>> > + * HCI_DEVCOREDUMP_ACTIVE: A devcoredump will be in this state once it has
>> > + * been initialized using hci_devcoredump_init(). Once active, the
>> > + * driver can append data using hci_devcoredump_append() or insert
>> > + * a pattern using hci_devcoredump_append_pattern().
>> > + *
>> > + * HCI_DEVCOREDUMP_DONE: Once the dump collection is complete, the drive
>> > + * can signal the completion using hci_devcoredump_complete(). A
>> > + * devcoredump is generated indicating the completion event and
>> > + * then the state machine is reset to the default state.
>> > + *
>> > + * HCI_DEVCOREDUMP_ABORT: The driver can cancel ongoing dump collection in
>> > + * case of any error using hci_devcoredump_abort(). A devcoredump
>> > + * is still generated with the available data indicating the abort
>> > + * event and then the state machine is reset to the default state.
>> > + *
>> > + * HCI_DEVCOREDUMP_TIMEOUT: A timeout timer for HCI_DEVCOREDUMP_TIMEOUT sec
>> > + * is started during devcoredump initialization. Once the timeout
>> > + * occurs, the driver is notified, a devcoredump is generated with
>> > + * the available data indicating the timeout event and then the
>> > + * state machine is reset to the default state.
>> > + *
>> > + * The driver must register using hci_devcoredump_register() before using the
>> > + * hci devcoredump APIs.
>> > + */
>> > +void hci_devcoredump_rx(struct work_struct *work)
>> > +{
>> > + struct hci_dev *hdev = container_of(work, struct hci_dev, dump.dump_rx);
>> > + struct sk_buff *skb;
>> > + struct hci_devcoredump_skb_pattern *pattern;
>> > + u32 dump_size;
>> > + int start_state;
>> > +
>> > +#define DBG_UNEXPECTED_STATE() \
>> > + bt_dev_dbg(hdev, \
>> > + "Unexpected packet (%d) for state (%d). ", \
>> > + hci_dmp_cb(skb)->pkt_type, hdev->dump.state)
>> > +
>> > + while ((skb = skb_dequeue(&hdev->dump.dump_q))) {
>> > + hci_dev_lock(hdev);
>> > + start_state = hdev->dump.state;
>> > +
>> > + switch (hci_dmp_cb(skb)->pkt_type) {
>> > + case HCI_DEVCOREDUMP_PKT_INIT:
>> > + if (hdev->dump.state != HCI_DEVCOREDUMP_IDLE) {
>> > + DBG_UNEXPECTED_STATE();
>> > + goto loop_continue;
>> > + }
>> > +
>> > + if (skb->len != sizeof(dump_size)) {
>> > + bt_dev_dbg(hdev, "Invalid dump init pkt");
>> > + goto loop_continue;
>> > + }
>> > +
>> > + dump_size = *((u32 *)skb->data);
>> > + if (!dump_size) {
>> > + bt_dev_err(hdev, "Zero size dump init pkt");
>> > + goto loop_continue;
>> > + }
>> > +
>> > + if (hci_devcoredump_prepare(hdev, dump_size)) {
>> > + bt_dev_err(hdev, "Failed to prepare for dump");
>> > + goto loop_continue;
>> > + }
>> > +
>> > + hci_devcoredump_update_state(hdev,
>> > + HCI_DEVCOREDUMP_ACTIVE);
>> > + queue_delayed_work(hdev->workqueue,
>> > + &hdev->dump.dump_timeout,
>> > + DEVCOREDUMP_TIMEOUT);
>> > + break;
>> > +
>> > + case HCI_DEVCOREDUMP_PKT_SKB:
>> > + if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) {
>> > + DBG_UNEXPECTED_STATE();
>> > + goto loop_continue;
>> > + }
>> > +
>> > + if (!hci_devcoredump_copy(hdev, skb->data, skb->len))
>> > + bt_dev_dbg(hdev, "Failed to insert skb");
>> > + break;
>> > +
>> > + case HCI_DEVCOREDUMP_PKT_PATTERN:
>> > + if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) {
>> > + DBG_UNEXPECTED_STATE();
>> > + goto loop_continue;
>> > + }
>> > +
>> > + if (skb->len != sizeof(*pattern)) {
>> > + bt_dev_dbg(hdev, "Invalid pattern skb");
>> > + goto loop_continue;
>> > + }
>> > +
>> > + pattern = (void *)skb->data;
>> > +
>> > + if (!hci_devcoredump_memset(hdev, pattern->pattern,
>> > + pattern->len))
>> > + bt_dev_dbg(hdev, "Failed to set pattern");
>> > + break;
>> > +
>> > + case HCI_DEVCOREDUMP_PKT_COMPLETE:
>> > + if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) {
>> > + DBG_UNEXPECTED_STATE();
>> > + goto loop_continue;
>> > + }
>> > +
>> > + hci_devcoredump_update_state(hdev,
>> > + HCI_DEVCOREDUMP_DONE);
>> > + dump_size = hdev->dump.tail - hdev->dump.head;
>> > +
>> > + bt_dev_info(hdev,
>> > + "Devcoredump complete with size %u "
>> > + "(expect %u)",
>> > + dump_size, hdev->dump.alloc_size);
>> > +
>> > + dev_coredumpv(&hdev->dev, hdev->dump.head, dump_size,
>> > + GFP_KERNEL);
>> > + break;
>> > +
>> > + case HCI_DEVCOREDUMP_PKT_ABORT:
>> > + if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) {
>> > + DBG_UNEXPECTED_STATE();
>> > + goto loop_continue;
>> > + }
>> > +
>> > + hci_devcoredump_update_state(hdev,
>> > + HCI_DEVCOREDUMP_ABORT);
>> > + dump_size = hdev->dump.tail - hdev->dump.head;
>> > +
>> > + bt_dev_info(hdev,
>> > + "Devcoredump aborted with size %u "
>> > + "(expect %u)",
>> > + dump_size, hdev->dump.alloc_size);
>> > +
>> > + /* Emit a devcoredump with the available data */
>> > + dev_coredumpv(&hdev->dev, hdev->dump.head, dump_size,
>> > + GFP_KERNEL);
>> > + break;
>> > +
>> > + default:
>> > + bt_dev_dbg(hdev,
>> > + "Unknown packet (%d) for state (%d). ",
>> > + hci_dmp_cb(skb)->pkt_type, hdev->dump.state);
>> > + break;
>> > + }
>> > +
>> > +loop_continue:
>> > + kfree_skb(skb);
>> > + hci_dev_unlock(hdev);
>> > +
>> > + if (start_state != hdev->dump.state)
>> > + hci_devcoredump_notify(hdev, hdev->dump.state);
>> > +
>> > + hci_dev_lock(hdev);
>> > + if (hdev->dump.state == HCI_DEVCOREDUMP_DONE ||
>> > + hdev->dump.state == HCI_DEVCOREDUMP_ABORT)
>> > + hci_devcoredump_reset(hdev);
>> > + hci_dev_unlock(hdev);
>> > + }
>> > +}
>> > +EXPORT_SYMBOL(hci_devcoredump_rx);
>> > +
>> > +void hci_devcoredump_timeout(struct work_struct *work)
>> > +{
>> > + struct hci_dev *hdev = container_of(work, struct hci_dev,
>> > + dump.dump_timeout.work);
>> > + u32 dump_size;
>> > +
>> > + hci_devcoredump_notify(hdev, HCI_DEVCOREDUMP_TIMEOUT);
>> > +
>> > + hci_dev_lock(hdev);
>> > +
>> > + cancel_work_sync(&hdev->dump.dump_rx);
>> > +
>> > + hci_devcoredump_update_state(hdev, HCI_DEVCOREDUMP_TIMEOUT);
>> > + dump_size = hdev->dump.tail - hdev->dump.head;
>> > + bt_dev_info(hdev, "Devcoredump timeout with size %u (expect %u)",
>> > + dump_size, hdev->dump.alloc_size);
>> > +
>> > + /* Emit a devcoredump with the available data */
>> > + dev_coredumpv(&hdev->dev, hdev->dump.head, dump_size, GFP_KERNEL);
>> > +
>> > + hci_devcoredump_reset(hdev);
>> > +
>> > + hci_dev_unlock(hdev);
>> > +}
>> > +EXPORT_SYMBOL(hci_devcoredump_timeout);
>> > +
>> > +int hci_devcoredump_register(struct hci_dev *hdev, dmp_hdr_t dmp_hdr,
>> > + notify_change_t notify_change)
>> > +{
>> > + /* driver must implement dmp_hdr() function for bluetooth devcoredump */
>> > + if (!dmp_hdr)
>> > + return -EINVAL;
>> > +
>> > + hci_dev_lock(hdev);
>> > + hdev->dump.dmp_hdr = dmp_hdr;
>> > + hdev->dump.notify_change = notify_change;
>> > + hdev->dump.supported = true;
>> > + hci_dev_unlock(hdev);
>> > +
>> > + return 0;
>> > +}
>> > +EXPORT_SYMBOL(hci_devcoredump_register);
>> > +
>> > +int hci_devcoredump_init(struct hci_dev *hdev, u32 dmp_size)
>> > +{
>> > + struct sk_buff *skb = NULL;
>> > +
>> > + if (!hdev->dump.supported)
>> > + return -EOPNOTSUPP;
>> > +
>> > + skb = alloc_skb(sizeof(dmp_size), GFP_ATOMIC);
>> > + if (!skb) {
>> > + bt_dev_err(hdev, "Failed to allocate devcoredump init");
>> > + return -ENOMEM;
>> > + }
>> > +
>> > + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_INIT;
>> > + skb_put_data(skb, &dmp_size, sizeof(dmp_size));
>> > +
>> > + skb_queue_tail(&hdev->dump.dump_q, skb);
>> > + queue_work(hdev->workqueue, &hdev->dump.dump_rx);
>> > +
>> > + return 0;
>> > +}
>> > +EXPORT_SYMBOL(hci_devcoredump_init);
>> > +
>> > +int hci_devcoredump_append(struct hci_dev *hdev, struct sk_buff *skb)
>> > +{
>> > + if (!skb)
>> > + return -ENOMEM;
>> > +
>> > + if (!hdev->dump.supported) {
>> > + kfree_skb(skb);
>> > + return -EOPNOTSUPP;
>> > + }
>> > +
>> > + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_SKB;
>> > +
>> > + skb_queue_tail(&hdev->dump.dump_q, skb);
>> > + queue_work(hdev->workqueue, &hdev->dump.dump_rx);
>> > +
>> > + return 0;
>> > +}
>> > +EXPORT_SYMBOL(hci_devcoredump_append);
>> > +
>> > +int hci_devcoredump_append_pattern(struct hci_dev *hdev, u8 pattern, u32 len)
>> > +{
>> > + struct hci_devcoredump_skb_pattern p;
>> > + struct sk_buff *skb = NULL;
>> > +
>> > + if (!hdev->dump.supported)
>> > + return -EOPNOTSUPP;
>> > +
>> > + skb = alloc_skb(sizeof(p), GFP_ATOMIC);
>> > + if (!skb) {
>> > + bt_dev_err(hdev, "Failed to allocate devcoredump pattern");
>> > + return -ENOMEM;
>> > + }
>> > +
>> > + p.pattern = pattern;
>> > + p.len = len;
>> > +
>> > + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_PATTERN;
>> > + skb_put_data(skb, &p, sizeof(p));
>> > +
>> > + skb_queue_tail(&hdev->dump.dump_q, skb);
>> > + queue_work(hdev->workqueue, &hdev->dump.dump_rx);
>> > +
>> > + return 0;
>> > +}
>> > +EXPORT_SYMBOL(hci_devcoredump_append_pattern);
>> > +
>> > +int hci_devcoredump_complete(struct hci_dev *hdev)
>> > +{
>> > + struct sk_buff *skb = NULL;
>> > +
>> > + if (!hdev->dump.supported)
>> > + return -EOPNOTSUPP;
>> > +
>> > + skb = alloc_skb(0, GFP_ATOMIC);
>> > + if (!skb) {
>> > + bt_dev_err(hdev, "Failed to allocate devcoredump complete");
>> > + return -ENOMEM;
>> > + }
>> > +
>> > + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_COMPLETE;
>> > +
>> > + skb_queue_tail(&hdev->dump.dump_q, skb);
>> > + queue_work(hdev->workqueue, &hdev->dump.dump_rx);
>> > +
>> > + return 0;
>> > +}
>> > +EXPORT_SYMBOL(hci_devcoredump_complete);
>> > +
>> > +int hci_devcoredump_abort(struct hci_dev *hdev)
>> > +{
>> > + struct sk_buff *skb = NULL;
>> > +
>> > + if (!hdev->dump.supported)
>> > + return -EOPNOTSUPP;
>> > +
>> > + skb = alloc_skb(0, GFP_ATOMIC);
>> > + if (!skb) {
>> > + bt_dev_err(hdev, "Failed to allocate devcoredump abort");
>> > + return -ENOMEM;
>> > + }
>> > +
>> > + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_ABORT;
>> > +
>> > + skb_queue_tail(&hdev->dump.dump_q, skb);
>> > + queue_work(hdev->workqueue, &hdev->dump.dump_rx);
>> > +
>> > + return 0;
>> > +}
>> > +EXPORT_SYMBOL(hci_devcoredump_abort);
>> > diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
>> > index 05c13f639b94..743c5bdb8daa 100644
>> > --- a/net/bluetooth/hci_core.c
>> > +++ b/net/bluetooth/hci_core.c
>> > @@ -2516,14 +2516,23 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
>> > INIT_WORK(&hdev->tx_work, hci_tx_work);
>> > INIT_WORK(&hdev->power_on, hci_power_on);
>> > INIT_WORK(&hdev->error_reset, hci_error_reset);
>> > +#ifdef CONFIG_DEV_COREDUMP
>> > + INIT_WORK(&hdev->dump.dump_rx, hci_devcoredump_rx);
>> > +#endif
>> >
>> > hci_cmd_sync_init(hdev);
>> >
>> > INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
>> > +#ifdef CONFIG_DEV_COREDUMP
>> > + INIT_DELAYED_WORK(&hdev->dump.dump_timeout, hci_devcoredump_timeout);
>> > +#endif
>> >
>> > skb_queue_head_init(&hdev->rx_q);
>> > skb_queue_head_init(&hdev->cmd_q);
>> > skb_queue_head_init(&hdev->raw_q);
>> > +#ifdef CONFIG_DEV_COREDUMP
>> > + skb_queue_head_init(&hdev->dump.dump_q);
>> > +#endif
>> >
>> > init_waitqueue_head(&hdev->req_wait_q);
>> >
>> > diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
>> > index e5602e209b63..a3d049b55b70 100644
>> > --- a/net/bluetooth/hci_sync.c
>> > +++ b/net/bluetooth/hci_sync.c
>> > @@ -3924,6 +3924,8 @@ int hci_dev_open_sync(struct hci_dev *hdev)
>> > goto done;
>> > }
>> >
>> > + hci_devcoredump_reset(hdev);
>> > +
>> > set_bit(HCI_RUNNING, &hdev->flags);
>> > hci_sock_dev_event(hdev, HCI_DEV_OPEN);
>> >
>> > --
>> > 2.37.0.rc0.104.g0611611a94-goog
>> >
>>
>>
>> --
>> Luiz Augusto von Dentz
>
>
> Regards,
> Manish.



--
Luiz Augusto von Dentz

\
 
 \ /
  Last update: 2022-07-08 02:36    [W:0.071 / U:0.016 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site