Re: [PATCH 6.6.y] Bluetooth: HCI: Set extended advertising data synchronously

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Christian,

On Tue, Jul 8, 2025 at 11:39 AM Greg KH <gregkh@xxxxxxxxxxxxxxxxxxx> wrote:
>
> On Mon, Jul 07, 2025 at 10:13:07AM +0200, Christian Eggers wrote:
> > Upstream commit 89fb8acc38852116d38d721ad394aad7f2871670
> >
> > Currently, for controllers with extended advertising, the advertising
> > data is set in the asynchronous response handler for extended
> > adverstising params. As most advertising settings are performed in a
> > synchronous context, the (asynchronous) setting of the advertising data
> > is done too late (after enabling the advertising).
> >
> > Move setting of adverstising data from asynchronous response handler
> > into synchronous context to fix ordering of HCI commands.
> >
> > Signed-off-by: Christian Eggers <ceggers@xxxxxxx>
> > Fixes: a0fb3726ba55 ("Bluetooth: Use Set ext adv/scan rsp data if controller supports")
> > Cc: stable@xxxxxxxxxxxxxxx
> > v2: https://lore.kernel.org/linux-bluetooth/20250626115209.17839-1-ceggers@xxxxxxx/
> > Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx>
> > ---
> >  net/bluetooth/hci_event.c |  36 -------
> >  net/bluetooth/hci_sync.c  | 213 ++++++++++++++++++++++++--------------
> >  2 files changed, 133 insertions(+), 116 deletions(-)
> >
> > diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> > index 008d14b3d8b8..147766458a6c 100644
> > --- a/net/bluetooth/hci_event.c
> > +++ b/net/bluetooth/hci_event.c
> > @@ -2139,40 +2139,6 @@ static u8 hci_cc_set_adv_param(struct hci_dev *hdev, void *data,
> >       return rp->status;
> >  }
> >
> > -static u8 hci_cc_set_ext_adv_param(struct hci_dev *hdev, void *data,
> > -                                struct sk_buff *skb)
> > -{
> > -     struct hci_rp_le_set_ext_adv_params *rp = data;
> > -     struct hci_cp_le_set_ext_adv_params *cp;
> > -     struct adv_info *adv_instance;
> > -
> > -     bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
> > -
> > -     if (rp->status)
> > -             return rp->status;
> > -
> > -     cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS);
> > -     if (!cp)
> > -             return rp->status;
> > -
> > -     hci_dev_lock(hdev);
> > -     hdev->adv_addr_type = cp->own_addr_type;
> > -     if (!cp->handle) {
> > -             /* Store in hdev for instance 0 */
> > -             hdev->adv_tx_power = rp->tx_power;
> > -     } else {
> > -             adv_instance = hci_find_adv_instance(hdev, cp->handle);
> > -             if (adv_instance)
> > -                     adv_instance->tx_power = rp->tx_power;
> > -     }
> > -     /* Update adv data as tx power is known now */
> > -     hci_update_adv_data(hdev, cp->handle);
> > -
> > -     hci_dev_unlock(hdev);
> > -
> > -     return rp->status;
> > -}
> > -
> >  static u8 hci_cc_read_rssi(struct hci_dev *hdev, void *data,
> >                          struct sk_buff *skb)
> >  {
> > @@ -4153,8 +4119,6 @@ static const struct hci_cc {
> >       HCI_CC(HCI_OP_LE_READ_NUM_SUPPORTED_ADV_SETS,
> >              hci_cc_le_read_num_adv_sets,
> >              sizeof(struct hci_rp_le_read_num_supported_adv_sets)),
> > -     HCI_CC(HCI_OP_LE_SET_EXT_ADV_PARAMS, hci_cc_set_ext_adv_param,
> > -            sizeof(struct hci_rp_le_set_ext_adv_params)),
> >       HCI_CC_STATUS(HCI_OP_LE_SET_EXT_ADV_ENABLE,
> >                     hci_cc_le_set_ext_adv_enable),
> >       HCI_CC_STATUS(HCI_OP_LE_SET_ADV_SET_RAND_ADDR,
> > diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> > index e92bc4ceb5ad..7b6c8f53e334 100644
> > --- a/net/bluetooth/hci_sync.c
> > +++ b/net/bluetooth/hci_sync.c
> > @@ -1224,9 +1224,129 @@ static int hci_set_adv_set_random_addr_sync(struct hci_dev *hdev, u8 instance,
> >                                    sizeof(cp), &cp, HCI_CMD_TIMEOUT);
> >  }
> >
> > +static int
> > +hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,
> > +                         const struct hci_cp_le_set_ext_adv_params *cp,
> > +                         struct hci_rp_le_set_ext_adv_params *rp)
> > +{
> > +     struct sk_buff *skb;
> > +
> > +     skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
> > +                          cp, HCI_CMD_TIMEOUT);
> > +
> > +     /* If command return a status event, skb will be set to -ENODATA */
> > +     if (skb == ERR_PTR(-ENODATA))
> > +             return 0;
> > +
> > +     if (IS_ERR(skb)) {
> > +             bt_dev_err(hdev, "Opcode 0x%4.4x failed: %ld",
> > +                        HCI_OP_LE_SET_EXT_ADV_PARAMS, PTR_ERR(skb));
> > +             return PTR_ERR(skb);
> > +     }
> > +
> > +     if (skb->len != sizeof(*rp)) {
> > +             bt_dev_err(hdev, "Invalid response length for 0x%4.4x: %u",
> > +                        HCI_OP_LE_SET_EXT_ADV_PARAMS, skb->len);
> > +             kfree_skb(skb);
> > +             return -EIO;
> > +     }
> > +
> > +     memcpy(rp, skb->data, sizeof(*rp));
> > +     kfree_skb(skb);
> > +
> > +     if (!rp->status) {
> > +             hdev->adv_addr_type = cp->own_addr_type;
> > +             if (!cp->handle) {
> > +                     /* Store in hdev for instance 0 */
> > +                     hdev->adv_tx_power = rp->tx_power;
> > +             } else if (adv) {
> > +                     adv->tx_power = rp->tx_power;
> > +             }
> > +     }
> > +
> > +     return rp->status;
> > +}
> > +
> > +static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
> > +{
> > +     struct {
> > +             struct hci_cp_le_set_ext_adv_data cp;
> > +             u8 data[HCI_MAX_EXT_AD_LENGTH];
> > +     } pdu;
> > +     u8 len;
> > +     struct adv_info *adv = NULL;
> > +     int err;
> > +
> > +     memset(&pdu, 0, sizeof(pdu));
> > +
> > +     if (instance) {
> > +             adv = hci_find_adv_instance(hdev, instance);
> > +             if (!adv || !adv->adv_data_changed)
> > +                     return 0;
> > +     }
> > +
> > +     len = eir_create_adv_data(hdev, instance, pdu.data);
> > +
> > +     pdu.cp.length = len;
> > +     pdu.cp.handle = instance;
> > +     pdu.cp.operation = LE_SET_ADV_DATA_OP_COMPLETE;
> > +     pdu.cp.frag_pref = LE_SET_ADV_DATA_NO_FRAG;
> > +
> > +     err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
> > +                                 sizeof(pdu.cp) + len, &pdu.cp,
> > +                                 HCI_CMD_TIMEOUT);
> > +     if (err)
> > +             return err;
> > +
> > +     /* Update data if the command succeed */
> > +     if (adv) {
> > +             adv->adv_data_changed = false;
> > +     } else {
> > +             memcpy(hdev->adv_data, pdu.data, len);
> > +             hdev->adv_data_len = len;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int hci_set_adv_data_sync(struct hci_dev *hdev, u8 instance)
> > +{
> > +     struct hci_cp_le_set_adv_data cp;
> > +     u8 len;
> > +
> > +     memset(&cp, 0, sizeof(cp));
> > +
> > +     len = eir_create_adv_data(hdev, instance, cp.data);
> > +
> > +     /* There's nothing to do if the data hasn't changed */
> > +     if (hdev->adv_data_len == len &&
> > +         memcmp(cp.data, hdev->adv_data, len) == 0)
> > +             return 0;
> > +
> > +     memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
> > +     hdev->adv_data_len = len;
> > +
> > +     cp.length = len;
> > +
> > +     return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_DATA,
> > +                                  sizeof(cp), &cp, HCI_CMD_TIMEOUT);
> > +}
> > +
> > +int hci_update_adv_data_sync(struct hci_dev *hdev, u8 instance)
> > +{
> > +     if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
> > +             return 0;
> > +
> > +     if (ext_adv_capable(hdev))
> > +             return hci_set_ext_adv_data_sync(hdev, instance);
> > +
> > +     return hci_set_adv_data_sync(hdev, instance);
> > +}
> > +
> >  int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
> >  {
> >       struct hci_cp_le_set_ext_adv_params cp;
> > +     struct hci_rp_le_set_ext_adv_params rp;
> >       bool connectable;
> >       u32 flags;
> >       bdaddr_t random_addr;
> > @@ -1333,8 +1453,12 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
> >               cp.secondary_phy = HCI_ADV_PHY_1M;
> >       }
> >
> > -     err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS,
> > -                                 sizeof(cp), &cp, HCI_CMD_TIMEOUT);
> > +     err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp);
> > +     if (err)
> > +             return err;
> > +
> > +     /* Update adv data as tx power is known now */
> > +     err = hci_set_ext_adv_data_sync(hdev, cp.handle);
> >       if (err)
> >               return err;
> >
> > @@ -1859,82 +1983,6 @@ int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason)
> >                                    sizeof(cp), &cp, HCI_CMD_TIMEOUT);
> >  }
> >
> > -static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
> > -{
> > -     struct {
> > -             struct hci_cp_le_set_ext_adv_data cp;
> > -             u8 data[HCI_MAX_EXT_AD_LENGTH];
> > -     } pdu;
> > -     u8 len;
> > -     struct adv_info *adv = NULL;
> > -     int err;
> > -
> > -     memset(&pdu, 0, sizeof(pdu));
> > -
> > -     if (instance) {
> > -             adv = hci_find_adv_instance(hdev, instance);
> > -             if (!adv || !adv->adv_data_changed)
> > -                     return 0;
> > -     }
> > -
> > -     len = eir_create_adv_data(hdev, instance, pdu.data);
> > -
> > -     pdu.cp.length = len;
> > -     pdu.cp.handle = instance;
> > -     pdu.cp.operation = LE_SET_ADV_DATA_OP_COMPLETE;
> > -     pdu.cp.frag_pref = LE_SET_ADV_DATA_NO_FRAG;
> > -
> > -     err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
> > -                                 sizeof(pdu.cp) + len, &pdu.cp,
> > -                                 HCI_CMD_TIMEOUT);
> > -     if (err)
> > -             return err;
> > -
> > -     /* Update data if the command succeed */
> > -     if (adv) {
> > -             adv->adv_data_changed = false;
> > -     } else {
> > -             memcpy(hdev->adv_data, pdu.data, len);
> > -             hdev->adv_data_len = len;
> > -     }
> > -
> > -     return 0;
> > -}
> > -
> > -static int hci_set_adv_data_sync(struct hci_dev *hdev, u8 instance)
> > -{
> > -     struct hci_cp_le_set_adv_data cp;
> > -     u8 len;
> > -
> > -     memset(&cp, 0, sizeof(cp));
> > -
> > -     len = eir_create_adv_data(hdev, instance, cp.data);
> > -
> > -     /* There's nothing to do if the data hasn't changed */
> > -     if (hdev->adv_data_len == len &&
> > -         memcmp(cp.data, hdev->adv_data, len) == 0)
> > -             return 0;
> > -
> > -     memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
> > -     hdev->adv_data_len = len;
> > -
> > -     cp.length = len;
> > -
> > -     return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_DATA,
> > -                                  sizeof(cp), &cp, HCI_CMD_TIMEOUT);
> > -}
> > -
> > -int hci_update_adv_data_sync(struct hci_dev *hdev, u8 instance)
> > -{
> > -     if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
> > -             return 0;
> > -
> > -     if (ext_adv_capable(hdev))
> > -             return hci_set_ext_adv_data_sync(hdev, instance);
> > -
> > -     return hci_set_adv_data_sync(hdev, instance);
> > -}
> > -
> >  int hci_schedule_adv_instance_sync(struct hci_dev *hdev, u8 instance,
> >                                  bool force)
> >  {
> > @@ -6257,6 +6305,7 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev,
> >                                               struct hci_conn *conn)
> >  {
> >       struct hci_cp_le_set_ext_adv_params cp;
> > +     struct hci_rp_le_set_ext_adv_params rp;
> >       int err;
> >       bdaddr_t random_addr;
> >       u8 own_addr_type;
> > @@ -6298,8 +6347,12 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev,
> >       if (err)
> >               return err;
> >
> > -     err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS,
> > -                                 sizeof(cp), &cp, HCI_CMD_TIMEOUT);
> > +     err = hci_set_ext_adv_params_sync(hdev, NULL, &cp, &rp);
> > +     if (err)
> > +             return err;
> > +
> > +     /* Update adv data as tx power is known now */
> > +     err = hci_set_ext_adv_data_sync(hdev, cp.handle);
> >       if (err)
> >               return err;
> >
> > --
> > [Resend, upstream commit id was missing]
> >
> > Hi Greg,
> >
> > I've backported this patch for 6.6. There were some trivial merge
> > conflicts due to moved coded sections.
> >
> > Please try it also for older trees. If I get any FAILED notices,
> > I'll try to prepare patches for specific trees.
>
> You made major changes here from the upstream version, PLEASE document
> them properly in the changelog.  Also, can you test it to verify that it
> works and doesn't blow up the stack like I'm guessing it might?

