From patchwork Thu Mar 2 23:15:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manish Mandlik X-Patchwork-Id: 13158029 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AF070C678D4 for ; Thu, 2 Mar 2023 23:15:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229644AbjCBXP5 (ORCPT ); Thu, 2 Mar 2023 18:15:57 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57574 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229457AbjCBXPz (ORCPT ); Thu, 2 Mar 2023 18:15:55 -0500 Received: from mail-pf1-x44a.google.com (mail-pf1-x44a.google.com [IPv6:2607:f8b0:4864:20::44a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 48ED5580EB for ; Thu, 2 Mar 2023 15:15:53 -0800 (PST) Received: by mail-pf1-x44a.google.com with SMTP id p36-20020a056a000a2400b005f72df7d97bso322424pfh.19 for ; Thu, 02 Mar 2023 15:15:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc:subject :date:message-id:reply-to; bh=oebqMv4GM4Nz+vLWUOZbs38nKYzSza6yhLjASyIi2dE=; b=nHnQuwpIav0sZeWxiZvqzzvLXotDq5vx1WiX1obtpYlZSRHmaxWAhr8fw7gpjHlH3W Bq6Eer5h5RhNuM7IApgsPy6YxfF5ECm9uTB4pny1xTTXrNbH5PZjHdpkQyBryXuddfum BXM2L9km1ir2V3BPzckUzmryRt6uiqVkN00rpoJRP5wOU7jjcOijQPHyhg+S+mDOaveX vkbJAYJtooh5FVEpInx7tOsC+tx/EkD1NyE1QTvDxWxdl+R8EE4MMgatSIdWsjmi7VUz n2XuLJHUIuSuMYZReQYmTQO39YbFGA6IRgA1iljOw1V0gkWwGOW3cII/3leHkQ+9gm3f M/Tg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=oebqMv4GM4Nz+vLWUOZbs38nKYzSza6yhLjASyIi2dE=; b=PxLRdfeU17jUIv/6hlQNgHdIOXoUuQ/PtGXnmO/Tawpdw9+R9qlXecRMnpmP41X3gC RegZ6UgEzOcvkXr5dvofSmael+B3gMMg54q+AfN/ISwhpV3E7r/oC/JLOW+NOZyJ6BL8 O24rrplxR2w5U45YOFTMay87rdX4lqYlsi6nSZvOrUOOdRP1BXs8aT+1g3McSenG06yU mw9yPEXZH2QA3flVbNG/1htKN51kpmvNwRIDoYCzbBP/5vWB7WEnYEN9s0u6YlOP/edP oBK9ckPtnWBg7YZxXWVy+KzND2rsQ5qQMy+Hf/trnyjbavBZp/DQfTTXtGjDKv5eqgrY f7sQ== X-Gm-Message-State: AO0yUKVMYgVk+grIZ3Fhzt70oATL/Yc/zxb+PHf54gkRUMhbAq1uBL2+ 4yjt2TCKWJGREk/pd1BjhkharCza5tRHpQ== X-Google-Smtp-Source: AK7set+sBDFoAxt9w05ySeS8K+2rDr8hC69eGdSqWz4TqLrKymQfOwJNzAftwNdsbx7wqsoSffFTqJ2PJc9WyA== X-Received: from mmandlik-cloudtop.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:2893]) (user=mmandlik job=sendgmr) by 2002:a17:903:449:b0:19a:87dd:9206 with SMTP id iw9-20020a170903044900b0019a87dd9206mr18646plb.0.1677798952662; Thu, 02 Mar 2023 15:15:52 -0800 (PST) Date: Thu, 2 Mar 2023 15:15:44 -0800 Mime-Version: 1.0 X-Mailer: git-send-email 2.40.0.rc0.216.gc4246ad0f0-goog Message-ID: <20230302151413.v7.1.I9b4e4818bab450657b19cda3497d363c9baa616e@changeid> Subject: [PATCH v7 1/4] Bluetooth: Add support for hci devcoredump From: Manish Mandlik To: marcel@holtmann.org, luiz.dentz@gmail.com Cc: chromeos-bluetooth-upstreaming@chromium.org, linux-bluetooth@vger.kernel.org, Abhishek Pandit-Subedi , Manish Mandlik , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Johan Hedberg , Paolo Abeni , linux-kernel@vger.kernel.org, netdev@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Abhishek Pandit-Subedi 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 Signed-off-by: Manish Mandlik Reviewed-by: Abhishek Pandit-Subedi --- (no changes since v6) Changes in v6: - Remove #ifdef from .c and move to function in .h as per suggestion - Remove coredump_enabled from hci_devcoredump struct since the sysfs flag related change has been abandoned Changes in v4: - Add .enabled() and .coredump() to hci_devcoredump struct Changes in v3: - Add attribute to enable/disable and set default state to disabled 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 include/net/bluetooth/coredump.h | 114 +++++++ include/net/bluetooth/hci_core.h | 14 + net/bluetooth/Makefile | 2 + net/bluetooth/coredump.c | 515 +++++++++++++++++++++++++++++++ net/bluetooth/hci_core.c | 1 + net/bluetooth/hci_sync.c | 2 + 6 files changed, 648 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..5da253527cfe --- /dev/null +++ b/include/net/bluetooth/coredump.h @@ -0,0 +1,114 @@ +/* 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 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 + * + * @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; + + 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_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, coredump_t coredump, + 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, + coredump_t coredump, + 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 9488671c1219..e405fd4d25db 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -32,6 +32,7 @@ #include #include #include +#include /* HCI priority */ #define HCI_PRIO_MAX 7 @@ -590,6 +591,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; @@ -1496,6 +1501,15 @@ static inline void hci_set_aosp_capable(struct hci_dev *hdev) #endif } +static inline void hci_devcoredump_setup(struct hci_dev *hdev) +{ +#ifdef CONFIG_DEV_COREDUMP + INIT_WORK(&hdev->dump.dump_rx, hci_devcoredump_rx); + INIT_DELAYED_WORK(&hdev->dump.dump_timeout, hci_devcoredump_timeout); + skb_queue_head_init(&hdev->dump.dump_q); +#endif +} + int hci_dev_open(__u16 dev); int hci_dev_close(__u16 dev); int hci_dev_do_close(struct hci_dev *hdev); diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 0e7b7db42750..141ac1fda0bf 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_LE) += iso.o bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o diff --git a/net/bluetooth/coredump.c b/net/bluetooth/coredump.c new file mode 100644 index 000000000000..ce9e36f77323 --- /dev/null +++ b/net/bluetooth/coredump.c @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Google Corporation + */ + +#include + +#include +#include + +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 %zu)", + 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 %zu)", + 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 %zu)", + 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, coredump_t coredump, + dmp_hdr_t dmp_hdr, notify_change_t notify_change) +{ + /* Driver must implement coredump() and dmp_hdr() functions for + * bluetooth devcoredump. The coredump() should trigger a coredump + * event on the controller when the device's coredump sysfs entry is + * written to. The dmp_hdr() should create a dump header to identify + * the controller/fw/driver info. + */ + if (!coredump || !dmp_hdr) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->dump.coredump = coredump; + 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); + +static inline bool hci_devcoredump_enabled(struct hci_dev *hdev) +{ + return hdev->dump.supported; +} + +int hci_devcoredump_init(struct hci_dev *hdev, u32 dmp_size) +{ + struct sk_buff *skb = NULL; + + if (!hci_devcoredump_enabled(hdev)) + 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 (!hci_devcoredump_enabled(hdev)) { + 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 (!hci_devcoredump_enabled(hdev)) + 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 (!hci_devcoredump_enabled(hdev)) + 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 (!hci_devcoredump_enabled(hdev)) + 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 334e308451f5..37aa70192ccb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -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->ncmd_timer, hci_ncmd_timeout); + hci_devcoredump_setup(hdev); hci_request_setup(hdev); hci_init_sysfs(hdev); diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 561a519a11bd..e0423867ff19 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4722,6 +4722,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); From patchwork Thu Mar 2 23:15:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manish Mandlik X-Patchwork-Id: 13158030 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 22013C6FA8E for ; Thu, 2 Mar 2023 23:15:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229749AbjCBXP5 (ORCPT ); Thu, 2 Mar 2023 18:15:57 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57588 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229625AbjCBXP4 (ORCPT ); Thu, 2 Mar 2023 18:15:56 -0500 Received: from mail-pf1-x449.google.com (mail-pf1-x449.google.com [IPv6:2607:f8b0:4864:20::449]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 426EB580FA for ; Thu, 2 Mar 2023 15:15:55 -0800 (PST) Received: by mail-pf1-x449.google.com with SMTP id y35-20020a056a00182300b005e8e2c6afe2so336484pfa.12 for ; Thu, 02 Mar 2023 15:15:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=6ZIXCTLgoXIOnNOIeD8QH+8i1pHPgpgdgWc+E/qLbmc=; b=X4WtmS/JKsbbQP4HnbFUXwuKUKnZrd1pJwRJovvs8he5VPYfl51dKrtoev3eFk/3Vh sOBhreI7OWswTZMQM+H7v+wHvShRKaEChs4Fx2zZ+xy+Kk9UEpd+4NuaAuJjGqqgExq2 yVqY6FY5OVwICbWaDNCH7L6BQFm/MOSd60gco+RM5xlATgFkC172ZJIZfUn1Q435Vfd6 WVgWhSmDVaFz+BGjm6uBznK/s4UyV0Kvd7V45iauL1xqQ20aBgSu/AGfL4S9jcq/g35F R2UbXvtcqsxCB6PR925NXAiv7fekjPIq5H2kZdP2HGK90aOkdgxsOymbd6CTCam9VBvM tqtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=6ZIXCTLgoXIOnNOIeD8QH+8i1pHPgpgdgWc+E/qLbmc=; b=qcMZUUfkR/EKBzFMyJ2BAoHFIkvGEc73in/b7IJEVEQU5EiwdQQzn2SC2o+LDsvX7S HWuv2/6p/L2UvOe7BhgJ60EO6H7yAYobN6+dffQa9ncxhSTDdU+GBWrbPbRfmVRbFrjj 7aE3WVeNjdbQvogjXPoE1UgV7stXMreTZchXIoRMP+BBVjcHMQtQR5Nuh7OEnNPNTipm 6knGaJDlKV2w9+DNv0xOVZVlvIZpUDnCWECXWN02X5hv4E2QXKhLOdzgeqyZk4B9p5sK p4UjZgHLzRHKnWywG473f5F2LwkvAtxuKa13TyiugDF7qoMFSiUnyHrJlvC8A5aCUUK4 3VLA== X-Gm-Message-State: AO0yUKU9ZJ81s1NbhONfruJCXOTF8oxNF9dlrNXqAI5iPvgU7RRi8con /lcMHkPpexeton+nIcs56iaG0ukS9Zee7Q== X-Google-Smtp-Source: AK7set/FgNGD2kdx45DRPPsGP9xZbqFnU3lIWQmGawpDz3HSgQ5ThH32WmV8JgPmwyOvvf7O9Vel3jnLB+Z1Uw== X-Received: from mmandlik-cloudtop.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:2893]) (user=mmandlik job=sendgmr) by 2002:a17:903:2483:b0:19b:f946:e57d with SMTP id p3-20020a170903248300b0019bf946e57dmr4264426plw.11.1677798954729; Thu, 02 Mar 2023 15:15:54 -0800 (PST) Date: Thu, 2 Mar 2023 15:15:45 -0800 In-Reply-To: <20230302151413.v7.1.I9b4e4818bab450657b19cda3497d363c9baa616e@changeid> Mime-Version: 1.0 References: <20230302151413.v7.1.I9b4e4818bab450657b19cda3497d363c9baa616e@changeid> X-Mailer: git-send-email 2.40.0.rc0.216.gc4246ad0f0-goog Message-ID: <20230302151413.v7.2.Ief9a81a3643d2291f6db2b3695c3a6e0159467dc@changeid> Subject: [PATCH v7 2/4] Bluetooth: Add vhci devcoredump support From: Manish Mandlik To: marcel@holtmann.org, luiz.dentz@gmail.com Cc: chromeos-bluetooth-upstreaming@chromium.org, linux-bluetooth@vger.kernel.org, Manish Mandlik , Johan Hedberg , linux-kernel@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org Add devcoredump support for vhci that creates forcce_devcoredump debugfs entry. This is used for mgmt-tester tests. Signed-off-by: Manish Mandlik --- Changes in v7: - New patch in the series drivers/bluetooth/Kconfig | 1 + drivers/bluetooth/hci_vhci.c | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 5a1a7bec3c42..7bc7a765ad69 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -363,6 +363,7 @@ config BT_HCIBLUECARD config BT_HCIVHCI tristate "HCI VHCI (Virtual HCI device) driver" + select WANT_DEV_COREDUMP help Bluetooth Virtual HCI device driver. This driver is required if you want to use HCI Emulation software. diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index c443c3b0a4da..8a74e66f8b8e 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -278,6 +278,75 @@ static int vhci_setup(struct hci_dev *hdev) return 0; } +static void vhci_coredump(struct hci_dev *hdev) +{ + /* No need to do anything */ +} + +static int vhci_coredump_hdr(struct hci_dev *hdev, char *buf, size_t size) +{ + char *ptr = buf; + size_t rem = size; + size_t read = 0; + + read = snprintf(ptr, rem, "Controller Name: vhci_ctrl\n"); + rem -= read; + ptr += read; + + read = snprintf(ptr, rem, "Firmware Version: vhci_fw\n"); + rem -= read; + ptr += read; + + read = snprintf(ptr, rem, "Driver: vhci_drv\n"); + rem -= read; + ptr += read; + + read = snprintf(ptr, rem, "Vendor: vhci\n"); + rem -= read; + ptr += read; + + return size - rem; +} + +static ssize_t force_devcoredump_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct vhci_data *data = file->private_data; + struct hci_dev *hdev = data->hdev; + struct sk_buff *skb = NULL; + char buf[512]; + int ret; + + ret = simple_write_to_buffer(&buf, sizeof(buf), ppos, user_buf, count); + if (ret < count) + return ret; + + skb = alloc_skb(count, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + skb_put_data(skb, &buf, count); + + hci_devcoredump_register(hdev, vhci_coredump, vhci_coredump_hdr, NULL); + + ret = hci_devcoredump_init(hdev, skb->len); + if (ret) { + BT_ERR("Failed to generate devcoredump"); + kfree_skb(skb); + return ret; + } + + hci_devcoredump_append(hdev, skb); + hci_devcoredump_complete(hdev); + + return count; +} + +static const struct file_operations force_devcoredump_fops = { + .open = simple_open, + .write = force_devcoredump_write, +}; + static int __vhci_create_device(struct vhci_data *data, __u8 opcode) { struct hci_dev *hdev; @@ -355,6 +424,9 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode) debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data, &aosp_capable_fops); + debugfs_create_file("force_devcoredump", 0644, hdev->debugfs, data, + &force_devcoredump_fops); + hci_skb_pkt_type(skb) = HCI_VENDOR_PKT; skb_put_u8(skb, 0xff); From patchwork Thu Mar 2 23:15:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manish Mandlik X-Patchwork-Id: 13158031 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EF9CFC7EE30 for ; Thu, 2 Mar 2023 23:16:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229771AbjCBXQA (ORCPT ); Thu, 2 Mar 2023 18:16:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57646 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229759AbjCBXP6 (ORCPT ); Thu, 2 Mar 2023 18:15:58 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6CD47580F6 for ; Thu, 2 Mar 2023 15:15:57 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-538116920c3so5639147b3.15 for ; Thu, 02 Mar 2023 15:15:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=rvS2IryHoEggHxb40R2qDr1c4jdX8M14m8UBo1w5O+A=; b=rTFlTa1B49TRn6fe2xljQwwVEglx8YFV8C9kde50fVDdWxEpUXz5X2LcnSFs/UO7Vi kZgleyqOxc1bNDl72lVTIPpczx/mQ4A2BkY35xMQhVeEbnZyFDIPf0YOt2uL7r08b3oa FNzJYcekYRd/AchigEWUvf/zsLzg6Gxwkqdt7NeYvchhSsGvCVDwG+EC1JP1OQ39mzHO +BtqViZKxhu80U0k0ZV5ZldhBuFF7gmXP5NxU6wQO28tVsogLiGKjQui1raD1W1kex4o XgxpzBfnkJ/HQkv8lWMTnMW4tu5Wuf0ybu8WH1Szihti8FXqC5cv92ed9D5iL6JYcjG2 zGwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=rvS2IryHoEggHxb40R2qDr1c4jdX8M14m8UBo1w5O+A=; b=Q2HiOq8rGIfYaRlGEOhUBNxrjfAj837aMHVsxEoCY4mzr8ZT7K9dsAm7mdFjkSgBff tYMb+B8UEnVmsqdxIm+aSe4Io8xJp2doz1oFr2xznSfZGTmLRnkKb33nwq3nP0WOIgN3 gwtU8hYWJb4FlZmdtQPx+3pQifmuN2u7tiy/eJkfXgfwFfEFSgt7JgB+K24uRNX3R7Hh 772hmxv261MaHpFVZvVgg4tQYJj3pizNrxniwastZLPm1qsGgIzVg2tteZ4xX9/afjy3 dXb5I07puepHtZcnSdNiPSqspIiO9InIO0Ykyloal8YHExUlhvwKJRS0mtywApqZoj60 ygIw== X-Gm-Message-State: AO0yUKUHWZcQTvkeyHqdDOG2vxdhjHSpwm8anwEhlR8sUIlA2GPKv+oT RadxdpDxcwuBrJXwi88FKn4bHm5VYb6hvg== X-Google-Smtp-Source: AK7set9dkRDI7rl+jcwdvMWgEqjajs9PD52kgK8a3qJzljVImQo6pHwhL5/Wy6RdERAUxBUQOrMvAfr0+nGNvw== X-Received: from mmandlik-cloudtop.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:2893]) (user=mmandlik job=sendgmr) by 2002:a81:af53:0:b0:53c:6fda:835f with SMTP id x19-20020a81af53000000b0053c6fda835fmr4363808ywj.0.1677798956703; Thu, 02 Mar 2023 15:15:56 -0800 (PST) Date: Thu, 2 Mar 2023 15:15:46 -0800 In-Reply-To: <20230302151413.v7.1.I9b4e4818bab450657b19cda3497d363c9baa616e@changeid> Mime-Version: 1.0 References: <20230302151413.v7.1.I9b4e4818bab450657b19cda3497d363c9baa616e@changeid> X-Mailer: git-send-email 2.40.0.rc0.216.gc4246ad0f0-goog Message-ID: <20230302151413.v7.3.I8cd97c192e8268567b6f467ccd993ec71897318e@changeid> Subject: [PATCH v7 3/4] Bluetooth: btusb: Add btusb devcoredump support From: Manish Mandlik To: marcel@holtmann.org, luiz.dentz@gmail.com Cc: chromeos-bluetooth-upstreaming@chromium.org, linux-bluetooth@vger.kernel.org, Manish Mandlik , Abhishek Pandit-Subedi , Johan Hedberg , linux-kernel@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org This patch implements the btusb driver side .coredump() callback to trigger a devcoredump via sysfs. Signed-off-by: Manish Mandlik Reviewed-by: Abhishek Pandit-Subedi --- (no changes since v6) Changes in v6: - Remove dev->coredump_disabled check since the sysfs flag related change has been abandoned Changes in v4: - New patch in the series drivers/bluetooth/btusb.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 4ca91c033d2f..9c9f7bf1375a 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4385,6 +4385,17 @@ static int btusb_resume(struct usb_interface *intf) } #endif +#ifdef CONFIG_DEV_COREDUMP +static void btusb_coredump(struct device *dev) +{ + struct btusb_data *data = dev_get_drvdata(dev); + struct hci_dev *hdev = data->hdev; + + if (hdev->dump.coredump) + hdev->dump.coredump(hdev); +} +#endif + static struct usb_driver btusb_driver = { .name = "btusb", .probe = btusb_probe, @@ -4396,6 +4407,14 @@ static struct usb_driver btusb_driver = { .id_table = btusb_table, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, + +#ifdef CONFIG_DEV_COREDUMP + .drvwrap = { + .driver = { + .coredump = btusb_coredump, + }, + }, +#endif }; module_usb_driver(btusb_driver); From patchwork Thu Mar 2 23:15:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manish Mandlik X-Patchwork-Id: 13158032 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E456AC678D4 for ; Thu, 2 Mar 2023 23:16:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229854AbjCBXQL (ORCPT ); Thu, 2 Mar 2023 18:16:11 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57726 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229789AbjCBXQA (ORCPT ); Thu, 2 Mar 2023 18:16:00 -0500 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6A134584B6 for ; Thu, 2 Mar 2023 15:15:59 -0800 (PST) Received: by mail-pl1-x649.google.com with SMTP id z1-20020a170902d54100b00198bc9ba4edso387894plf.21 for ; Thu, 02 Mar 2023 15:15:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=TW3tCdu1PCumKULwi1Tlb3Y38EM+c6pzOcsgOT9nE0I=; b=PihfOpr1dEQXFBvw3y/yNxRVILkUps7HM0ENpvLZ5KfWFbOd2TnJJHDJhrBdYsqI26 H5u3SvO2W+lPX7HuRWVM702Oo3GPG+EYOHY2s1Hg6E7wH0DOfodiGKlSri/fSIDGtXci Gq7r+bqWooZ+knpigSyeMxZHdnzZ4Ut1jjFcX/D+v0sMbPFocUb4t3Y5Oz4CRWcfSgqf gEmYYnXLtE5mMMldfLfCdfRsUZDDQE/DpbNjsXV7s+YxJRTXfnafHmyYuJUnuTGVdXb7 V2puYUghx2Kzj+k5jxlS85lpKMkZRKA4T/6V011Q8cvFypK/f1Q7yPz0SFG9xbhxnYUv 9DRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=TW3tCdu1PCumKULwi1Tlb3Y38EM+c6pzOcsgOT9nE0I=; b=ljMeB7NAo6G/tAfz6fdZi+dK/eBtv1idCPwMplCDVMQjVVvj8I0MlVvdxxjhaOyrHM Yn6zhLQh7aNT4pEh/N2IOyF79TxY1LwjjbVzXAoFqQCRxmSi37QiKsaC27mO723KomNh gs1NXKilOGNmVun5e+yiYWWdp6MPTFwnjXAH5wwQUJvE8Ro7u2/NEqijRT31oDjQJKvq ZzcFYsQy210oev8wyaLagA2ZIQ0hgX80ZFg6lJngk8Y96rIcsfAGbGzYIYCtmcMY2/9N p1OHg0dE6Wiq7j9+J79W0cuSdapEu0D6IW953XRcsLq8ehqrylM8rrXlo9UKnrwWxVTo 5uIQ== X-Gm-Message-State: AO0yUKWUVv/eNgIr7TyAUJLc+HcTMZquLx1kHIMHVO60z1vE3wHCi75R G7A/W34YBE4rNF9/u1Kz726jLTGZB/LnWg== X-Google-Smtp-Source: AK7set/IiGZ+6zvuv2NA/KY9f04kzkGQDU69NL3pB7CBNEiojuCclzM+DjmI5T2NV2Xh2wv8cMakIQSPazozzw== X-Received: from mmandlik-cloudtop.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:2893]) (user=mmandlik job=sendgmr) by 2002:a63:9c0a:0:b0:503:913f:77b9 with SMTP id f10-20020a639c0a000000b00503913f77b9mr3002861pge.6.1677798958779; Thu, 02 Mar 2023 15:15:58 -0800 (PST) Date: Thu, 2 Mar 2023 15:15:47 -0800 In-Reply-To: <20230302151413.v7.1.I9b4e4818bab450657b19cda3497d363c9baa616e@changeid> Mime-Version: 1.0 References: <20230302151413.v7.1.I9b4e4818bab450657b19cda3497d363c9baa616e@changeid> X-Mailer: git-send-email 2.40.0.rc0.216.gc4246ad0f0-goog Message-ID: <20230302151413.v7.4.I8ac78c64382c66cdb49076a2053ef9c44ebf1ea3@changeid> Subject: [PATCH v7 4/4] Bluetooth: btintel: Add Intel devcoredump support From: Manish Mandlik To: marcel@holtmann.org, luiz.dentz@gmail.com Cc: chromeos-bluetooth-upstreaming@chromium.org, linux-bluetooth@vger.kernel.org, Abhishek Pandit-Subedi , Manish Mandlik , Chethan Tumkur Narayan , Johan Hedberg , linux-kernel@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Abhishek Pandit-Subedi Intercept debug exception events from the controller and put them into a devcoredump using hci devcoredump APIs. The debug exception contains data in a TLV format and it will be parsed in userspace. Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Manish Mandlik Reviewed-by: Abhishek Pandit-Subedi Reviewed-by: Chethan Tumkur Narayan --- Changes in v7: - Update btintel_coredump() and btusb_intel_diagnostics() Changes in v6: - Implement btintel_coredump() Changes in v4: - Add btintel_coredump() placeholder Changes in v2: - Create a local struct to store coredump_info in btintel.c - Call btintel_register_devcoredump_support() from btintel.c - Fix strncpy() destination bound warning drivers/bluetooth/btintel.c | 81 ++++++++++++++++++++++++++++++++++++- drivers/bluetooth/btintel.h | 12 +++++- drivers/bluetooth/btusb.c | 54 +++++++++++++++++++++---- 3 files changed, 137 insertions(+), 10 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index bede8b005594..3f0bc3ea4af7 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -36,6 +36,13 @@ struct cmd_write_boot_params { u8 fw_build_yy; } __packed; +#define DRIVER_NAME_LEN 16 +static struct { + char driver_name[DRIVER_NAME_LEN]; + u8 hw_variant; + u32 fw_build_num; +} coredump_info; + int btintel_check_bdaddr(struct hci_dev *hdev) { struct hci_rp_read_bd_addr *bda; @@ -308,6 +315,9 @@ int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver) return -EINVAL; } + coredump_info.hw_variant = ver->hw_variant; + coredump_info.fw_build_num = ver->fw_build_num; + bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u", variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f, ver->fw_build_num, ver->fw_build_ww, @@ -502,6 +512,9 @@ static int btintel_version_info_tlv(struct hci_dev *hdev, return -EINVAL; } + coredump_info.hw_variant = INTEL_HW_VARIANT(version->cnvi_bt); + coredump_info.fw_build_num = version->build_num; + bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant, 2000 + (version->timestamp >> 8), version->timestamp & 0xff, version->build_type, version->build_num); @@ -1453,6 +1466,67 @@ int btintel_set_quality_report(struct hci_dev *hdev, bool enable) } EXPORT_SYMBOL_GPL(btintel_set_quality_report); +static void btintel_coredump(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc4e, 0, NULL, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Coredump failed (%ld)", PTR_ERR(skb)); + return; + } + + kfree_skb(skb); +} + +static int btintel_dmp_hdr(struct hci_dev *hdev, char *buf, size_t size) +{ + char *ptr = buf; + size_t rem = size; + size_t read = 0; + + read = snprintf(ptr, rem, "Controller Name: 0x%X\n", + coredump_info.hw_variant); + rem -= read; + ptr += read; + + read = snprintf(ptr, rem, "Firmware Version: 0x%X\n", + coredump_info.fw_build_num); + rem -= read; + ptr += read; + + read = snprintf(ptr, rem, "Driver: %s\n", coredump_info.driver_name); + rem -= read; + ptr += read; + + read = snprintf(ptr, rem, "Vendor: Intel\n"); + rem -= read; + ptr += read; + + return size - rem; +} + +static int btintel_register_devcoredump_support(struct hci_dev *hdev) +{ + struct intel_debug_features features; + int err; + + err = btintel_read_debug_features(hdev, &features); + if (err) { + bt_dev_info(hdev, "Error reading debug features"); + return err; + } + + if (!(features.page1[0] & 0x3f)) { + bt_dev_info(hdev, "Telemetry exception format not supported"); + return -EOPNOTSUPP; + } + + hci_devcoredump_register(hdev, btintel_coredump, btintel_dmp_hdr, NULL); + + return err; +} + static const struct firmware *btintel_legacy_rom_get_fw(struct hci_dev *hdev, struct intel_version *ver) { @@ -2582,6 +2656,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) btintel_set_msft_opcode(hdev, ver.hw_variant); err = btintel_bootloader_setup(hdev, &ver); + btintel_register_devcoredump_support(hdev); break; default: bt_dev_err(hdev, "Unsupported Intel hw variant (%u)", @@ -2655,6 +2730,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) btintel_set_msft_opcode(hdev, ver.hw_variant); err = btintel_bootloader_setup(hdev, &ver); + btintel_register_devcoredump_support(hdev); break; case 0x17: case 0x18: @@ -2678,6 +2754,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) INTEL_HW_VARIANT(ver_tlv.cnvi_bt)); err = btintel_bootloader_setup_tlv(hdev, &ver_tlv); + btintel_register_devcoredump_support(hdev); break; default: bt_dev_err(hdev, "Unsupported Intel hw variant (%u)", @@ -2727,7 +2804,7 @@ static int btintel_shutdown_combined(struct hci_dev *hdev) return 0; } -int btintel_configure_setup(struct hci_dev *hdev) +int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name) { hdev->manufacturer = 2; hdev->setup = btintel_setup_combined; @@ -2736,6 +2813,8 @@ int btintel_configure_setup(struct hci_dev *hdev) hdev->set_diag = btintel_set_diag_combined; hdev->set_bdaddr = btintel_set_bdaddr; + strncpy(coredump_info.driver_name, driver_name, DRIVER_NAME_LEN - 1); + return 0; } EXPORT_SYMBOL_GPL(btintel_configure_setup); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 8e7da877efae..c34553fef3b0 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -150,6 +150,13 @@ struct btintel_loc_aware_reg { __le32 delta; } __packed; +#define INTEL_TLV_TYPE_ID 0x01 + +#define INTEL_TLV_SYSTEM_EXCEPTION 0x00 +#define INTEL_TLV_FATAL_EXCEPTION 0x01 +#define INTEL_TLV_DEBUG_EXCEPTION 0x02 +#define INTEL_TLV_TEST_EXCEPTION 0xDE + #define INTEL_HW_PLATFORM(cnvx_bt) ((u8)(((cnvx_bt) & 0x0000ff00) >> 8)) #define INTEL_HW_VARIANT(cnvx_bt) ((u8)(((cnvx_bt) & 0x003f0000) >> 16)) #define INTEL_CNVX_TOP_TYPE(cnvx_top) ((cnvx_top) & 0x00000fff) @@ -219,7 +226,7 @@ int btintel_read_boot_params(struct hci_dev *hdev, struct intel_boot_params *params); int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver, const struct firmware *fw, u32 *boot_param); -int btintel_configure_setup(struct hci_dev *hdev); +int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name); void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len); void btintel_secure_send_result(struct hci_dev *hdev, const void *ptr, unsigned int len); @@ -300,7 +307,8 @@ static inline int btintel_download_firmware(struct hci_dev *dev, return -EOPNOTSUPP; } -static inline int btintel_configure_setup(struct hci_dev *hdev) +static inline int btintel_configure_setup(struct hci_dev *hdev, + const char *driver_name) { return -ENODEV; } diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9c9f7bf1375a..4f7aa32696f4 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2376,16 +2376,47 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer, return btusb_recv_bulk(data, buffer, count); } +static int btusb_intel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct intel_tlv *tlv = (void *)&skb->data[5]; + + /* The first event is always an event type TLV */ + if (tlv->type != INTEL_TLV_TYPE_ID) + goto recv_frame; + + switch (tlv->val[0]) { + case INTEL_TLV_SYSTEM_EXCEPTION: + case INTEL_TLV_FATAL_EXCEPTION: + case INTEL_TLV_DEBUG_EXCEPTION: + case INTEL_TLV_TEST_EXCEPTION: + /* Generate devcoredump from exception */ + if (!hci_devcoredump_init(hdev, skb->len)) { + hci_devcoredump_append(hdev, skb); + hci_devcoredump_complete(hdev); + } else { + bt_dev_err(hdev, "Failed to generate devcoredump"); + kfree_skb(skb); + } + return 0; + default: + bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]); + } + +recv_frame: + return hci_recv_frame(hdev, skb); +} + static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb) { - if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) { - struct hci_event_hdr *hdr = (void *)skb->data; + struct hci_event_hdr *hdr = (void *)skb->data; + const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 }; - if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && - hdr->plen > 0) { - const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; - unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; + if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && + hdr->plen > 0) { + const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; + unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; + if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) { switch (skb->data[2]) { case 0x02: /* When switching to the operational firmware @@ -2404,6 +2435,15 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb) break; } } + + /* Handle all diagnostics events separately. May still call + * hci_recv_frame. + */ + if (len >= sizeof(diagnostics_hdr) && + memcmp(&skb->data[2], diagnostics_hdr, + sizeof(diagnostics_hdr)) == 0) { + return btusb_intel_diagnostics(hdev, skb); + } } return hci_recv_frame(hdev, skb); @@ -4008,7 +4048,7 @@ static int btusb_probe(struct usb_interface *intf, /* Combined Intel Device setup to support multiple setup routine */ if (id->driver_info & BTUSB_INTEL_COMBINED) { - err = btintel_configure_setup(hdev); + err = btintel_configure_setup(hdev, btusb_driver.name); if (err) goto out_free_dev;