]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - include/net/sch_generic.h
net: sched: prevent insertion of new classifiers during chain flush
[linux.git] / include / net / sch_generic.h
index 9481f2c142e26ee1174653d673e6134edd9851da..e8cf36ed3e8769d9b58b113a36bcbc0707fa0e84 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/list.h>
 #include <linux/refcount.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 #include <net/gen_stats.h>
 #include <net/rtnetlink.h>
 
@@ -321,6 +322,12 @@ struct tcf_proto {
        void                    *data;
        const struct tcf_proto_ops      *ops;
        struct tcf_chain        *chain;
+       /* Lock protects tcf_proto shared state and can be used by unlocked
+        * classifiers to protect their private data.
+        */
+       spinlock_t              lock;
+       bool                    deleting;
+       refcount_t              refcnt;
        struct rcu_head         rcu;
 };
 
@@ -340,6 +347,8 @@ struct qdisc_skb_cb {
 typedef void tcf_chain_head_change_t(struct tcf_proto *tp_head, void *priv);
 
 struct tcf_chain {
+       /* Protects filter_chain. */
+       struct mutex filter_chain_lock;
        struct tcf_proto __rcu *filter_chain;
        struct list_head list;
        struct tcf_block *block;
@@ -347,11 +356,16 @@ struct tcf_chain {
        unsigned int refcnt;
        unsigned int action_refcnt;
        bool explicitly_created;
+       bool flushing;
        const struct tcf_proto_ops *tmplt_ops;
        void *tmplt_priv;
 };
 
 struct tcf_block {
+       /* Lock protects tcf_block and lifetime-management data of chains
+        * attached to the block (refcnt, action_refcnt, explicitly_created).
+        */
+       struct mutex lock;
        struct list_head chain_list;
        u32 index; /* block index for shared blocks */
        refcount_t refcnt;
@@ -369,6 +383,34 @@ struct tcf_block {
        struct rcu_head rcu;
 };
 
+#ifdef CONFIG_PROVE_LOCKING
+static inline bool lockdep_tcf_chain_is_locked(struct tcf_chain *chain)
+{
+       return lockdep_is_held(&chain->filter_chain_lock);
+}
+
+static inline bool lockdep_tcf_proto_is_locked(struct tcf_proto *tp)
+{
+       return lockdep_is_held(&tp->lock);
+}
+#else
+static inline bool lockdep_tcf_chain_is_locked(struct tcf_block *chain)
+{
+       return true;
+}
+
+static inline bool lockdep_tcf_proto_is_locked(struct tcf_proto *tp)
+{
+       return true;
+}
+#endif /* #ifdef CONFIG_PROVE_LOCKING */
+
+#define tcf_chain_dereference(p, chain)                                        \
+       rcu_dereference_protected(p, lockdep_tcf_chain_is_locked(chain))
+
+#define tcf_proto_dereference(p, tp)                                   \
+       rcu_dereference_protected(p, lockdep_tcf_proto_is_locked(tp))
+
 static inline void tcf_block_offload_inc(struct tcf_block *block, u32 *flags)
 {
        if (*flags & TCA_CLS_FLAGS_IN_HW)
@@ -580,8 +622,7 @@ struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue,
 void qdisc_reset(struct Qdisc *qdisc);
 void qdisc_put(struct Qdisc *qdisc);
 void qdisc_put_unlocked(struct Qdisc *qdisc);
-void qdisc_tree_reduce_backlog(struct Qdisc *qdisc, unsigned int n,
-                              unsigned int len);
+void qdisc_tree_reduce_backlog(struct Qdisc *qdisc, int n, int len);
 #ifdef CONFIG_NET_SCHED
 int qdisc_offload_dump_helper(struct Qdisc *q, enum tc_setup_type type,
                              void *type_data);