Skip to content

Commit 7a15365

Browse files
committed
Merge branch 'Refactor-classifier-API-to-work-with-Qdisc-blocks-without-rtnl-lock'
Vlad Buslov says: ==================== Refactor classifier API to work with Qdisc/blocks without rtnl lock Currently, all netlink protocol handlers for updating rules, actions and qdiscs are protected with single global rtnl lock which removes any possibility for parallelism. This patch set is a third step to remove rtnl lock dependency from TC rules update path. Recently, new rtnl registration flag RTNL_FLAG_DOIT_UNLOCKED was added. Handlers registered with this flag are called without RTNL taken. End goal is to have rule update handlers(RTM_NEWTFILTER, RTM_DELTFILTER, etc.) to be registered with UNLOCKED flag to allow parallel execution. However, there is no intention to completely remove or split rtnl lock itself. This patch set addresses specific problems in implementation of classifiers API that prevent its control path from being executed concurrently. Additional changes are required to refactor classifiers API and individual classifiers for parallel execution. This patch set lays groundwork to eventually register rule update handlers as rtnl-unlocked by modifying code in cls API that works with Qdiscs and blocks. Following patch set does the same for chains and classifiers. The goal of this change is to refactor tcf_block_find() and its dependencies to allow concurrent execution: - Extend Qdisc API with rcu to lookup and take reference to Qdisc without relying on rtnl lock. - Extend tcf_block with atomic reference counting and rcu. - Always take reference to tcf_block while working with it. - Implement tcf_block_release() to release resources obtained by tcf_block_find() - Create infrastructure to allow registering Qdiscs with class ops that do not require the caller to hold rtnl lock. All three netlink rule update handlers use tcf_block_find() to lookup Qdisc and block, and this patch set introduces additional means of synchronization to substitute rtnl lock in cls API. Some functions in cls and sch APIs have historic names that no longer clearly describe their intent. In order not make this code even more confusing when introducing their concurrency-friendly versions, rename these functions to describe actual implementation. Changes from V2 to V3: - Patch 1: - Explicitly include refcount.h in rtnetlink.h. - Patch 3: - Move rcu_head field to the end of struct Qdisc. - Rearrange local variable declarations in qdisc_lookup_rcu(). - Patch 5: - Remove tcf_qdisc_put() and inline its content to callers. Changes from V1 to V2: - Rebase on latest net-next. - Patch 8 - remove. - Patch 9 - fold into patch 11. - Patch 11: - Rename tcf_block_{get|put}() to tcf_block_refcnt_{get|put}(). - Patch 13 - remove. ==================== Acked-by: Cong Wang <xiyou.wangcong@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2 parents c774973 + 787ce6d commit 7a15365

24 files changed

+294
-112
lines changed

include/linux/rtnetlink.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/mutex.h>
77
#include <linux/netdevice.h>
88
#include <linux/wait.h>
9+
#include <linux/refcount.h>
910
#include <uapi/linux/rtnetlink.h>
1011

1112
extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo);
@@ -34,6 +35,7 @@ extern void rtnl_unlock(void);
3435
extern int rtnl_trylock(void);
3536
extern int rtnl_is_locked(void);
3637
extern int rtnl_lock_killable(void);
38+
extern bool refcount_dec_and_rtnl_lock(refcount_t *r);
3739

3840
extern wait_queue_head_t netdev_unregistering_wq;
3941
extern struct rw_semaphore pernet_ops_rwsem;
@@ -83,6 +85,11 @@ static inline struct netdev_queue *dev_ingress_queue(struct net_device *dev)
8385
return rtnl_dereference(dev->ingress_queue);
8486
}
8587

88+
static inline struct netdev_queue *dev_ingress_queue_rcu(struct net_device *dev)
89+
{
90+
return rcu_dereference(dev->ingress_queue);
91+
}
92+
8693
struct netdev_queue *dev_ingress_queue_create(struct net_device *dev);
8794

8895
#ifdef CONFIG_NET_INGRESS

include/net/pkt_sched.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ int qdisc_set_default(const char *id);
102102
void qdisc_hash_add(struct Qdisc *q, bool invisible);
103103
void qdisc_hash_del(struct Qdisc *q);
104104
struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle);
105+
struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle);
105106
struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
106107
struct nlattr *tab,
107108
struct netlink_ext_ack *extack);

include/net/sch_generic.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ struct Qdisc {
105105

106106
spinlock_t busylock ____cacheline_aligned_in_smp;
107107
spinlock_t seqlock;
108+
struct rcu_head rcu;
108109
};
109110

110111
static inline void qdisc_refcount_inc(struct Qdisc *qdisc)
@@ -114,6 +115,19 @@ static inline void qdisc_refcount_inc(struct Qdisc *qdisc)
114115
refcount_inc(&qdisc->refcnt);
115116
}
116117

118+
/* Intended to be used by unlocked users, when concurrent qdisc release is
119+
* possible.
120+
*/
121+
122+
static inline struct Qdisc *qdisc_refcount_inc_nz(struct Qdisc *qdisc)
123+
{
124+
if (qdisc->flags & TCQ_F_BUILTIN)
125+
return qdisc;
126+
if (refcount_inc_not_zero(&qdisc->refcnt))
127+
return qdisc;
128+
return NULL;
129+
}
130+
117131
static inline bool qdisc_is_running(struct Qdisc *qdisc)
118132
{
119133
if (qdisc->flags & TCQ_F_NOLOCK)
@@ -331,7 +345,7 @@ struct tcf_chain {
331345
struct tcf_block {
332346
struct list_head chain_list;
333347
u32 index; /* block index for shared blocks */
334-
unsigned int refcnt;
348+
refcount_t refcnt;
335349
struct net *net;
336350
struct Qdisc *q;
337351
struct list_head cb_list;
@@ -343,6 +357,7 @@ struct tcf_block {
343357
struct tcf_chain *chain;
344358
struct list_head filter_chain_list;
345359
} chain0;
360+
struct rcu_head rcu;
346361
};
347362

348363
static inline void tcf_block_offload_inc(struct tcf_block *block, u32 *flags)
@@ -554,7 +569,8 @@ void dev_deactivate_many(struct list_head *head);
554569
struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue,
555570
struct Qdisc *qdisc);
556571
void qdisc_reset(struct Qdisc *qdisc);
557-
void qdisc_destroy(struct Qdisc *qdisc);
572+
void qdisc_put(struct Qdisc *qdisc);
573+
void qdisc_put_unlocked(struct Qdisc *qdisc);
558574
void qdisc_tree_reduce_backlog(struct Qdisc *qdisc, unsigned int n,
559575
unsigned int len);
560576
struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,

net/core/rtnetlink.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ int rtnl_is_locked(void)
130130
}
131131
EXPORT_SYMBOL(rtnl_is_locked);
132132

133+
bool refcount_dec_and_rtnl_lock(refcount_t *r)
134+
{
135+
return refcount_dec_and_mutex_lock(r, &rtnl_mutex);
136+
}
137+
EXPORT_SYMBOL(refcount_dec_and_rtnl_lock);
138+
133139
#ifdef CONFIG_PROVE_LOCKING
134140
bool lockdep_rtnl_is_held(void)
135141
{

0 commit comments

Comments
 (0)