net: add a noref bit on skb dst
[pandora-kernel.git] / include / linux / skbuff.h
index c9525bc..7cdfb4d 100644 (file)
@@ -264,7 +264,7 @@ typedef unsigned char *sk_buff_data_t;
  *     @transport_header: Transport layer header
  *     @network_header: Network layer header
  *     @mac_header: Link layer header
- *     @_skb_dst: destination entry
+ *     @_skb_refdst: destination entry (with norefcount bit)
  *     @sp: the security path, used for xfrm
  *     @cb: Control buffer. Free for use by every layer. Put private vars here
  *     @len: Length of actual data
@@ -328,7 +328,7 @@ struct sk_buff {
         */
        char                    cb[48] __aligned(8);
 
-       unsigned long           _skb_dst;
+       unsigned long           _skb_refdst;
 #ifdef CONFIG_XFRM
        struct  sec_path        *sp;
 #endif
@@ -419,14 +419,64 @@ struct sk_buff {
 
 #include <asm/system.h>
 
+/*
+ * skb might have a dst pointer attached, refcounted or not.
+ * _skb_refdst low order bit is set if refcount was _not_ taken
+ */
+#define SKB_DST_NOREF  1UL
+#define SKB_DST_PTRMASK        ~(SKB_DST_NOREF)
+
+/**
+ * skb_dst - returns skb dst_entry
+ * @skb: buffer
+ *
+ * Returns skb dst_entry, regardless of reference taken or not.
+ */
 static inline struct dst_entry *skb_dst(const struct sk_buff *skb)
 {
-       return (struct dst_entry *)skb->_skb_dst;
+       /* If refdst was not refcounted, check we still are in a 
+        * rcu_read_lock section
+        */
+       WARN_ON((skb->_skb_refdst & SKB_DST_NOREF) &&
+               !rcu_read_lock_held() &&
+               !rcu_read_lock_bh_held());
+       return (struct dst_entry *)(skb->_skb_refdst & SKB_DST_PTRMASK);
 }
 
+/**
+ * skb_dst_set - sets skb dst
+ * @skb: buffer
+ * @dst: dst entry
+ *
+ * Sets skb dst, assuming a reference was taken on dst and should
+ * be released by skb_dst_drop()
+ */
 static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst)
 {
-       skb->_skb_dst = (unsigned long)dst;
+       skb->_skb_refdst = (unsigned long)dst;
+}
+
+/**
+ * skb_dst_set_noref - sets skb dst, without a reference
+ * @skb: buffer
+ * @dst: dst entry
+ *
+ * Sets skb dst, assuming a reference was not taken on dst
+ * skb_dst_drop() should not dst_release() this dst
+ */
+static inline void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst)
+{
+       WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
+       skb->_skb_refdst = (unsigned long)dst | SKB_DST_NOREF;
+}
+
+/**
+ * skb_dst_is_noref - Test if skb dst isnt refcounted
+ * @skb: buffer
+ */
+static inline bool skb_dst_is_noref(const struct sk_buff *skb)
+{
+       return (skb->_skb_refdst & SKB_DST_NOREF) && skb_dst(skb);
 }
 
 static inline struct rtable *skb_rtable(const struct sk_buff *skb)