diff mbox series

[v2,5/5] Bluetooth: add BT_NO_ERRQUEUE_POLL socket option

Message ID 134027f3cbaeb7095d080c27cd4b1053d2eb560e.1710440392.git.pav@iki.fi (mailing list archive)
State New, archived
Headers show
Series Bluetooth: add TX timestamping for ISO/SCO/L2CAP | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
tedd_an/CheckPatch success CheckPatch PASS
tedd_an/GitLint success Gitlint PASS
tedd_an/SubjectPrefix success Gitlint PASS
tedd_an/IncrementalBuild success Incremental Build PASS

Commit Message

Pauli Virtanen March 14, 2024, 6:20 p.m. UTC
Add socket option to disable POLLERR on non-empty socket error queue.

Applications can use this for sleeping socket error POLLERR waits, also
when using TX timestamping. This is useful when multiple layers of the
stack are using the same socket.

Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
 include/net/bluetooth/bluetooth.h |  9 +++-
 net/bluetooth/af_bluetooth.c      | 72 ++++++++++++++++++++++++++++++-
 net/bluetooth/iso.c               |  8 ++--
 net/bluetooth/l2cap_sock.c        |  8 ++--
 net/bluetooth/sco.c               |  8 ++--
 5 files changed, 91 insertions(+), 14 deletions(-)

Comments

Luiz Augusto von Dentz March 15, 2024, 3:18 a.m. UTC | #1
Hi Pauli,

On Thu, Mar 14, 2024 at 2:23 PM Pauli Virtanen <pav@iki.fi> wrote:
>
> Add socket option to disable POLLERR on non-empty socket error queue.
>
> Applications can use this for sleeping socket error POLLERR waits, also
> when using TX timestamping. This is useful when multiple layers of the
> stack are using the same socket.

So the idea here is that bluetoothd would set BT_NO_ERRQUEUE_POLL to
not be awakened by timestamps? I wonder why this works on per
fd/socket level while SO_TIMESTAMPING does apply to all sockets
though, or perhaps that is not really related to SO_TIMESTAMPING but
errqueue being the same to all cloned sockets? I'm a little hesitant
to start introducing new socket options if the whole point was to
avoid doing so with the use of SO_TIMESTAMPING.


