vserver 1.9.3
[linux-2.6.git] / net / ipv4 / ip_fragment.c
index 6fd69fe..df083de 100644 (file)
@@ -169,14 +169,18 @@ static void ipfrag_secret_rebuild(unsigned long dummy)
 atomic_t ip_frag_mem = ATOMIC_INIT(0); /* Memory used for fragments */
 
 /* Memory Tracking Functions. */
-static __inline__ void frag_kfree_skb(struct sk_buff *skb)
+static __inline__ void frag_kfree_skb(struct sk_buff *skb, int *work)
 {
+       if (work)
+               *work -= skb->truesize;
        atomic_sub(skb->truesize, &ip_frag_mem);
        kfree_skb(skb);
 }
 
-static __inline__ void frag_free_queue(struct ipq *qp)
+static __inline__ void frag_free_queue(struct ipq *qp, int *work)
 {
+       if (work)
+               *work -= sizeof(struct ipq);
        atomic_sub(sizeof(struct ipq), &ip_frag_mem);
        kfree(qp);
 }
@@ -195,7 +199,7 @@ static __inline__ struct ipq *frag_alloc_queue(void)
 /* Destruction primitives. */
 
 /* Complete destruction of ipq. */
-static void ip_frag_destroy(struct ipq *qp)
+static void ip_frag_destroy(struct ipq *qp, int *work)
 {
        struct sk_buff *fp;
 
@@ -207,18 +211,18 @@ static void ip_frag_destroy(struct ipq *qp)
        while (fp) {
                struct sk_buff *xp = fp->next;
 
-               frag_kfree_skb(fp);
+               frag_kfree_skb(fp, work);
                fp = xp;
        }
 
        /* Finally, release the queue descriptor itself. */
-       frag_free_queue(qp);
+       frag_free_queue(qp, work);
 }
 
-static __inline__ void ipq_put(struct ipq *ipq)
+static __inline__ void ipq_put(struct ipq *ipq, int *work)
 {
        if (atomic_dec_and_test(&ipq->refcnt))
-               ip_frag_destroy(ipq);
+               ip_frag_destroy(ipq, work);
 }
 
 /* Kill ipq entry. It is not destroyed immediately,
@@ -237,16 +241,19 @@ static void ipq_kill(struct ipq *ipq)
 }
 
 /* Memory limiting on fragments.  Evictor trashes the oldest 
- * fragment queue until we are back under the low threshold.
+ * fragment queue until we are back under the threshold.
  */
-static void ip_evictor(void)
+static void __ip_evictor(int threshold)
 {
        struct ipq *qp;
        struct list_head *tmp;
+       int work;
 
-       for(;;) {
-               if (atomic_read(&ip_frag_mem) <= sysctl_ipfrag_low_thresh)
-                       return;
+       work = atomic_read(&ip_frag_mem) - threshold;
+       if (work <= 0)
+               return;
+
+       while (work > 0) {
                read_lock(&ipfrag_lock);
                if (list_empty(&ipq_lru_list)) {
                        read_unlock(&ipfrag_lock);
@@ -262,11 +269,16 @@ static void ip_evictor(void)
                        ipq_kill(qp);
                spin_unlock(&qp->lock);
 
-               ipq_put(qp);
+               ipq_put(qp, &work);
                IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
        }
 }
 
+static inline void ip_evictor(void)
+{
+       __ip_evictor(sysctl_ipfrag_low_thresh);
+}
+
 /*
  * Oops, a fragment queue timed out.  Kill it and send an ICMP reply.
  */
@@ -294,7 +306,7 @@ static void ip_expire(unsigned long arg)
        }
 out:
        spin_unlock(&qp->lock);
-       ipq_put(qp);
+       ipq_put(qp, NULL);
 }
 
 /* Creation primitives. */
@@ -317,7 +329,7 @@ static struct ipq *ip_frag_intern(unsigned int hash, struct ipq *qp_in)
                        atomic_inc(&qp->refcnt);
                        write_unlock(&ipfrag_lock);
                        qp_in->last_in |= COMPLETE;
-                       ipq_put(qp_in);
+                       ipq_put(qp_in, NULL);
                        return qp;
                }
        }
@@ -506,7 +518,7 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
                                qp->fragments = next;
 
                        qp->meat -= free_it->len;
-                       frag_kfree_skb(free_it);
+                       frag_kfree_skb(free_it, NULL);
                }
        }
 
@@ -657,7 +669,7 @@ struct sk_buff *ip_defrag(struct sk_buff *skb)
                        ret = ip_frag_reasm(qp, dev);
 
                spin_unlock(&qp->lock);
-               ipq_put(qp);
+               ipq_put(qp, NULL);
                return ret;
        }
 
@@ -677,4 +689,10 @@ void ipfrag_init(void)
        add_timer(&ipfrag_secret_timer);
 }
 
+void ipfrag_flush(void)
+{
+       __ip_evictor(0);
+}
+
 EXPORT_SYMBOL(ip_defrag);
+EXPORT_SYMBOL(ipfrag_flush);