diff options
| author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-05-11 19:19:48 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2010-05-17 20:18:50 -0400 |
| commit | 7fee226ad2397b635e2fd565a59ca3ae08a164cd (patch) | |
| tree | 0bcd26150ad74ec1a237109de87a3d214a07fc22 /include/net | |
| parent | ebda37c27d0c768947e9b058332d7ea798210cf8 (diff) | |
net: add a noref bit on skb dst
Use low order bit of skb->_skb_dst to tell dst is not refcounted.
Change _skb_dst to _skb_refdst to make sure all uses are catched.
skb_dst() returns the dst, regardless of noref bit set or not, but
with a lockdep check to make sure a noref dst is not given if current
user is not rcu protected.
New skb_dst_set_noref() helper to set an notrefcounted dst on a skb.
(with lockdep check)
skb_dst_drop() drops a reference only if skb dst was refcounted.
skb_dst_force() helper is used to force a refcount on dst, when skb
is queued and not anymore RCU protected.
Use skb_dst_force() in __sk_add_backlog(), __dev_xmit_skb() if
!IFF_XMIT_DST_RELEASE or skb enqueued on qdisc queue, in
sock_queue_rcv_skb(), in __nf_queue().
Use skb_dst_force() in dev_requeue_skb().
Note: dst_use_noref() still dirties dst, we might transform it
later to do one dirtying per jiffies.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'include/net')
| -rw-r--r-- | include/net/dst.h | 48 | ||||
| -rw-r--r-- | include/net/sock.h | 13 |
2 files changed, 53 insertions, 8 deletions
diff --git a/include/net/dst.h b/include/net/dst.h index aac5a5fcfda9..27207a13f2a6 100644 --- a/include/net/dst.h +++ b/include/net/dst.h | |||
| @@ -168,6 +168,12 @@ static inline void dst_use(struct dst_entry *dst, unsigned long time) | |||
| 168 | dst->lastuse = time; | 168 | dst->lastuse = time; |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | static inline void dst_use_noref(struct dst_entry *dst, unsigned long time) | ||
| 172 | { | ||
| 173 | dst->__use++; | ||
| 174 | dst->lastuse = time; | ||
| 175 | } | ||
| 176 | |||
| 171 | static inline | 177 | static inline |
| 172 | struct dst_entry * dst_clone(struct dst_entry * dst) | 178 | struct dst_entry * dst_clone(struct dst_entry * dst) |
| 173 | { | 179 | { |
| @@ -177,11 +183,47 @@ struct dst_entry * dst_clone(struct dst_entry * dst) | |||
| 177 | } | 183 | } |
| 178 | 184 | ||
| 179 | extern void dst_release(struct dst_entry *dst); | 185 | extern void dst_release(struct dst_entry *dst); |
| 186 | |||
| 187 | static inline void refdst_drop(unsigned long refdst) | ||
| 188 | { | ||
| 189 | if (!(refdst & SKB_DST_NOREF)) | ||
| 190 | dst_release((struct dst_entry *)(refdst & SKB_DST_PTRMASK)); | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * skb_dst_drop - drops skb dst | ||
| 195 | * @skb: buffer | ||
| 196 | * | ||
| 197 | * Drops dst reference count if a reference was taken. | ||
| 198 | */ | ||
| 180 | static inline void skb_dst_drop(struct sk_buff *skb) | 199 | static inline void skb_dst_drop(struct sk_buff *skb) |
| 181 | { | 200 | { |
| 182 | if (skb->_skb_dst) | 201 | if (skb->_skb_refdst) { |
| 183 | dst_release(skb_dst(skb)); | 202 | refdst_drop(skb->_skb_refdst); |
| 184 | skb->_skb_dst = 0UL; | 203 | skb->_skb_refdst = 0UL; |
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | static inline void skb_dst_copy(struct sk_buff *nskb, const struct sk_buff *oskb) | ||
| 208 | { | ||
| 209 | nskb->_skb_refdst = oskb->_skb_refdst; | ||
| 210 | if (!(nskb->_skb_refdst & SKB_DST_NOREF)) | ||
| 211 | dst_clone(skb_dst(nskb)); | ||
| 212 | } | ||
| 213 | |||
| 214 | /** | ||
| 215 | * skb_dst_force - makes sure skb dst is refcounted | ||
| 216 | * @skb: buffer | ||
| 217 | * | ||
| 218 | * If dst is not yet refcounted, let's do it | ||
| 219 | */ | ||
| 220 | static inline void skb_dst_force(struct sk_buff *skb) | ||
| 221 | { | ||
| 222 | if (skb_dst_is_noref(skb)) { | ||
| 223 | WARN_ON(!rcu_read_lock_held()); | ||
| 224 | skb->_skb_refdst &= ~SKB_DST_NOREF; | ||
| 225 | dst_clone(skb_dst(skb)); | ||
| 226 | } | ||
| 185 | } | 227 | } |
| 186 | 228 | ||
| 187 | /* Children define the path of the packet through the | 229 | /* Children define the path of the packet through the |
diff --git a/include/net/sock.h b/include/net/sock.h index aed16eb9db4b..5697caf8cc76 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
| @@ -600,12 +600,15 @@ static inline int sk_stream_memory_free(struct sock *sk) | |||
| 600 | /* OOB backlog add */ | 600 | /* OOB backlog add */ |
| 601 | static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb) | 601 | static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb) |
| 602 | { | 602 | { |
| 603 | if (!sk->sk_backlog.tail) { | 603 | /* dont let skb dst not refcounted, we are going to leave rcu lock */ |
| 604 | sk->sk_backlog.head = sk->sk_backlog.tail = skb; | 604 | skb_dst_force(skb); |
| 605 | } else { | 605 | |
| 606 | if (!sk->sk_backlog.tail) | ||
| 607 | sk->sk_backlog.head = skb; | ||
| 608 | else | ||
| 606 | sk->sk_backlog.tail->next = skb; | 609 | sk->sk_backlog.tail->next = skb; |
| 607 | sk->sk_backlog.tail = skb; | 610 | |
| 608 | } | 611 | sk->sk_backlog.tail = skb; |
| 609 | skb->next = NULL; | 612 | skb->next = NULL; |
| 610 | } | 613 | } |
| 611 | 614 | ||