> Signed-off-by: Pauli Virtanen <pav@iki.fi>
> ---
>  include/net/bluetooth/bluetooth.h |  9 +++-
>  net/bluetooth/af_bluetooth.c      | 72 ++++++++++++++++++++++++++++++-
>  net/bluetooth/iso.c               |  8 ++--
>  net/bluetooth/l2cap_sock.c        |  8 ++--
>  net/bluetooth/sco.c               |  8 ++--
>  5 files changed, 91 insertions(+), 14 deletions(-)
>
> diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
> index 9280e72093ee..dcee5384d715 100644
> --- a/include/net/bluetooth/bluetooth.h
> +++ b/include/net/bluetooth/bluetooth.h
> @@ -240,6 +240,8 @@ struct bt_codecs {
>
>  #define BT_ISO_BASE            20
>
> +#define BT_NO_ERRQUEUE_POLL    21
> +
>  __printf(1, 2)
>  void bt_info(const char *fmt, ...);
>  __printf(1, 2)
> @@ -387,7 +389,8 @@ struct bt_sock {
>  enum {
>         BT_SK_DEFER_SETUP,
>         BT_SK_SUSPEND,
> -       BT_SK_PKT_STATUS
> +       BT_SK_PKT_STATUS,
> +       BT_SK_NO_ERRQUEUE_POLL
>  };
>
>  struct bt_sock_list {
> @@ -410,6 +413,10 @@ int  bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg,
>                             size_t len, int flags);
>  __poll_t bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait);
>  int  bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
> +int bt_sock_setsockopt(struct socket *sock, int level, int optname,
> +                      sockptr_t optval, unsigned int optlen);
> +int bt_sock_getsockopt(struct socket *sock, int level, int optname,
> +                      char __user *optval, int __user *optlen);
>  int  bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
>  int  bt_sock_wait_ready(struct sock *sk, unsigned int msg_flags);
>
> diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
> index 67604ccec2f4..997197aa7b48 100644
> --- a/net/bluetooth/af_bluetooth.c
> +++ b/net/bluetooth/af_bluetooth.c
> @@ -500,6 +500,12 @@ static inline __poll_t bt_accept_poll(struct sock *parent)
>         return 0;
>  }
>
> +static bool bt_sock_error_queue_poll(struct sock *sk)
> +{
> +       return !test_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags) &&
> +               !skb_queue_empty_lockless(&sk->sk_error_queue);
> +}
> +
>  __poll_t bt_sock_poll(struct file *file, struct socket *sock,
>                       poll_table *wait)
>  {
> @@ -511,7 +517,7 @@ __poll_t bt_sock_poll(struct file *file, struct socket *sock,
>         if (sk->sk_state == BT_LISTEN)
>                 return bt_accept_poll(sk);
>
> -       if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
> +       if (sk->sk_err || bt_sock_error_queue_poll(sk))
>                 mask |= EPOLLERR |
>                         (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
>
> @@ -582,6 +588,70 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
>  }
>  EXPORT_SYMBOL(bt_sock_ioctl);
>
> +int bt_sock_setsockopt(struct socket *sock, int level, int optname,
> +                      sockptr_t optval, unsigned int optlen)
> +{
> +       struct sock *sk = sock->sk;
> +       int err = 0;
> +       u32 opt;
> +
> +       if (level != SOL_BLUETOOTH)
> +               return -ENOPROTOOPT;
> +
> +       lock_sock(sk);
> +
> +       switch (optname) {
> +       case BT_NO_ERRQUEUE_POLL:
> +               if (copy_from_sockptr(&opt, optval, sizeof(opt))) {
> +                       err = -EFAULT;
> +                       break;
> +               }
> +
> +               if (opt)
> +                       set_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags);
> +               else
> +                       clear_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags);
> +               break;
> +
> +       default:
> +               err = -ENOPROTOOPT;
> +               break;
> +       }
> +
> +       release_sock(sk);
> +       return err;
> +}
> +EXPORT_SYMBOL(bt_sock_setsockopt);
> +
> +int bt_sock_getsockopt(struct socket *sock, int level, int optname,
> +                      char __user *optval, int __user *optlen)
> +{
> +       struct sock *sk = sock->sk;
> +       int err = 0;
> +       u32 opt;
> +
> +       if (level != SOL_BLUETOOTH)
> +               return -ENOPROTOOPT;
> +
> +       lock_sock(sk);
> +
> +       switch (optname) {
> +       case BT_NO_ERRQUEUE_POLL:
> +               opt = test_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags);
> +               if (put_user(opt, (u32 __user *)optval))
> +                       err = -EFAULT;
> +               break;
> +
> +       default:
> +               err = -ENOPROTOOPT;
> +               break;
> +       }
> +
> +       release_sock(sk);
> +       return err;
> +}
> +EXPORT_SYMBOL(bt_sock_getsockopt);
> +
>  /* This function expects the sk lock to be held when called */
>  int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
>  {
> diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
> index a77ab9df7994..6f5af549a8cc 100644
> --- a/net/bluetooth/iso.c
> +++ b/net/bluetooth/iso.c
> @@ -1596,8 +1596,8 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
>                 break;
>
>         default:
> -               err = -ENOPROTOOPT;
> -               break;
> +               release_sock(sk);
> +               return bt_sock_setsockopt(sock, level, optname, optval, optlen);
>         }
>
>         release_sock(sk);
> @@ -1667,8 +1667,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
>                 break;
>
>         default:
> -               err = -ENOPROTOOPT;
> -               break;
> +               release_sock(sk);
> +               return bt_sock_getsockopt(sock, level, optname, optval, optlen);
>         }
>
>         release_sock(sk);
> diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
> index 9a9f933a748b..06277ce1fd6b 100644
> --- a/net/bluetooth/l2cap_sock.c
> +++ b/net/bluetooth/l2cap_sock.c
> @@ -697,8 +697,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
>                 break;
>
>         default:
> -               err = -ENOPROTOOPT;
> -               break;
> +               release_sock(sk);
> +               return bt_sock_getsockopt(sock, level, optname, optval, optlen);
>         }
>
>         release_sock(sk);
> @@ -1102,8 +1102,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
>                 break;
>
>         default:
> -               err = -ENOPROTOOPT;
> -               break;
> +               release_sock(sk);
> +               return bt_sock_setsockopt(sock, level, optname, optval, optlen);
>         }
>
>         release_sock(sk);
> diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
> index b3c2af7c7d67..b8b1b314aed4 100644
> --- a/net/bluetooth/sco.c
> +++ b/net/bluetooth/sco.c
> @@ -968,8 +968,8 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
>                 break;
>
>         default:
> -               err = -ENOPROTOOPT;
> -               break;
> +               release_sock(sk);
> +               return bt_sock_setsockopt(sock, level, optname, optval, optlen);
>         }
>
>         release_sock(sk);
> @@ -1211,8 +1211,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
>                 break;
>
>         default:
> -               err = -ENOPROTOOPT;
> -               break;
> +               release_sock(sk);
> +               return bt_sock_getsockopt(sock, level, optname, optval, optlen);
>         }
>
>         release_sock(sk);
> --
> 2.44.0
>
>
Pauli Virtanen March 15, 2024, 10:49 a.m. UTC | #2
Hi Luiz,

