Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: frags: fix a lockdep false positive

lockdep does not know that the locks used by IPv4 defrag
and IPv6 reassembly units are of different classes.

It complains because of following chains :

1) sch_direct_xmit() (lock txq->_xmit_lock)
dev_hard_start_xmit()
xmit_one()
dev_queue_xmit_nit()
packet_rcv_fanout()
ip_check_defrag()
ip_defrag()
spin_lock() (lock frag queue spinlock)

2) ip6_input_finish()
ipv6_frag_rcv() (lock frag queue spinlock)
ip6_frag_queue()
icmpv6_param_prob() (lock txq->_xmit_lock at some point)

We could add lockdep annotations, but we also can make sure IPv6
calls icmpv6_param_prob() only after the release of the frag queue spinlock,
since this naturally makes frag queue spinlock a leaf in lock hierarchy.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
415787d7 0dcec221

+12 -11
+12 -11
net/ipv6/reassembly.c
··· 163 163 } 164 164 165 165 static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, 166 - struct frag_hdr *fhdr, int nhoff) 166 + struct frag_hdr *fhdr, int nhoff, 167 + u32 *prob_offset) 167 168 { 168 169 struct sk_buff *prev, *next; 169 170 struct net_device *dev; ··· 180 179 ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1))); 181 180 182 181 if ((unsigned int)end > IPV6_MAXPLEN) { 183 - __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev), 184 - IPSTATS_MIB_INHDRERRORS); 185 - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 186 - ((u8 *)&fhdr->frag_off - 187 - skb_network_header(skb))); 182 + *prob_offset = (u8 *)&fhdr->frag_off - skb_network_header(skb); 188 183 return -1; 189 184 } 190 185 ··· 211 214 /* RFC2460 says always send parameter problem in 212 215 * this case. -DaveM 213 216 */ 214 - __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev), 215 - IPSTATS_MIB_INHDRERRORS); 216 - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 217 - offsetof(struct ipv6hdr, payload_len)); 217 + *prob_offset = offsetof(struct ipv6hdr, payload_len); 218 218 return -1; 219 219 } 220 220 if (end > fq->q.len) { ··· 513 519 iif = skb->dev ? skb->dev->ifindex : 0; 514 520 fq = fq_find(net, fhdr->identification, hdr, iif); 515 521 if (fq) { 522 + u32 prob_offset = 0; 516 523 int ret; 517 524 518 525 spin_lock(&fq->q.lock); 519 526 520 527 fq->iif = iif; 521 - ret = ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff); 528 + ret = ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff, 529 + &prob_offset); 522 530 523 531 spin_unlock(&fq->q.lock); 524 532 inet_frag_put(&fq->q); 533 + if (prob_offset) { 534 + __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev), 535 + IPSTATS_MIB_INHDRERRORS); 536 + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, prob_offset); 537 + } 525 538 return ret; 526 539 } 527 540