diff options
Diffstat (limited to 'net/packet/af_packet.c')
-rw-r--r-- | net/packet/af_packet.c | 25 |
1 files changed, 22 insertions, 3 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 3a383fd72f82..8f0156b10f8d 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1341,6 +1341,20 @@ static int fanout_rr_next(struct packet_fanout *f, unsigned int num) return x; } +static bool fanout_flow_is_huge(struct packet_sock *po, struct sk_buff *skb) +{ + u32 rxhash; + int i, count = 0; + + rxhash = skb_get_hash(skb); + for (i = 0; i < ROLLOVER_HLEN; i++) + if (po->rollover->history[i] == rxhash) + count++; + + po->rollover->history[prandom_u32() % ROLLOVER_HLEN] = rxhash; + return count > (ROLLOVER_HLEN >> 1); +} + static unsigned int fanout_demux_hash(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) @@ -1381,11 +1395,16 @@ static unsigned int fanout_demux_rollover(struct packet_fanout *f, unsigned int num) { struct packet_sock *po, *po_next; - unsigned int i, j; + unsigned int i, j, room; po = pkt_sk(f->arr[idx]); - if (try_self && packet_rcv_has_room(po, skb) != ROOM_NONE) - return idx; + + if (try_self) { + room = packet_rcv_has_room(po, skb); + if (room == ROOM_NORMAL || + (room == ROOM_LOW && !fanout_flow_is_huge(po, skb))) + return idx; + } i = j = min_t(int, po->rollover->sock, num - 1); do { |