Skip to content

Commit 5889a62

Browse files
committed
Merge branch 'sch_fq-optimizations'
Eric Dumazet says: ==================== net_sched: sch_fq: round of optimizations This series is focused on better layout of struct fq_flow to reduce number of cache line misses in fq_enqueue() and dequeue operations. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents ee1bd48 + 348e289 commit 5889a62

File tree

1 file changed

+48
-36
lines changed

1 file changed

+48
-36
lines changed

net/sched/sch_fq.c

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -66,22 +66,27 @@ static inline struct fq_skb_cb *fq_skb_cb(struct sk_buff *skb)
6666
* in linear list (head,tail), otherwise are placed in a rbtree (t_root).
6767
*/
6868
struct fq_flow {
69+
/* First cache line : used in fq_gc(), fq_enqueue(), fq_dequeue() */
6970
struct rb_root t_root;
7071
struct sk_buff *head; /* list of skbs for this flow : first skb */
7172
union {
7273
struct sk_buff *tail; /* last skb in the list */
73-
unsigned long age; /* jiffies when flow was emptied, for gc */
74+
unsigned long age; /* (jiffies | 1UL) when flow was emptied, for gc */
7475
};
7576
struct rb_node fq_node; /* anchor in fq_root[] trees */
7677
struct sock *sk;
78+
u32 socket_hash; /* sk_hash */
7779
int qlen; /* number of packets in flow queue */
80+
81+
/* Second cache line, used in fq_dequeue() */
7882
int credit;
79-
u32 socket_hash; /* sk_hash */
80-
struct fq_flow *next; /* next pointer in RR lists, or &detached */
83+
/* 32bit hole on 64bit arches */
84+
85+
struct fq_flow *next; /* next pointer in RR lists */
8186

8287
struct rb_node rate_node; /* anchor in q->delayed tree */
8388
u64 time_next_packet;
84-
};
89+
} ____cacheline_aligned_in_smp;
8590

8691
struct fq_flow_head {
8792
struct fq_flow *first;
@@ -126,20 +131,25 @@ struct fq_sched_data {
126131
struct qdisc_watchdog watchdog;
127132
};
128133

129-
/* special value to mark a detached flow (not on old/new list) */
130-
static struct fq_flow detached, throttled;
131-
134+
/*
135+
* f->tail and f->age share the same location.
136+
* We can use the low order bit to differentiate if this location points
137+
* to a sk_buff or contains a jiffies value, if we force this value to be odd.
138+
* This assumes f->tail low order bit must be 0 since alignof(struct sk_buff) >= 2
139+
*/
132140
static void fq_flow_set_detached(struct fq_flow *f)
133141
{
134-
f->next = &detached;
135-
f->age = jiffies;
142+
f->age = jiffies | 1UL;
136143
}
137144

138145
static bool fq_flow_is_detached(const struct fq_flow *f)
139146
{
140-
return f->next == &detached;
147+
return !!(f->age & 1UL);
141148
}
142149

150+
/* special value to mark a throttled flow (not on old/new list) */
151+
static struct fq_flow throttled;
152+
143153
static bool fq_flow_is_throttled(const struct fq_flow *f)
144154
{
145155
return f->next == &throttled;
@@ -204,9 +214,10 @@ static void fq_gc(struct fq_sched_data *q,
204214
struct rb_root *root,
205215
struct sock *sk)
206216
{
207-
struct fq_flow *f, *tofree[FQ_GC_MAX];
208217
struct rb_node **p, *parent;
209-
int fcnt = 0;
218+
void *tofree[FQ_GC_MAX];
219+
struct fq_flow *f;
220+
int i, fcnt = 0;
210221

211222
p = &root->rb_node;
212223
parent = NULL;
@@ -229,15 +240,18 @@ static void fq_gc(struct fq_sched_data *q,
229240
p = &parent->rb_left;
230241
}
231242

243+
if (!fcnt)
244+
return;
245+
246+
for (i = fcnt; i > 0; ) {
247+
f = tofree[--i];
248+
rb_erase(&f->fq_node, root);
249+
}
232250
q->flows -= fcnt;
233251
q->inactive_flows -= fcnt;
234252
q->stat_gc_flows += fcnt;
235-
while (fcnt) {
236-
struct fq_flow *f = tofree[--fcnt];
237253

238-
rb_erase(&f->fq_node, root);
239-
kmem_cache_free(fq_flow_cachep, f);
240-
}
254+
kmem_cache_free_bulk(fq_flow_cachep, fcnt, tofree);
241255
}
242256

243257
static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q)
@@ -370,19 +384,17 @@ static void fq_erase_head(struct Qdisc *sch, struct fq_flow *flow,
370384
}
371385
}
372386

373-
/* remove one skb from head of flow queue */
374-
static struct sk_buff *fq_dequeue_head(struct Qdisc *sch, struct fq_flow *flow)
387+
/* Remove one skb from flow queue.
388+
* This skb must be the return value of prior fq_peek().
389+
*/
390+
static void fq_dequeue_skb(struct Qdisc *sch, struct fq_flow *flow,
391+
struct sk_buff *skb)
375392
{
376-
struct sk_buff *skb = fq_peek(flow);
377-
378-
if (skb) {
379-
fq_erase_head(sch, flow, skb);
380-
skb_mark_not_on_list(skb);
381-
flow->qlen--;
382-
qdisc_qstats_backlog_dec(sch, skb);
383-
sch->q.qlen--;
384-
}
385-
return skb;
393+
fq_erase_head(sch, flow, skb);
394+
skb_mark_not_on_list(skb);
395+
flow->qlen--;
396+
qdisc_qstats_backlog_dec(sch, skb);
397+
sch->q.qlen--;
386398
}
387399

388400
static void flow_queue_add(struct fq_flow *flow, struct sk_buff *skb)
@@ -494,9 +506,11 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
494506
if (!sch->q.qlen)
495507
return NULL;
496508

497-
skb = fq_dequeue_head(sch, &q->internal);
498-
if (skb)
509+
skb = fq_peek(&q->internal);
510+
if (unlikely(skb)) {
511+
fq_dequeue_skb(sch, &q->internal, skb);
499512
goto out;
513+
}
500514

501515
now = ktime_get_ns();
502516
fq_check_throttled(q, now);
@@ -532,14 +546,13 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
532546
fq_flow_set_throttled(q, f);
533547
goto begin;
534548
}
549+
prefetch(&skb->end);
535550
if ((s64)(now - time_next_packet - q->ce_threshold) > 0) {
536551
INET_ECN_set_ce(skb);
537552
q->stat_ce_mark++;
538553
}
539-
}
540-
541-
skb = fq_dequeue_head(sch, f);
542-
if (!skb) {
554+
fq_dequeue_skb(sch, f, skb);
555+
} else {
543556
head->first = f->next;
544557
/* force a pass through old_flows to prevent starvation */
545558
if ((head == &q->new_flows) && q->old_flows.first) {
@@ -550,7 +563,6 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
550563
}
551564
goto begin;
552565
}
553-
prefetch(&skb->end);
554566
plen = qdisc_pkt_len(skb);
555567
f->credit -= plen;
556568

0 commit comments

Comments
 (0)