to, 2024-03-14 kello 23:18 -0400, Luiz Augusto von Dentz kirjoitti:
> Hi Pauli,
> 
> On Thu, Mar 14, 2024 at 2:23 PM Pauli Virtanen <pav@iki.fi> wrote:
> > 
> > Add socket option to disable POLLERR on non-empty socket error queue.
> > 
> > Applications can use this for sleeping socket error POLLERR waits, also
> > when using TX timestamping. This is useful when multiple layers of the
> > stack are using the same socket.
> 
> So the idea here is that bluetoothd would set BT_NO_ERRQUEUE_POLL to
> not be awakened by timestamps? I wonder why this works on per
> fd/socket level while SO_TIMESTAMPING does apply to all sockets
> though, or perhaps that is not really related to SO_TIMESTAMPING but
> errqueue being the same to all cloned sockets? I'm a little hesitant
> to start introducing new socket options if the whole point was to
> avoid doing so with the use of SO_TIMESTAMPING.

It works the same as SO_TIMESTAMPING, setting BT_NO_ERRQUEUE_POLL
applies to all fds referring to the socket, so it is not per fd either.

The idea is that Pipewire sets BT_NO_ERRQUEUE_POLL before enabling
SO_TIMESTAMPING. Then nobody gets POLLERR on errqueue messages.

Instead, it reads MSG_ERRQUEUE always when sending the next packet,
instead of using POLLERR. This works fine in practice and is tested.


I'd appreciate if we can agree on the whole-stack plan.


***

For sockets vs. fds, see net/socket.c:sockfd_lookup

dup() of fd creates new int fd pointing to the same struct file &
struct socket, but not duplicate struct socket/sock, see fs/file.c:1412

Passing fds via unix socket like in DBus seems the same, see
include/net/scm.h:scm_recv_one_fd & fs/file.c:receive_fd

Indeed, you can also see this in debug logs, e.g. 

[53573.676885] iso_sock_getname:1103: sock 0000000018c143a2, sk 000000002aff8d2b  (from BlueZ)
[53580.831753] iso_sock_getname:1103: sock 00000000419df212, sk 00000000526df659  (from BlueZ)
...
[53592.472038] iso_sock_sendmsg:1127: sock 0000000018c143a2, sk 000000002aff8d2b  (from PW)
[53592.472062] iso_sock_sendmsg:1127: sock 00000000419df212, sk 00000000526df659  (from PW)

setsockopt(), errqueue, RX queue etc. are struct socket level stuff.

