Re: [PATCH BlueZ] tools/iso-tester: add tests for socket shutdown with linger enabled

Luiz Augusto von Dentz <[email protected]>
Newsgroups org.kernel.vger.linux-bluetooth
Message-ID <CABBYNZLyajk6bry4Wnr5DScz+KMVBQZ5wpOX51AnxYir6h5bYw@mail.gmail.com>
Hi Pauli,

On Tue, Jul 21, 2026 at 5:20 AM Pauli Virtanen <[email protected]> wrote:
>
> Add tests with SO_LINGER enabled, to test kernel sock transitions
> properly to BT_CLOSED on shutdown.
>
> Unlike L2CAP, ISO sockets don't have a feature to send POLLHUP only
> after HCI Disconnect is received, so only SO_LINGER can be used to test
> the sequence is shutdown -> Disconnect, HUP -> release, and not
> shutdown -> HUP -> release -> Disconnect.
>
> Add tests:
>
> ISO Defer Linger - Success
> ISO Connect Linger - Success
> ---
>  tools/iso-tester.c | 68 +++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 67 insertions(+), 1 deletion(-)
>
> diff --git a/tools/iso-tester.c b/tools/iso-tester.c
> index fe27eaaf3..a792a804b 100644
> --- a/tools/iso-tester.c
> +++ b/tools/iso-tester.c
> @@ -478,6 +478,7 @@ struct test_data {
>         bool suspending;
>         struct tx_tstamp_data tx_ts;
>         int seqnum;
> +       GThread *thread;

I don't think this is a good idea, most of our userspace code isn't
thread-safe, so I'd prefer we keep it that way and perhaps handle this
with some async I/O or something else.

>  };
>
>  struct iso_client_data {
> @@ -522,6 +523,9 @@ struct iso_client_data {
>          * Used for testing TX timestamping OPT_ID.
>          */
>         unsigned int repeat_send;
> +
> +       /* Whether to enable SO_LINGER (for test_connect_wait_close) */
> +       bool so_linger;
>  };
>
>  typedef bool (*iso_defer_accept_t)(struct test_data *data, GIOChannel *io,
> @@ -717,6 +721,11 @@ static void test_post_teardown(const void *test_data)
>
>         hciemu_unref(data->hciemu);
>         data->hciemu = NULL;
> +
> +       if (data->thread) {
> +               g_thread_unref(data->thread);
> +               data->thread = NULL;
> +       }
>  }
>
>  static void test_data_free(void *test_data)
> @@ -1352,6 +1361,19 @@ static const struct iso_client_data connect_ac_1_2_cig_1_2 = {
>         .mconn = true,
>  };
>
> +static const struct iso_client_data linger_16_2_1 = {
> +       .qos = QOS_16_2_1,
> +       .expect_err = 0,
> +       .so_linger = true,
> +};
> +
> +static const struct iso_client_data linger_defer_16_2_1 = {
> +       .qos = QOS_16_2_1,
> +       .expect_err = 0,
> +       .defer = true,
> +       .so_linger = true,
> +};
> +
>  static const struct iso_client_data bcast_48_1_g = {
>         .qos = QOS_OUT_48_1_g,
>         .expect_err = 0,
> @@ -3679,6 +3701,19 @@ static void test_connect2_busy(const void *test_data)
>         setup_connect(data, 0, iso_connect_cb_busy);
>  }
>
> +static gpointer shutdown_sk_thread_cb(gpointer data)
> +{
> +       int sk = PTR_TO_UINT(data);
> +       struct timespec start, end;
> +
> +       clock_gettime(CLOCK_MONOTONIC, &start);
> +       shutdown(sk, SHUT_RDWR);
> +       clock_gettime(CLOCK_MONOTONIC, &end);
> +
> +       /* Completed before linger timeout? */
> +       return UINT_TO_PTR(TS_NSEC(&end) - TS_NSEC(&start) < SEC_NSEC(2));
> +}
> +
>  static gboolean iso_connect_close_cb(GIOChannel *io, GIOCondition cond,
>                                                         gpointer user_data)
>  {
> @@ -3688,6 +3723,13 @@ static gboolean iso_connect_close_cb(GIOChannel *io, GIOCondition cond,
>
>         tester_print("Disconnected");
>
> +       if (data->thread) {
> +               /* Wait for shutdown() to complete, for linger enabled */
> +               if (!g_thread_join(data->thread))
> +                       tester_test_failed();
> +               data->thread = NULL;
> +       }
> +
>         --data->step;
>         if (!data->step)
>                 tester_test_passed();
> @@ -3737,16 +3779,34 @@ static gboolean iso_connect_wait_close_cb(GIOChannel *io, GIOCondition cond,
>                                                         gpointer user_data)
>  {
>         struct test_data *data = tester_get_data();
> +       const struct iso_client_data *isodata = data->test_data;
>         int sk;
>
>         tester_print("Connected");
>
>         sk = g_io_channel_unix_get_fd(io);
>
> +       if (isodata->so_linger) {
> +               struct linger val = { .l_onoff = 1, .l_linger = 2 };
> +
> +               if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &val, sizeof(val))) {
> +                       tester_warn("Can't set socket option : %s (%d)",
> +                                                       strerror(errno), errno);
> +                       tester_test_failed();
> +                       return FALSE;
> +               }
> +       }
> +
>         data->io_id[0] = g_io_add_watch(io, G_IO_HUP, iso_connect_close_cb,
>                                                                         data);
>
> -       shutdown(sk, SHUT_RDWR);
> +       if (!isodata->so_linger)
> +               shutdown(sk, SHUT_RDWR);
> +       else {
> +               /* With linger shutdown() blocks, so run in separate thread */
> +               data->thread = g_thread_new("close_sk", shutdown_sk_thread_cb,
> +                                                       UINT_TO_PTR(sk));
> +       }
>
>         return FALSE;
>  }
> @@ -4160,6 +4220,12 @@ int main(int argc, char *argv[])
>         test_iso("ISO Connect Wait Close - Success", &connect_16_2_1,
>                                         setup_powered, test_connect_wait_close);
>
> +       test_iso("ISO Defer Linger - Success", &linger_16_2_1,
> +                                       setup_powered, test_connect_wait_close);
> +
> +       test_iso("ISO Connect Linger - Success", &linger_defer_16_2_1,
> +                                       setup_powered, test_connect_wait_close);
> +
>         test_iso("ISO Connect Suspend - Success", &connect_suspend,
>                                                         setup_powered,
>                                                         test_connect_suspend);
> --
> 2.55.0
>
>


-- 
Luiz Augusto von Dentz
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.