mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
ipv6: lockless IPV6_RECVERR implemetation
np->recverr is moved to inet->inet_flags to fix data-races. Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: David Ahern <dsahern@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1086ca7cce
commit
3fa29971c6
@ -243,8 +243,7 @@ struct ipv6_pinfo {
|
||||
} rxopt;
|
||||
|
||||
/* sockopt flags */
|
||||
__u16 recverr:1,
|
||||
sndflow:1,
|
||||
__u16 sndflow:1,
|
||||
repflow:1,
|
||||
pmtudisc:3,
|
||||
padding:1, /* 1 bit hole */
|
||||
|
@ -274,6 +274,7 @@ enum {
|
||||
INET_FLAGS_AUTOFLOWLABEL_SET = 23,
|
||||
INET_FLAGS_AUTOFLOWLABEL = 24,
|
||||
INET_FLAGS_DONTFRAG = 25,
|
||||
INET_FLAGS_RECVERR6 = 26,
|
||||
};
|
||||
|
||||
/* cmsg flags for inet */
|
||||
|
@ -1303,9 +1303,7 @@ static inline int ip6_sock_set_v6only(struct sock *sk)
|
||||
|
||||
static inline void ip6_sock_set_recverr(struct sock *sk)
|
||||
{
|
||||
lock_sock(sk);
|
||||
inet6_sk(sk)->recverr = true;
|
||||
release_sock(sk);
|
||||
inet6_set_bit(RECVERR6, sk);
|
||||
}
|
||||
|
||||
static inline int __ip6_sock_set_addr_preferences(struct sock *sk, int val)
|
||||
|
@ -185,7 +185,7 @@ static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!sock_owned_by_user(sk) && np->recverr) {
|
||||
if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) {
|
||||
sk->sk_err = err;
|
||||
sk_error_report(sk);
|
||||
} else {
|
||||
|
@ -581,7 +581,7 @@ void ping_err(struct sk_buff *skb, int offset, u32 info)
|
||||
* 4.1.3.3.
|
||||
*/
|
||||
if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) ||
|
||||
(family == AF_INET6 && !inet6_sk(sk)->recverr)) {
|
||||
(family == AF_INET6 && !inet6_test_bit(RECVERR6, sk))) {
|
||||
if (!harderr || sk->sk_state != TCP_ESTABLISHED)
|
||||
goto out;
|
||||
} else {
|
||||
|
@ -305,11 +305,10 @@ static void ipv6_icmp_error_rfc4884(const struct sk_buff *skb,
|
||||
void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
|
||||
__be16 port, u32 info, u8 *payload)
|
||||
{
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct icmp6hdr *icmph = icmp6_hdr(skb);
|
||||
struct sock_exterr_skb *serr;
|
||||
|
||||
if (!np->recverr)
|
||||
if (!inet6_test_bit(RECVERR6, sk))
|
||||
return;
|
||||
|
||||
skb = skb_clone(skb, GFP_ATOMIC);
|
||||
@ -344,12 +343,11 @@ EXPORT_SYMBOL_GPL(ipv6_icmp_error);
|
||||
|
||||
void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info)
|
||||
{
|
||||
const struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct sock_exterr_skb *serr;
|
||||
struct ipv6hdr *iph;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!np->recverr)
|
||||
if (!inet6_test_bit(RECVERR6, sk))
|
||||
return;
|
||||
|
||||
skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
|
||||
|
@ -481,6 +481,13 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
||||
case IPV6_DONTFRAG:
|
||||
inet6_assign_bit(DONTFRAG, sk, valbool);
|
||||
return 0;
|
||||
case IPV6_RECVERR:
|
||||
if (optlen < sizeof(int))
|
||||
return -EINVAL;
|
||||
inet6_assign_bit(RECVERR6, sk, valbool);
|
||||
if (!val)
|
||||
skb_errqueue_purge(&sk->sk_error_queue);
|
||||
return 0;
|
||||
}
|
||||
if (needs_rtnl)
|
||||
rtnl_lock();
|
||||
@ -943,14 +950,6 @@ done:
|
||||
np->pmtudisc = val;
|
||||
retv = 0;
|
||||
break;
|
||||
case IPV6_RECVERR:
|
||||
if (optlen < sizeof(int))
|
||||
goto e_inval;
|
||||
np->recverr = valbool;
|
||||
if (!val)
|
||||
skb_errqueue_purge(&sk->sk_error_queue);
|
||||
retv = 0;
|
||||
break;
|
||||
case IPV6_FLOWINFO_SEND:
|
||||
if (optlen < sizeof(int))
|
||||
goto e_inval;
|
||||
@ -1380,7 +1379,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
break;
|
||||
|
||||
case IPV6_RECVERR:
|
||||
val = np->recverr;
|
||||
val = inet6_test_bit(RECVERR6, sk);
|
||||
break;
|
||||
|
||||
case IPV6_FLOWINFO_SEND:
|
||||
|
@ -291,6 +291,7 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb,
|
||||
struct inet6_skb_parm *opt,
|
||||
u8 type, u8 code, int offset, __be32 info)
|
||||
{
|
||||
bool recverr = inet6_test_bit(RECVERR6, sk);
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
int err;
|
||||
int harderr;
|
||||
@ -300,7 +301,7 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb,
|
||||
2. Socket is connected (otherwise the error indication
|
||||
is useless without recverr and error is hard.
|
||||
*/
|
||||
if (!np->recverr && sk->sk_state != TCP_ESTABLISHED)
|
||||
if (!recverr && sk->sk_state != TCP_ESTABLISHED)
|
||||
return;
|
||||
|
||||
harderr = icmpv6_err_convert(type, code, &err);
|
||||
@ -312,14 +313,14 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb,
|
||||
ip6_sk_redirect(skb, sk);
|
||||
return;
|
||||
}
|
||||
if (np->recverr) {
|
||||
if (recverr) {
|
||||
u8 *payload = skb->data;
|
||||
if (!inet_test_bit(HDRINCL, sk))
|
||||
payload += offset;
|
||||
ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload);
|
||||
}
|
||||
|
||||
if (np->recverr || harderr) {
|
||||
if (recverr || harderr) {
|
||||
sk->sk_err = err;
|
||||
sk_error_report(sk);
|
||||
}
|
||||
@ -587,7 +588,6 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
|
||||
struct flowi6 *fl6, struct dst_entry **dstp,
|
||||
unsigned int flags, const struct sockcm_cookie *sockc)
|
||||
{
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct net *net = sock_net(sk);
|
||||
struct ipv6hdr *iph;
|
||||
struct sk_buff *skb;
|
||||
@ -668,7 +668,7 @@ out:
|
||||
error:
|
||||
IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
|
||||
error_check:
|
||||
if (err == -ENOBUFS && !np->recverr)
|
||||
if (err == -ENOBUFS && !inet6_test_bit(RECVERR6, sk))
|
||||
err = 0;
|
||||
return err;
|
||||
}
|
||||
|
@ -508,7 +508,7 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
tcp_ld_RTO_revert(sk, seq);
|
||||
}
|
||||
|
||||
if (!sock_owned_by_user(sk) && np->recverr) {
|
||||
if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) {
|
||||
WRITE_ONCE(sk->sk_err, err);
|
||||
sk_error_report(sk);
|
||||
} else {
|
||||
|
@ -619,7 +619,7 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!np->recverr) {
|
||||
if (!inet6_test_bit(RECVERR6, sk)) {
|
||||
if (!harderr || sk->sk_state != TCP_ESTABLISHED)
|
||||
goto out;
|
||||
} else {
|
||||
@ -1283,7 +1283,7 @@ csum_partial:
|
||||
send:
|
||||
err = ip6_send_skb(skb);
|
||||
if (err) {
|
||||
if (err == -ENOBUFS && !inet6_sk(sk)->recverr) {
|
||||
if (err == -ENOBUFS && !inet6_test_bit(RECVERR6, sk)) {
|
||||
UDP6_INC_STATS(sock_net(sk),
|
||||
UDP_MIB_SNDBUFERRORS, is_udplite);
|
||||
err = 0;
|
||||
@ -1608,7 +1608,7 @@ do_append_data:
|
||||
up->pending = 0;
|
||||
|
||||
if (err > 0)
|
||||
err = np->recverr ? net_xmit_errno(err) : 0;
|
||||
err = inet6_test_bit(RECVERR6, sk) ? net_xmit_errno(err) : 0;
|
||||
release_sock(sk);
|
||||
|
||||
out:
|
||||
|
@ -128,7 +128,6 @@ static void sctp_v6_err_handle(struct sctp_transport *t, struct sk_buff *skb,
|
||||
{
|
||||
struct sctp_association *asoc = t->asoc;
|
||||
struct sock *sk = asoc->base.sk;
|
||||
struct ipv6_pinfo *np;
|
||||
int err = 0;
|
||||
|
||||
switch (type) {
|
||||
@ -149,9 +148,8 @@ static void sctp_v6_err_handle(struct sctp_transport *t, struct sk_buff *skb,
|
||||
break;
|
||||
}
|
||||
|
||||
np = inet6_sk(sk);
|
||||
icmpv6_err_convert(type, code, &err);
|
||||
if (!sock_owned_by_user(sk) && np->recverr) {
|
||||
if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) {
|
||||
sk->sk_err = err;
|
||||
sk_error_report(sk);
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user