Skip to content

Commit

Permalink
net: rcu-ify tcf_proto
Browse files Browse the repository at this point in the history
rcu'ify tcf_proto this allows calling tc_classify() without holding
any locks. Updaters are protected by RTNL.

This patch prepares the core net_sched infrastracture for running
the classifier/action chains without holding the qdisc lock however
it does nothing to ensure cls_xxx and act_xxx types also work without
locking. Additional patches are required to address the fall out.

Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
jrfastab authored and davem330 committed Sep 13, 2014
1 parent 46e5da4 commit 25d8c0d
Show file tree
Hide file tree
Showing 17 changed files with 121 additions and 88 deletions.
9 changes: 5 additions & 4 deletions include/net/sch_generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ struct Qdisc_class_ops {
void (*walk)(struct Qdisc *, struct qdisc_walker * arg);

/* Filter manipulation */
struct tcf_proto ** (*tcf_chain)(struct Qdisc *, unsigned long);
struct tcf_proto __rcu ** (*tcf_chain)(struct Qdisc *, unsigned long);
unsigned long (*bind_tcf)(struct Qdisc *, unsigned long,
u32 classid);
void (*unbind_tcf)(struct Qdisc *, unsigned long);
Expand Down Expand Up @@ -212,8 +212,8 @@ struct tcf_proto_ops {

struct tcf_proto {
/* Fast access part */
struct tcf_proto *next;
void *root;
struct tcf_proto __rcu *next;
void __rcu *root;
int (*classify)(struct sk_buff *,
const struct tcf_proto *,
struct tcf_result *);
Expand All @@ -225,6 +225,7 @@ struct tcf_proto {
struct Qdisc *q;
void *data;
const struct tcf_proto_ops *ops;
struct rcu_head rcu;
};

struct qdisc_skb_cb {
Expand Down Expand Up @@ -378,7 +379,7 @@ struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue,
void __qdisc_calculate_pkt_len(struct sk_buff *skb,
const struct qdisc_size_table *stab);
void tcf_destroy(struct tcf_proto *tp);
void tcf_destroy_chain(struct tcf_proto **fl);
void tcf_destroy_chain(struct tcf_proto __rcu **fl);

/* Reset all TX qdiscs greater then index of a device. */
static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)
Expand Down
30 changes: 15 additions & 15 deletions net/sched/cls_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,15 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
{
struct net *net = sock_net(skb->sk);
struct nlattr *tca[TCA_MAX + 1];
spinlock_t *root_lock;
struct tcmsg *t;
u32 protocol;
u32 prio;
u32 nprio;
u32 parent;
struct net_device *dev;
struct Qdisc *q;
struct tcf_proto **back, **chain;
struct tcf_proto __rcu **back;
struct tcf_proto __rcu **chain;
struct tcf_proto *tp;
const struct tcf_proto_ops *tp_ops;
const struct Qdisc_class_ops *cops;
Expand Down Expand Up @@ -197,7 +197,9 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
goto errout;

/* Check the chain for existence of proto-tcf with this priority */
for (back = chain; (tp = *back) != NULL; back = &tp->next) {
for (back = chain;
(tp = rtnl_dereference(*back)) != NULL;
back = &tp->next) {
if (tp->prio >= prio) {
if (tp->prio == prio) {
if (!nprio ||
Expand All @@ -209,8 +211,6 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
}
}

root_lock = qdisc_root_sleeping_lock(q);

if (tp == NULL) {
/* Proto-tcf does not exist, create new one */

Expand Down Expand Up @@ -259,7 +259,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
}
tp->ops = tp_ops;
tp->protocol = protocol;
tp->prio = nprio ? : TC_H_MAJ(tcf_auto_prio(*back));
tp->prio = nprio ? :
TC_H_MAJ(tcf_auto_prio(rtnl_dereference(*back)));
tp->q = q;
tp->classify = tp_ops->classify;
tp->classid = parent;
Expand All @@ -280,9 +281,9 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)

if (fh == 0) {
if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
spin_lock_bh(root_lock);
*back = tp->next;
spin_unlock_bh(root_lock);
struct tcf_proto *next = rtnl_dereference(tp->next);

RCU_INIT_POINTER(*back, next);

tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
tcf_destroy(tp);
Expand Down Expand Up @@ -322,10 +323,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE);
if (err == 0) {
if (tp_created) {
spin_lock_bh(root_lock);
tp->next = *back;
*back = tp;
spin_unlock_bh(root_lock);
RCU_INIT_POINTER(tp->next, rtnl_dereference(*back));
rcu_assign_pointer(*back, tp);
}
tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
} else {
Expand Down Expand Up @@ -420,7 +419,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
int s_t;
struct net_device *dev;
struct Qdisc *q;
struct tcf_proto *tp, **chain;
struct tcf_proto *tp, __rcu **chain;
struct tcmsg *tcm = nlmsg_data(cb->nlh);
unsigned long cl = 0;
const struct Qdisc_class_ops *cops;
Expand Down Expand Up @@ -454,7 +453,8 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)

s_t = cb->args[0];

for (tp = *chain, t = 0; tp; tp = tp->next, t++) {
for (tp = rtnl_dereference(*chain), t = 0;
tp; tp = rtnl_dereference(tp->next), t++) {
if (t < s_t)
continue;
if (TC_H_MAJ(tcm->tcm_info) &&
Expand Down
10 changes: 5 additions & 5 deletions net/sched/sch_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1781,7 +1781,7 @@ int tc_classify_compat(struct sk_buff *skb, const struct tcf_proto *tp,
__be16 protocol = skb->protocol;
int err;

for (; tp; tp = tp->next) {
for (; tp; tp = rcu_dereference_bh(tp->next)) {
if (tp->protocol != protocol &&
tp->protocol != htons(ETH_P_ALL))
continue;
Expand Down Expand Up @@ -1833,15 +1833,15 @@ void tcf_destroy(struct tcf_proto *tp)
{
tp->ops->destroy(tp);
module_put(tp->ops->owner);
kfree(tp);
kfree_rcu(tp, rcu);
}

void tcf_destroy_chain(struct tcf_proto **fl)
void tcf_destroy_chain(struct tcf_proto __rcu **fl)
{
struct tcf_proto *tp;

while ((tp = *fl) != NULL) {
*fl = tp->next;
while ((tp = rtnl_dereference(*fl)) != NULL) {
RCU_INIT_POINTER(*fl, tp->next);
tcf_destroy(tp);
}
}
Expand Down
20 changes: 11 additions & 9 deletions net/sched/sch_atm.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

struct atm_flow_data {
struct Qdisc *q; /* FIFO, TBF, etc. */
struct tcf_proto *filter_list;
struct tcf_proto __rcu *filter_list;
struct atm_vcc *vcc; /* VCC; NULL if VCC is closed */
void (*old_pop)(struct atm_vcc *vcc,
struct sk_buff *skb); /* chaining */
Expand Down Expand Up @@ -273,7 +273,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
error = -ENOBUFS;
goto err_out;
}
flow->filter_list = NULL;
RCU_INIT_POINTER(flow->filter_list, NULL);
flow->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid);
if (!flow->q)
flow->q = &noop_qdisc;
Expand Down Expand Up @@ -311,7 +311,7 @@ static int atm_tc_delete(struct Qdisc *sch, unsigned long arg)
pr_debug("atm_tc_delete(sch %p,[qdisc %p],flow %p)\n", sch, p, flow);
if (list_empty(&flow->list))
return -EINVAL;
if (flow->filter_list || flow == &p->link)
if (rcu_access_pointer(flow->filter_list) || flow == &p->link)
return -EBUSY;
/*
* Reference count must be 2: one for "keepalive" (set at class
Expand Down Expand Up @@ -345,7 +345,8 @@ static void atm_tc_walk(struct Qdisc *sch, struct qdisc_walker *walker)
}
}

static struct tcf_proto **atm_tc_find_tcf(struct Qdisc *sch, unsigned long cl)
static struct tcf_proto __rcu **atm_tc_find_tcf(struct Qdisc *sch,
unsigned long cl)
{
struct atm_qdisc_data *p = qdisc_priv(sch);
struct atm_flow_data *flow = (struct atm_flow_data *)cl;
Expand All @@ -369,11 +370,12 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
flow = NULL;
if (TC_H_MAJ(skb->priority) != sch->handle ||
!(flow = (struct atm_flow_data *)atm_tc_get(sch, skb->priority))) {
struct tcf_proto *fl;

list_for_each_entry(flow, &p->flows, list) {
if (flow->filter_list) {
result = tc_classify_compat(skb,
flow->filter_list,
&res);
fl = rcu_dereference_bh(flow->filter_list);
if (fl) {
result = tc_classify_compat(skb, fl, &res);
if (result < 0)
continue;
flow = (struct atm_flow_data *)res.class;
Expand Down Expand Up @@ -544,7 +546,7 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt)
if (!p->link.q)
p->link.q = &noop_qdisc;
pr_debug("atm_tc_init: link (%p) qdisc %p\n", &p->link, p->link.q);
p->link.filter_list = NULL;
RCU_INIT_POINTER(p->link.filter_list, NULL);
p->link.vcc = NULL;
p->link.sock = NULL;
p->link.classid = sch->handle;
Expand Down
11 changes: 7 additions & 4 deletions net/sched/sch_cbq.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ struct cbq_class {
struct gnet_stats_rate_est64 rate_est;
struct tc_cbq_xstats xstats;

struct tcf_proto *filter_list;
struct tcf_proto __rcu *filter_list;

int refcnt;
int filters;
Expand Down Expand Up @@ -221,6 +221,7 @@ cbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
struct cbq_class **defmap;
struct cbq_class *cl = NULL;
u32 prio = skb->priority;
struct tcf_proto *fl;
struct tcf_result res;

/*
Expand All @@ -235,11 +236,12 @@ cbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
int result = 0;
defmap = head->defaults;

fl = rcu_dereference_bh(head->filter_list);
/*
* Step 2+n. Apply classifier.
*/
if (!head->filter_list ||
(result = tc_classify_compat(skb, head->filter_list, &res)) < 0)
result = tc_classify_compat(skb, fl, &res);
if (!fl || result < 0)
goto fallback;

cl = (void *)res.class;
Expand Down Expand Up @@ -1954,7 +1956,8 @@ static int cbq_delete(struct Qdisc *sch, unsigned long arg)
return 0;
}

static struct tcf_proto **cbq_find_tcf(struct Qdisc *sch, unsigned long arg)
static struct tcf_proto __rcu **cbq_find_tcf(struct Qdisc *sch,
unsigned long arg)
{
struct cbq_sched_data *q = qdisc_priv(sch);
struct cbq_class *cl = (struct cbq_class *)arg;
Expand Down
15 changes: 9 additions & 6 deletions net/sched/sch_choke.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct choke_sched_data {

/* Variables */
struct red_vars vars;
struct tcf_proto *filter_list;
struct tcf_proto __rcu *filter_list;
struct {
u32 prob_drop; /* Early probability drops */
u32 prob_mark; /* Early probability marks */
Expand Down Expand Up @@ -193,9 +193,11 @@ static bool choke_classify(struct sk_buff *skb,
{
struct choke_sched_data *q = qdisc_priv(sch);
struct tcf_result res;
struct tcf_proto *fl;
int result;

result = tc_classify(skb, q->filter_list, &res);
fl = rcu_dereference_bh(q->filter_list);
result = tc_classify(skb, fl, &res);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
Expand Down Expand Up @@ -249,19 +251,19 @@ static bool choke_match_random(const struct choke_sched_data *q,
return false;

oskb = choke_peek_random(q, pidx);
if (q->filter_list)
if (rcu_access_pointer(q->filter_list))
return choke_get_classid(nskb) == choke_get_classid(oskb);

return choke_match_flow(oskb, nskb);
}

static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
struct choke_sched_data *q = qdisc_priv(sch);
const struct red_parms *p = &q->parms;
int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;

if (q->filter_list) {
if (rcu_access_pointer(q->filter_list)) {
/* If using external classifiers, get result and record it. */
if (!choke_classify(skb, sch, &ret))
goto other_drop; /* Packet was eaten by filter */
Expand Down Expand Up @@ -554,7 +556,8 @@ static unsigned long choke_bind(struct Qdisc *sch, unsigned long parent,
return 0;
}

static struct tcf_proto **choke_find_tcf(struct Qdisc *sch, unsigned long cl)
static struct tcf_proto __rcu **choke_find_tcf(struct Qdisc *sch,
unsigned long cl)
{
struct choke_sched_data *q = qdisc_priv(sch);

Expand Down
9 changes: 6 additions & 3 deletions net/sched/sch_drr.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct drr_class {

struct drr_sched {
struct list_head active;
struct tcf_proto *filter_list;
struct tcf_proto __rcu *filter_list;
struct Qdisc_class_hash clhash;
};

Expand Down Expand Up @@ -184,7 +184,8 @@ static void drr_put_class(struct Qdisc *sch, unsigned long arg)
drr_destroy_class(sch, cl);
}

static struct tcf_proto **drr_tcf_chain(struct Qdisc *sch, unsigned long cl)
static struct tcf_proto __rcu **drr_tcf_chain(struct Qdisc *sch,
unsigned long cl)
{
struct drr_sched *q = qdisc_priv(sch);

Expand Down Expand Up @@ -319,6 +320,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
struct drr_sched *q = qdisc_priv(sch);
struct drr_class *cl;
struct tcf_result res;
struct tcf_proto *fl;
int result;

if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) {
Expand All @@ -328,7 +330,8 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
}

*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
result = tc_classify(skb, q->filter_list, &res);
fl = rcu_dereference_bh(q->filter_list);
result = tc_classify(skb, fl, &res);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
Expand Down
9 changes: 5 additions & 4 deletions net/sched/sch_dsmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

struct dsmark_qdisc_data {
struct Qdisc *q;
struct tcf_proto *filter_list;
struct tcf_proto __rcu *filter_list;
u8 *mask; /* "owns" the array */
u8 *value;
u16 indices;
Expand Down Expand Up @@ -186,8 +186,8 @@ static void dsmark_walk(struct Qdisc *sch, struct qdisc_walker *walker)
}
}

static inline struct tcf_proto **dsmark_find_tcf(struct Qdisc *sch,
unsigned long cl)
static inline struct tcf_proto __rcu **dsmark_find_tcf(struct Qdisc *sch,
unsigned long cl)
{
struct dsmark_qdisc_data *p = qdisc_priv(sch);
return &p->filter_list;
Expand Down Expand Up @@ -229,7 +229,8 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch)
skb->tc_index = TC_H_MIN(skb->priority);
else {
struct tcf_result res;
int result = tc_classify(skb, p->filter_list, &res);
struct tcf_proto *fl = rcu_dereference_bh(p->filter_list);
int result = tc_classify(skb, fl, &res);

pr_debug("result %d class 0x%04x\n", result, res.classid);

Expand Down
Loading

0 comments on commit 25d8c0d

Please sign in to comment.