> 
> 
> > Signed-off-by: Pauli Virtanen <pav@iki.fi>
> > ---
> >  include/net/bluetooth/bluetooth.h |  9 +++-
> >  net/bluetooth/af_bluetooth.c      | 72 ++++++++++++++++++++++++++++++-
> >  net/bluetooth/iso.c               |  8 ++--
> >  net/bluetooth/l2cap_sock.c        |  8 ++--
> >  net/bluetooth/sco.c               |  8 ++--
> >  5 files changed, 91 insertions(+), 14 deletions(-)
> > 
> > diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
> > index 9280e72093ee..dcee5384d715 100644
> > --- a/include/net/bluetooth/bluetooth.h
> > +++ b/include/net/bluetooth/bluetooth.h
> > @@ -240,6 +240,8 @@ struct bt_codecs {
> > 
> >  #define BT_ISO_BASE            20
> > 
> > +#define BT_NO_ERRQUEUE_POLL    21
> > +
> >  __printf(1, 2)
> >  void bt_info(const char *fmt, ...);
> >  __printf(1, 2)
> > @@ -387,7 +389,8 @@ struct bt_sock {
> >  enum {
> >         BT_SK_DEFER_SETUP,
> >         BT_SK_SUSPEND,
> > -       BT_SK_PKT_STATUS
> > +       BT_SK_PKT_STATUS,
> > +       BT_SK_NO_ERRQUEUE_POLL
> >  };
> > 
> >  struct bt_sock_list {
> > @@ -410,6 +413,10 @@ int  bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg,
> >                             size_t len, int flags);
> >  __poll_t bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait);
> >  int  bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
> > +int bt_sock_setsockopt(struct socket *sock, int level, int optname,
> > +                      sockptr_t optval, unsigned int optlen);
> > +int bt_sock_getsockopt(struct socket *sock, int level, int optname,
> > +                      char __user *optval, int __user *optlen);
> >  int  bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
> >  int  bt_sock_wait_ready(struct sock *sk, unsigned int msg_flags);
> > 
> > diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
> > index 67604ccec2f4..997197aa7b48 100644
> > --- a/net/bluetooth/af_bluetooth.c
> > +++ b/net/bluetooth/af_bluetooth.c
> > @@ -500,6 +500,12 @@ static inline __poll_t bt_accept_poll(struct sock *parent)
> >         return 0;
> >  }
> > 
> > +static bool bt_sock_error_queue_poll(struct sock *sk)
> > +{
> > +       return !test_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags) &&
> > +               !skb_queue_empty_lockless(&sk->sk_error_queue);
> > +}
> > +
> >  __poll_t bt_sock_poll(struct file *file, struct socket *sock,
> >                       poll_table *wait)
> >  {
> > @@ -511,7 +517,7 @@ __poll_t bt_sock_poll(struct file *file, struct socket *sock,
> >         if (sk->sk_state == BT_LISTEN)
> >                 return bt_accept_poll(sk);
> > 
> > -       if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
> > +       if (sk->sk_err || bt_sock_error_queue_poll(sk))
> >                 mask |= EPOLLERR |
> >                         (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
> > 
> > @@ -582,6 +588,70 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
> >  }
> >  EXPORT_SYMBOL(bt_sock_ioctl);
> > 
> > +int bt_sock_setsockopt(struct socket *sock, int level, int optname,
> > +                      sockptr_t optval, unsigned int optlen)
> > +{
> > +       struct sock *sk = sock->sk;
> > +       int err = 0;
> > +       u32 opt;
> > +
> > +       if (level != SOL_BLUETOOTH)
> > +               return -ENOPROTOOPT;
> > +
> > +       lock_sock(sk);
> > +
> > +       switch (optname) {
> > +       case BT_NO_ERRQUEUE_POLL:
> > +               if (copy_from_sockptr(&opt, optval, sizeof(opt))) {
> > +                       err = -EFAULT;
> > +                       break;
> > +               }
> > +
> > +               if (opt)
> > +                       set_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags);
> > +               else
> > +                       clear_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags);
> > +               break;
> > +
> > +       default:
> > +               err = -ENOPROTOOPT;
> > +               break;
> > +       }
> > +
> > +       release_sock(sk);
> > +       return err;
> > +}
> > +EXPORT_SYMBOL(bt_sock_setsockopt);
> > +
> > +int bt_sock_getsockopt(struct socket *sock, int level, int optname,
> > +                      char __user *optval, int __user *optlen)
> > +{
> > +       struct sock *sk = sock->sk;
> > +       int err = 0;
> > +       u32 opt;
> > +
> > +       if (level != SOL_BLUETOOTH)
> > +               return -ENOPROTOOPT;
> > +
> > +       lock_sock(sk);
> > +
> > +       switch (optname) {
> > +       case BT_NO_ERRQUEUE_POLL:
> > +               opt = test_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags);
> > +               if (put_user(opt, (u32 __user *)optval))
> > +                       err = -EFAULT;
> > +               break;
> > +
> > +       default:
> > +               err = -ENOPROTOOPT;
> > +               break;
> > +       }
> > +
> > +       release_sock(sk);
> > +       return err;
> > +}
> > +EXPORT_SYMBOL(bt_sock_getsockopt);
> > +
> >  /* This function expects the sk lock to be held when called */
> >  int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
> >  {
> > diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
> > index a77ab9df7994..6f5af549a8cc 100644
> > --- a/net/bluetooth/iso.c
> > +++ b/net/bluetooth/iso.c
> > @@ -1596,8 +1596,8 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
> >                 break;
> > 
> >         default:
> > -               err = -ENOPROTOOPT;
> > -               break;
> > +               release_sock(sk);
> > +               return bt_sock_setsockopt(sock, level, optname, optval, optlen);
> >         }
> > 
> >         release_sock(sk);
> > @@ -1667,8 +1667,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
> >                 break;
> > 
> >         default:
> > -               err = -ENOPROTOOPT;
> > -               break;
> > +               release_sock(sk);
> > +               return bt_sock_getsockopt(sock, level, optname, optval, optlen);
> >         }
> > 
> >         release_sock(sk);
> > diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
> > index 9a9f933a748b..06277ce1fd6b 100644
> > --- a/net/bluetooth/l2cap_sock.c
> > +++ b/net/bluetooth/l2cap_sock.c
> > @@ -697,8 +697,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
> >                 break;
> > 
> >         default:
> > -               err = -ENOPROTOOPT;
> > -               break;
> > +               release_sock(sk);
> > +               return bt_sock_getsockopt(sock, level, optname, optval, optlen);
> >         }
> > 
> >         release_sock(sk);
> > @@ -1102,8 +1102,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
> >                 break;
> > 
> >         default:
> > -               err = -ENOPROTOOPT;
> > -               break;
> > +               release_sock(sk);
> > +               return bt_sock_setsockopt(sock, level, optname, optval, optlen);
> >         }
> > 
> >         release_sock(sk);
> > diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
> > index b3c2af7c7d67..b8b1b314aed4 100644
> > --- a/net/bluetooth/sco.c
> > +++ b/net/bluetooth/sco.c
> > @@ -968,8 +968,8 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
> >                 break;
> > 
> >         default:
> > -               err = -ENOPROTOOPT;
> > -               break;
> > +               release_sock(sk);
> > +               return bt_sock_setsockopt(sock, level, optname, optval, optlen);
> >         }
> > 
> >         release_sock(sk);
> > @@ -1211,8 +1211,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
> >                 break;
> > 
> >         default:
> > -               err = -ENOPROTOOPT;
> > -               break;
> > +               release_sock(sk);
> > +               return bt_sock_getsockopt(sock, level, optname, optval, optlen);
> >         }
> > 
> >         release_sock(sk);
> > --
> > 2.44.0
> > 
> > 
> 
>
diff mbox series

