mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
net: dropreason: add SKB_DROP_REASON_FRAG_REASM_TIMEOUT
Used to track skbs freed after a timeout happened in a reassmbly unit. Passing a @reason argument to inet_frag_rbtree_purge() allows to use correct consumed status for frags that have been successfully re-assembled. Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
4ecbb1c27c
commit
77adfd3a1d
@ -69,6 +69,7 @@
|
||||
FN(IP_INNOROUTES) \
|
||||
FN(PKT_TOO_BIG) \
|
||||
FN(DUP_FRAG) \
|
||||
FN(FRAG_REASM_TIMEOUT) \
|
||||
FNe(MAX)
|
||||
|
||||
/**
|
||||
@ -303,6 +304,8 @@ enum skb_drop_reason {
|
||||
SKB_DROP_REASON_PKT_TOO_BIG,
|
||||
/** @SKB_DROP_REASON_DUP_FRAG: duplicate fragment */
|
||||
SKB_DROP_REASON_DUP_FRAG,
|
||||
/** @SKB_DROP_REASON_FRAG_REASM_TIMEOUT: fragment reassembly timeout */
|
||||
SKB_DROP_REASON_FRAG_REASM_TIMEOUT,
|
||||
/**
|
||||
* @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be
|
||||
* used as a real 'reason'
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <linux/in6.h>
|
||||
#include <linux/rbtree_types.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <net/dropreason.h>
|
||||
|
||||
/* Per netns frag queues directory */
|
||||
struct fqdir {
|
||||
@ -34,12 +35,14 @@ struct fqdir {
|
||||
* @INET_FRAG_LAST_IN: final fragment has arrived
|
||||
* @INET_FRAG_COMPLETE: frag queue has been processed and is due for destruction
|
||||
* @INET_FRAG_HASH_DEAD: inet_frag_kill() has not removed fq from rhashtable
|
||||
* @INET_FRAG_DROP: if skbs must be dropped (instead of being consumed)
|
||||
*/
|
||||
enum {
|
||||
INET_FRAG_FIRST_IN = BIT(0),
|
||||
INET_FRAG_LAST_IN = BIT(1),
|
||||
INET_FRAG_COMPLETE = BIT(2),
|
||||
INET_FRAG_HASH_DEAD = BIT(3),
|
||||
INET_FRAG_DROP = BIT(4),
|
||||
};
|
||||
|
||||
struct frag_v4_compare_key {
|
||||
@ -139,7 +142,8 @@ void inet_frag_destroy(struct inet_frag_queue *q);
|
||||
struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key);
|
||||
|
||||
/* Free all skbs in the queue; return the sum of their truesizes. */
|
||||
unsigned int inet_frag_rbtree_purge(struct rb_root *root);
|
||||
unsigned int inet_frag_rbtree_purge(struct rb_root *root,
|
||||
enum skb_drop_reason reason);
|
||||
|
||||
static inline void inet_frag_put(struct inet_frag_queue *q)
|
||||
{
|
||||
|
@ -76,6 +76,7 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
|
||||
if (fq->q.flags & INET_FRAG_COMPLETE)
|
||||
goto out;
|
||||
|
||||
fq->q.flags |= INET_FRAG_DROP;
|
||||
inet_frag_kill(&fq->q);
|
||||
|
||||
dev = dev_get_by_index_rcu(net, fq->iif);
|
||||
@ -101,7 +102,7 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
|
||||
spin_unlock(&fq->q.lock);
|
||||
|
||||
icmpv6_send(head, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0);
|
||||
kfree_skb(head);
|
||||
kfree_skb_reason(head, SKB_DROP_REASON_FRAG_REASM_TIMEOUT);
|
||||
goto out_rcu_unlock;
|
||||
|
||||
out:
|
||||
|
@ -133,6 +133,7 @@ static void inet_frags_free_cb(void *ptr, void *arg)
|
||||
count = del_timer_sync(&fq->timer) ? 1 : 0;
|
||||
|
||||
spin_lock_bh(&fq->lock);
|
||||
fq->flags |= INET_FRAG_DROP;
|
||||
if (!(fq->flags & INET_FRAG_COMPLETE)) {
|
||||
fq->flags |= INET_FRAG_COMPLETE;
|
||||
count++;
|
||||
@ -260,7 +261,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head)
|
||||
kmem_cache_free(f->frags_cachep, q);
|
||||
}
|
||||
|
||||
unsigned int inet_frag_rbtree_purge(struct rb_root *root)
|
||||
unsigned int inet_frag_rbtree_purge(struct rb_root *root,
|
||||
enum skb_drop_reason reason)
|
||||
{
|
||||
struct rb_node *p = rb_first(root);
|
||||
unsigned int sum = 0;
|
||||
@ -274,7 +276,7 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root)
|
||||
struct sk_buff *next = FRAG_CB(skb)->next_frag;
|
||||
|
||||
sum += skb->truesize;
|
||||
kfree_skb(skb);
|
||||
kfree_skb_reason(skb, reason);
|
||||
skb = next;
|
||||
}
|
||||
}
|
||||
@ -284,17 +286,21 @@ EXPORT_SYMBOL(inet_frag_rbtree_purge);
|
||||
|
||||
void inet_frag_destroy(struct inet_frag_queue *q)
|
||||
{
|
||||
struct fqdir *fqdir;
|
||||
unsigned int sum, sum_truesize = 0;
|
||||
enum skb_drop_reason reason;
|
||||
struct inet_frags *f;
|
||||
struct fqdir *fqdir;
|
||||
|
||||
WARN_ON(!(q->flags & INET_FRAG_COMPLETE));
|
||||
reason = (q->flags & INET_FRAG_DROP) ?
|
||||
SKB_DROP_REASON_FRAG_REASM_TIMEOUT :
|
||||
SKB_CONSUMED;
|
||||
WARN_ON(del_timer(&q->timer) != 0);
|
||||
|
||||
/* Release all fragment data. */
|
||||
fqdir = q->fqdir;
|
||||
f = fqdir->f;
|
||||
sum_truesize = inet_frag_rbtree_purge(&q->rb_fragments);
|
||||
sum_truesize = inet_frag_rbtree_purge(&q->rb_fragments, reason);
|
||||
sum = sum_truesize + f->qsize;
|
||||
|
||||
call_rcu(&q->rcu, inet_frag_destroy_rcu);
|
||||
|
@ -153,6 +153,7 @@ static void ip_expire(struct timer_list *t)
|
||||
if (qp->q.flags & INET_FRAG_COMPLETE)
|
||||
goto out;
|
||||
|
||||
qp->q.flags |= INET_FRAG_DROP;
|
||||
ipq_kill(qp);
|
||||
__IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
|
||||
__IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT);
|
||||
@ -194,7 +195,7 @@ out:
|
||||
spin_unlock(&qp->q.lock);
|
||||
out_rcu_unlock:
|
||||
rcu_read_unlock();
|
||||
kfree_skb(head);
|
||||
kfree_skb_reason(head, SKB_DROP_REASON_FRAG_REASM_TIMEOUT);
|
||||
ipq_put(qp);
|
||||
}
|
||||
|
||||
@ -254,7 +255,8 @@ static int ip_frag_reinit(struct ipq *qp)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments);
|
||||
sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments,
|
||||
SKB_DROP_REASON_NOT_SPECIFIED);
|
||||
sub_frag_mem_limit(qp->q.fqdir, sum_truesize);
|
||||
|
||||
qp->q.flags = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user