Module Name: src Committed By: rmind Date: Mon Jun 9 12:57:05 UTC 2014
Modified Files: src/sys/net: if.c pktqueue.c pktqueue.h Log Message: Implement pktq_set_maxlen() and let sysctl net.inet.{ip,ip6}.ifq.maxlen be changed on the fly again. To generate a diff of this commit: cvs rdiff -u -r1.278 -r1.279 src/sys/net/if.c cvs rdiff -u -r1.1 -r1.2 src/sys/net/pktqueue.c src/sys/net/pktqueue.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/if.c diff -u src/sys/net/if.c:1.278 src/sys/net/if.c:1.279 --- src/sys/net/if.c:1.278 Sat Jun 7 13:25:33 2014 +++ src/sys/net/if.c Mon Jun 9 12:57:04 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: if.c,v 1.278 2014/06/07 13:25:33 he Exp $ */ +/* $NetBSD: if.c,v 1.279 2014/06/09 12:57:04 rmind Exp $ */ /*- * Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc. @@ -90,7 +90,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.278 2014/06/07 13:25:33 he Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.279 2014/06/09 12:57:04 rmind Exp $"); #include "opt_inet.h" @@ -2341,6 +2341,20 @@ bad: #if defined(INET) || defined(INET6) static int +sysctl_pktq_maxlen(SYSCTLFN_ARGS, pktqueue_t *pq) +{ + u_int nmaxlen = pktq_get_count(pq, PKTQ_MAXLEN); + struct sysctlnode node = *rnode; + int error; + + node.sysctl_data = &nmaxlen; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + return pktq_set_maxlen(pq, nmaxlen); +} + +static int sysctl_pktq_count(SYSCTLFN_ARGS, pktqueue_t *pq, u_int count_id) { int count = pktq_get_count(pq, count_id); @@ -2357,12 +2371,21 @@ sysctl_pktq_count(SYSCTLFN_ARGS, pktqueu } #if defined(INET) -SYSCTL_NET_PKTQ(ip_pktq, maxlen, PKTQ_MAXLEN) +static int +sysctl_net_ip_pktq_maxlen(SYSCTLFN_ARGS) +{ + return sysctl_pktq_maxlen(SYSCTLFN_CALL(rnode), ip_pktq); +} SYSCTL_NET_PKTQ(ip_pktq, items, PKTQ_NITEMS) SYSCTL_NET_PKTQ(ip_pktq, drops, PKTQ_DROPS) #endif + #if defined(INET6) -SYSCTL_NET_PKTQ(ip6_pktq, maxlen, PKTQ_MAXLEN) +static int +sysctl_net_ip6_pktq_maxlen(SYSCTLFN_ARGS) +{ + return sysctl_pktq_maxlen(SYSCTLFN_CALL(rnode), ip6_pktq); +} SYSCTL_NET_PKTQ(ip6_pktq, items, PKTQ_NITEMS) SYSCTL_NET_PKTQ(ip6_pktq, drops, PKTQ_DROPS) #endif Index: src/sys/net/pktqueue.c diff -u src/sys/net/pktqueue.c:1.1 src/sys/net/pktqueue.c:1.2 --- src/sys/net/pktqueue.c:1.1 Thu Jun 5 23:48:16 2014 +++ src/sys/net/pktqueue.c Mon Jun 9 12:57:04 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: pktqueue.c,v 1.1 2014/06/05 23:48:16 rmind Exp $ */ +/* $NetBSD: pktqueue.c,v 1.2 2014/06/09 12:57:04 rmind Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pktqueue.c,v 1.1 2014/06/05 23:48:16 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pktqueue.c,v 1.2 2014/06/09 12:57:04 rmind Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -299,3 +299,63 @@ pktq_flush(pktqueue_t *pq) } } } + +/* + * pktq_set_maxlen: create per-CPU queues using a new size and replace + * the existing queues without losing any packets. + */ +int +pktq_set_maxlen(pktqueue_t *pq, size_t maxlen) +{ + const u_int slotbytes = ncpu * sizeof(pcq_t *); + pcq_t **qs; + + if (!maxlen || maxlen > PCQ_MAXLEN) + return EINVAL; + if (pq->pq_maxlen == maxlen) + return 0; + + /* First, allocate the new queues and replace them. */ + qs = kmem_zalloc(slotbytes, KM_SLEEP); + for (u_int i = 0; i < ncpu; i++) { + qs[i] = pcq_create(maxlen, KM_SLEEP); + } + mutex_enter(&pq->pq_lock); + for (u_int i = 0; i < ncpu; i++) { + /* Swap: store of a word is atomic. */ + pcq_t *q = pq->pq_queue[i]; + pq->pq_queue[i] = qs[i]; + qs[i] = q; + } + pq->pq_maxlen = maxlen; + mutex_exit(&pq->pq_lock); + + /* + * At this point, the new packets are flowing into the new + * queues. However, the old queues may have same packets + * present which are no longer being present. We are going + * to re-enqueue them. This may change the order of packet + * arrival, but it is not considered an issue. + * + * There may also in-flight interrupts calling pktq_dequeue() + * which reference the old queues. Issue a barrier to ensure + * that we are going to be the only pcq_get() callers on the + * old queues. + */ + pktq_barrier(pq); + + for (u_int i = 0; i < ncpu; i++) { + struct mbuf *m; + + while ((m = pcq_get(qs[i])) != NULL) { + while (!pcq_put(pq->pq_queue[i], m)) { + kpause("pktqrenq", false, 1, NULL); + } + } + pcq_destroy(qs[i]); + } + + /* Well, that was fun. */ + kmem_free(qs, slotbytes); + return 0; +} Index: src/sys/net/pktqueue.h diff -u src/sys/net/pktqueue.h:1.1 src/sys/net/pktqueue.h:1.2 --- src/sys/net/pktqueue.h:1.1 Thu Jun 5 23:48:16 2014 +++ src/sys/net/pktqueue.h Mon Jun 9 12:57:04 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: pktqueue.h,v 1.1 2014/06/05 23:48:16 rmind Exp $ */ +/* $NetBSD: pktqueue.h,v 1.2 2014/06/09 12:57:04 rmind Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -49,6 +49,7 @@ bool pktq_enqueue(pktqueue_t *, struct struct mbuf * pktq_dequeue(pktqueue_t *); void pktq_barrier(pktqueue_t *); void pktq_flush(pktqueue_t *); +int pktq_set_maxlen(pktqueue_t *, size_t); uint32_t pktq_rps_hash(const struct mbuf *); uint64_t pktq_get_count(pktqueue_t *, pktq_count_t);