@Christian Eggers try running resulting image with mgmt-tester and the
following tests:

Add Ext Advertising - Invalid Params 1 (Multiple Phys)
Add Ext Advertising - Invalid Params 2 (Multiple PHYs)
Add Ext Advertising - Invalid Params 3 (Multiple PHYs)
Add Ext Advertising - Invalid Params 4 (Multiple PHYs)
Add Ext Advertising - Success 1 (Powered, Add Adv Inst)
Add Ext Advertising - Success 2 (!Powered, Add Adv Inst)
Add Ext Advertising - Success 3 (!Powered, Adv Enable)
Add Ext Advertising - Success 4 (Set Adv on override)
Add Ext Advertising - Success 5 (Set Adv off override)
Add Ext Advertising - Success 6 (Scan Rsp Dta, Adv ok)
Add Ext Advertising - Success 7 (Scan Rsp Dta, Scan ok)
Add Ext Advertising - Success 8 (Connectable Flag)
Add Ext Advertising - Success 9 (General Discov Flag)
Add Ext Advertising - Success 10 (Limited Discov Flag)
Add Ext Advertising - Success 11 (Managed Flags)
Add Ext Advertising - Success 12 (TX Power Flag)
Add Ext Advertising - Success 13 (ADV_SCAN_IND)
Add Ext Advertising - Success 14 (ADV_NONCONN_IND)
Add Ext Advertising - Success 15 (ADV_IND)
Add Ext Advertising - Success 16 (Connectable -> on)
Add Ext Advertising - Success 17 (Connectable -> off)
Add Ext Advertising - Success 20 (Add Adv override)
Add Ext Advertising - Success 21 (Timeout expires)
Add Ext Advertising - Success 22 (LE -> off, Remove)
Add Ext Advertising - Success (Empty ScRsp)
Add Ext Advertising - Success (ScRsp only)
Add Ext Advertising - Invalid Params (ScRsp too long)
Add Ext Advertising - Success (ScRsp appear)
Add Ext Advertising - Invalid Params (ScRsp appear long)
Add Ext Advertising - Success (Appear is null)
Add Ext Advertising - Success (Name is null)
Add Ext Advertising - Success (Complete name)
Add Ext Advertising - Success (Shortened name)
Add Ext Advertising - Success (Short name)
Add Ext Advertising - Success (Name + data)
Add Ext Advertising - Invalid Params (Name + data)
Add Ext Advertising - Success (Name+data+appear)
Add Ext Advertising - Success (PHY -> 1M)
Add Ext Advertising - Success (PHY -> 2M)
Add Ext Advertising - Success (PHY -> Coded)
Add Ext Advertising - Success (Ext Pdu Scannable)
Add Ext Advertising - Success (Ext Pdu Connectable)
Add Ext Advertising - Success (Ext Pdu Conn Scan)
Add Ext Advertising - Success (1m Connectable -> on)
Add Ext Advertising - Success (1m Connectable -> off)

This should exercise the code you are changing, I assume they should
all pass with 6.6.y but perhaps you can discard if there were
something not passing since it can be the result of mgmt-tester
changes that are not backward compatible.

> thanks,
>
> greg k-h
>


-- 
Luiz Augusto von Dentz





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux