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:3.641 / U:0.792 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site