Patch

diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 9280e72093ee..dcee5384d715 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -240,6 +240,8 @@  struct bt_codecs {
 
 #define BT_ISO_BASE		20
 
+#define BT_NO_ERRQUEUE_POLL	21
+
 __printf(1, 2)
 void bt_info(const char *fmt, ...);
 __printf(1, 2)
@@ -387,7 +389,8 @@  struct bt_sock {
 enum {
 	BT_SK_DEFER_SETUP,
 	BT_SK_SUSPEND,
-	BT_SK_PKT_STATUS
+	BT_SK_PKT_STATUS,
+	BT_SK_NO_ERRQUEUE_POLL
 };
 
 struct bt_sock_list {
@@ -410,6 +413,10 @@  int  bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg,
 			    size_t len, int flags);
 __poll_t bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait);
 int  bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+int bt_sock_setsockopt(struct socket *sock, int level, int optname,
+		       sockptr_t optval, unsigned int optlen);
+int bt_sock_getsockopt(struct socket *sock, int level, int optname,
+		       char __user *optval, int __user *optlen);
 int  bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
 int  bt_sock_wait_ready(struct sock *sk, unsigned int msg_flags);
 
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 67604ccec2f4..997197aa7b48 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -500,6 +500,12 @@  static inline __poll_t bt_accept_poll(struct sock *parent)
 	return 0;
 }
 
+static bool bt_sock_error_queue_poll(struct sock *sk)
+{
+	return !test_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags) &&
+		!skb_queue_empty_lockless(&sk->sk_error_queue);
+}
+
 __poll_t bt_sock_poll(struct file *file, struct socket *sock,
 		      poll_table *wait)
 {
@@ -511,7 +517,7 @@  __poll_t bt_sock_poll(struct file *file, struct socket *sock,
 	if (sk->sk_state == BT_LISTEN)
 		return bt_accept_poll(sk);
 
-	if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
+	if (sk->sk_err || bt_sock_error_queue_poll(sk))
 		mask |= EPOLLERR |
 			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
 
@@ -582,6 +588,70 @@  int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 }
 EXPORT_SYMBOL(bt_sock_ioctl);
 
+int bt_sock_setsockopt(struct socket *sock, int level, int optname,
+		       sockptr_t optval, unsigned int optlen)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+	u32 opt;
+
+	if (level != SOL_BLUETOOTH)
+		return -ENOPROTOOPT;
+
+	lock_sock(sk);
+
+	switch (optname) {
+	case BT_NO_ERRQUEUE_POLL:
+		if (copy_from_sockptr(&opt, optval, sizeof(opt))) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (opt)
+			set_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags);
+		else
+			clear_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags);
+		break;
+
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	}
+
+	release_sock(sk);
+	return err;
+}
+EXPORT_SYMBOL(bt_sock_setsockopt);
+
+int bt_sock_getsockopt(struct socket *sock, int level, int optname,
+		       char __user *optval, int __user *optlen)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+	u32 opt;
+
+	if (level != SOL_BLUETOOTH)
+		return -ENOPROTOOPT;
+
+	lock_sock(sk);
+
+	switch (optname) {
+	case BT_NO_ERRQUEUE_POLL:
+		opt = test_bit(BT_SK_NO_ERRQUEUE_POLL, &bt_sk(sk)->flags);
+		if (put_user(opt, (u32 __user *)optval))
+			err = -EFAULT;
+		break;
+
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	}
+
+	release_sock(sk);
+	return err;
+}
+EXPORT_SYMBOL(bt_sock_getsockopt);
+
 /* This function expects the sk lock to be held when called */
 int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
 {
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index a77ab9df7994..6f5af549a8cc 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -1596,8 +1596,8 @@  static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
 		break;
 
 	default:
-		err = -ENOPROTOOPT;
-		break;
+		release_sock(sk);
+		return bt_sock_setsockopt(sock, level, optname, optval, optlen);
 	}
 
 	release_sock(sk);
@@ -1667,8 +1667,8 @@  static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
 		break;
 
 	default:
-		err = -ENOPROTOOPT;
-		break;
+		release_sock(sk);
+		return bt_sock_getsockopt(sock, level, optname, optval, optlen);
 	}
 
 	release_sock(sk);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 9a9f933a748b..06277ce1fd6b 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -697,8 +697,8 @@  static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
 		break;
 
 	default:
-		err = -ENOPROTOOPT;
-		break;
+		release_sock(sk);
+		return bt_sock_getsockopt(sock, level, optname, optval, optlen);
 	}
 
 	release_sock(sk);
@@ -1102,8 +1102,8 @@  static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
 		break;
 
 	default:
-		err = -ENOPROTOOPT;
-		break;
+		release_sock(sk);
+		return bt_sock_setsockopt(sock, level, optname, optval, optlen);
 	}
 
 	release_sock(sk);
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index b3c2af7c7d67..b8b1b314aed4 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -968,8 +968,8 @@  static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
 		break;
 
 	default:
-		err = -ENOPROTOOPT;
-		break;
+		release_sock(sk);
+		return bt_sock_setsockopt(sock, level, optname, optval, optlen);
 	}
 
 	release_sock(sk);
@@ -1211,8 +1211,8 @@  static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 		break;
 
 	default:
-		err = -ENOPROTOOPT;
-		break;
+		release_sock(sk);
+		return bt_sock_getsockopt(sock, level, optname, optval, optlen);
 	}
 
 	release_sock(sk);