# HG changeset patch # Parent 62d4c2f971fb8e37024ae1e508262a5090eef8e8 diff -r 62d4c2f971fb -r ccca6c93ab9b sys/conf/files --- a/sys/conf/files Thu May 29 14:02:00 2014 -0700 +++ b/sys/conf/files Thu May 29 14:02:21 2014 -0700 @@ -3146,6 +3146,7 @@ net/bpf_filter.c optional bpf | netgrap net/bpf_zerocopy.c optional bpf net/bridgestp.c optional bridge | if_bridge net/flowtable.c optional flowtable inet | flowtable inet6 +net/flow_throttle.c standard net/ieee8023ad_lacp.c optional lagg net/if.c standard net/if_arcsubr.c optional arcnet diff -r 62d4c2f971fb -r ccca6c93ab9b sys/kern/uipc_socket.c --- a/sys/kern/uipc_socket.c Thu May 29 14:02:00 2014 -0700 +++ b/sys/kern/uipc_socket.c Thu May 29 14:02:21 2014 -0700 @@ -127,6 +127,7 @@ #include #include #include +#include #include #include #include @@ -558,6 +559,7 @@ sonewconn(struct socket *head, int conns so->so_rcv.sb_flags |= head->so_rcv.sb_flags & SB_AUTOSIZE; so->so_snd.sb_flags |= head->so_snd.sb_flags & SB_AUTOSIZE; so->so_state |= connstatus; + set_tx_throttle(head, so); ACCEPT_LOCK(); /* * The accept socket may be tearing down but we just @@ -760,6 +762,7 @@ sofree(struct socket *so) * before calling pru_detach. This means that protocols shold not * assume they can perform socket wakeups, etc, in their detach code. */ + sofree_flow_throttler(so); sbdestroy(&so->so_snd, so); sbdestroy(&so->so_rcv, so); seldrain(&so->so_snd.sb_sel); @@ -2572,6 +2575,10 @@ sosetopt(struct socket *so, struct socko #endif break; + case SO_TX_THROTTLE: + error = do_setopt_tx_throttle(so, sopt); + break; + default: error = ENOPROTOOPT; break; @@ -2758,6 +2765,10 @@ integer: optval = so->so_incqlen; goto integer; + case SO_TX_THROTTLE: + error = do_getopt_tx_throttle(so, sopt); + break; + default: error = ENOPROTOOPT; break; diff -r 62d4c2f971fb -r ccca6c93ab9b sys/net/flow_throttle.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/net/flow_throttle.c Thu May 29 14:02:21 2014 -0700 @@ -0,0 +1,590 @@ +/*- + * Copyright (c) 2013-2014 Chelsio Communications, Inc. + * All rights reserved. + * Written by: Navdeep Parhar + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* XXX: solely for INKERNEL */ +#include /* XXX: ditto */ +#include /* XXX: ditto */ +#include /* XXX: ditto */ +#include + +#define SO_FT_MAGIC 0x89abcdef +static MALLOC_DEFINE(M_FLOWTHROTTLE, "so_throttle", + "socket parameters for flow throttling"); + +enum { + FTI_ACTIVATED = 1 << 0, /* Flow throttling has been activated */ + FTI_FLOOR = 1 << 1, /* If set, use largest rate available + * that's smaller than the + * user-specified rate; else use the + * smallest rate available that's larger + * than the user-specified rate + */ +}; + +static int sysctl_flow_throttling_enable(SYSCTL_HANDLER_ARGS); +static int sysctl_floor_or_ceil(SYSCTL_HANDLER_ARGS); +static int sysctl_flow_rates(SYSCTL_HANDLER_ARGS); +static int activate_flow_throttling_locked(void); +static int deactivate_flow_throttling_locked(void); + +#define MAX_FLOW_THROTTLE_RATES 16 +static struct flow_throttling_info { + struct mtx fti_lock; + u_int fti_flags; + STAILQ_HEAD(, flow_throttling_provider) flow_throttling_providers; + u_int nflows; + u_int nrates; + u_int min_rate; + u_int max_rate; + vmem_t *fid_arena; + u_int fids_in_use; + u_int fids_peak; + u_int fid_alloc_failed; + u_int flow_rate[MAX_FLOW_THROTTLE_RATES]; +} flow_throttling_info; +static struct flow_throttling_info *fti = &flow_throttling_info; + +SYSCTL_NODE(_net, OID_AUTO, ft, CTLFLAG_RW, 0, "flow throttle sysctl"); + +SYSCTL_PROC(_net_ft, OID_AUTO, enabled, CTLTYPE_INT | CTLFLAG_RW, 0, 0, + sysctl_flow_throttling_enable, "I", "Flow throttling enabled"); +SYSCTL_PROC(_net_ft, OID_AUTO, floor, CTLTYPE_INT | CTLFLAG_RW, 0, 0, + sysctl_floor_or_ceil, "I", "0 = use ceil, 1 = use floor"); +SYSCTL_UINT(_net_ft, OID_AUTO, nflows, CTLFLAG_RD, + &flow_throttling_info.nflows, 0, "Number of flow identifiers available"); +SYSCTL_UINT(_net_ft, OID_AUTO, nrates, CTLFLAG_RD, + &flow_throttling_info.nrates, 0, "Number of flow rates available"); +SYSCTL_PROC(_net_ft, OID_AUTO, flow_rates, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, + sysctl_flow_rates, "I", "Flow rates"); +SYSCTL_UINT(_net_ft, OID_AUTO, fids_in_use, CTLFLAG_RD, + &flow_throttling_info.fids_in_use, 0, "Number of flow identifiers in use"); +SYSCTL_UINT(_net_ft, OID_AUTO, fids_peak, CTLFLAG_RD, + &flow_throttling_info.fids_peak, 0, "Peak usage of flow identifiers"); +SYSCTL_UINT(_net_ft, OID_AUTO, fid_alloc_failed, CTLFLAG_RD, + &flow_throttling_info.fid_alloc_failed, 0, + "Number of flow identifier allocation failures"); + +static void +init_flow_throttling(void *arg) +{ + + mtx_init(&fti->fti_lock, "flow throttling", NULL, MTX_DEF); + STAILQ_INIT(&fti->flow_throttling_providers); +} +SYSINIT(flow_throttling, SI_SUB_LAST, SI_ORDER_ANY, init_flow_throttling, NULL); + +/* + * XXX: not generic at all. + */ +static void +setup_flow_rate_table(void) +{ + int i, j; + static u_int rate[] = { + 1000, 2000, 3000, 4000, 5000, 8000, 10000, 12000, 15000, 20000, + 25000, 30000, 35000, 40000, 45000, 50000 + }; + + KASSERT(fti->nrates > 0, ("%s: nrates not initialized yet.", __func__)); + + for (i = j = 0; i < nitems(rate) && j < fti->nrates; i++) { + if (rate[i] < fti->min_rate) + continue; + if (rate[i] > fti->max_rate) + break; + fti->flow_rate[j++] = rate[i]; + } + fti->nrates = j; + if (fti->nrates == 0) { + log(LOG_ERR, "%s: no usable rate for flow throttling.\n", + __func__); + } +} + +int +register_flow_throttler(struct flow_throttling_provider *ft) +{ + int rc, i; + + if (ft->name == NULL || ft->nflows == 0 || ft->nrates == 0 || + ft->min_rate == 0 || ft->max_rate == 0 || + ft->set_throttle_rate == NULL || ft->max_rate < ft->min_rate) + return (EINVAL); + + mtx_lock(&fti->fti_lock); + if (STAILQ_EMPTY(&fti->flow_throttling_providers)) { + + /* First provider to be registered */ + + KASSERT((fti->fti_flags & FTI_ACTIVATED) == 0, + ("%s: no providers but flow throttling active.", __func__)); + fti->nflows = ft->nflows; + fti->nrates = min(ft->nrates, MAX_FLOW_THROTTLE_RATES); + fti->min_rate = ft->min_rate; + fti->max_rate = ft->max_rate; + } else if ((fti->fti_flags & FTI_ACTIVATED) == 0) { + + /* + * Not the first to be registered, but flow throttling not + * activated yet. This one must have some overlapping range + * with the providers already registered. + */ + if (ft->max_rate < fti->min_rate || + ft->min_rate > fti->max_rate) { + rc = EINVAL; + goto done; + } + + fti->nflows = min(fti->nflows, ft->nflows); + fti->nrates = min(fti->nrates, ft->nrates); + fti->min_rate = max(fti->min_rate, ft->min_rate); + fti->max_rate = min(fti->max_rate, ft->max_rate); + } else { + + /* + * Flow throttling is already activated. This one must conform + * to the floors/ceilings already established. + */ + if (ft->nflows < fti->nflows || ft->nrates < fti->nrates || + ft->min_rate > fti->min_rate || + ft->max_rate < fti->max_rate) { + rc = EINVAL; + goto done; + } + + for (i = 0; i < fti->nrates; i++) { + rc = ft->set_throttle_rate(ft->cookie, i, + fti->flow_rate[i]); + if (rc != 0) + goto done; + } + } + STAILQ_INSERT_TAIL(&fti->flow_throttling_providers, ft, link); + rc = 0; +done: + mtx_unlock(&fti->fti_lock); + return (rc); +} + +static int +activate_flow_throttling_locked(void) +{ + struct flow_throttling_provider *ft; + int rc, i; + + mtx_assert(&fti->fti_lock, MA_OWNED); + + if (STAILQ_EMPTY(&fti->flow_throttling_providers)) + return (ENXIO); + + fti->fid_arena = vmem_create("flow throttling ids", 0, fti->nflows, 1, + 0, M_FIRSTFIT | M_NOWAIT); + if (fti->fid_arena == NULL) + return (ENOMEM); + + setup_flow_rate_table(); + + for (i = 0; i < fti->nrates; i++) { + STAILQ_FOREACH(ft, &fti->flow_throttling_providers, link) { + rc = ft->set_throttle_rate(ft->cookie, i, + fti->flow_rate[i]); + if (rc != 0) { + log(LOG_ERR, "%s: error %u while setting " + "rate[%u] = %uKbps for provider \"%s\"\n", + __func__, rc, i, fti->flow_rate[i], + ft->name); + vmem_destroy(fti->fid_arena); + return (rc); + } + } + } + + fti->fti_flags |= FTI_ACTIVATED; + + return (0); +} + +static int +deactivate_flow_throttling_locked(void) +{ + + mtx_assert(&fti->fti_lock, MA_OWNED); + + if (fti->fids_in_use > 0) { + KASSERT(fti->fti_flags & FTI_ACTIVATED, + ("%s: fids_in_use = %u when flow throttling not active", + __func__, fti->fids_in_use)); + return (EBUSY); + } + + vmem_destroy(fti->fid_arena); + fti->fti_flags &= ~FTI_ACTIVATED; + + return (0); +} + +static int +sysctl_flow_throttling_enable(SYSCTL_HANDLER_ARGS) +{ + int enable, rc; + + mtx_lock(&fti->fti_lock); + enable = (fti->fti_flags & FTI_ACTIVATED) != 0; + mtx_unlock(&fti->fti_lock); + + rc = sysctl_handle_int(oidp, &enable, 0, req); + if (rc != 0 || req->newptr == NULL) + return (rc); + + mtx_lock(&fti->fti_lock); + if (enable && !(fti->fti_flags & FTI_ACTIVATED)) + rc = activate_flow_throttling_locked(); + else if (!enable && (fti->fti_flags & FTI_ACTIVATED)) + rc = deactivate_flow_throttling_locked(); + mtx_unlock(&fti->fti_lock); + return (rc); +} + +static int +sysctl_floor_or_ceil(SYSCTL_HANDLER_ARGS) +{ + int floor, rc; + + mtx_lock(&fti->fti_lock); + floor = (fti->fti_flags & FTI_FLOOR) != 0; + mtx_unlock(&fti->fti_lock); + + rc = sysctl_handle_int(oidp, &floor, 0, req); + if (rc != 0 || req->newptr == NULL) + return (rc); + + mtx_lock(&fti->fti_lock); + if (floor) + fti->fti_flags |= FTI_FLOOR; + else + fti->fti_flags &= ~FTI_FLOOR; + mtx_unlock(&fti->fti_lock); + + return (rc); +} + +static int +sysctl_flow_rates(SYSCTL_HANDLER_ARGS) +{ + struct sbuf sb; + int i; + + sbuf_new(&sb, NULL, 256, SBUF_AUTOEXTEND); + + mtx_lock(&fti->fti_lock); + for (i = 0; i < fti->nrates; i++) + sbuf_printf(&sb, "%uKbps ", fti->flow_rate[i]); + mtx_unlock(&fti->fti_lock); + sbuf_trim(&sb); + sbuf_finish(&sb); + sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); + sbuf_delete(&sb); + + return (0); +} + +static u_int +rate_to_ridx_floor(u_int rate) +{ + u_int ridx; + + mtx_assert(&fti->fti_lock, MA_OWNED); + KASSERT(fti->fti_flags & FTI_ACTIVATED, + ("%s: flow throttling not activated yet", __func__)); + + for (ridx = 0; ridx < fti->nrates - 1; ridx++) { + if (fti->flow_rate[ridx + 1] > rate) + break; + } + + return (ridx); +} + +static u_int +rate_to_ridx_ceil(u_int rate) +{ + u_int ridx; + + mtx_assert(&fti->fti_lock, MA_OWNED); + KASSERT(fti->fti_flags & FTI_ACTIVATED, + ("%s: flow throttling not activated yet", __func__)); + + for (ridx = fti->nrates - 1; ridx > 0; ridx--) { + if (fti->flow_rate[ridx - 1] < rate) + break; + } + + return (ridx); +} + +static u_int +requested_rate_to_ridx(u_int rate) +{ + u_int ridx; + + mtx_assert(&fti->fti_lock, MA_OWNED); + KASSERT(fti->fti_flags & FTI_ACTIVATED, + ("%s: flow throttling not activated yet", __func__)); + + if (rate != 0) { + ridx = fti->fti_flags & FTI_FLOOR ? rate_to_ridx_floor(rate) : + rate_to_ridx_ceil(rate); + } else + ridx = fti->fti_flags & FTI_FLOOR ? 0 : fti->nrates - 1; + + return (ridx); +} + +static int +alloc_flow_throttler(void **pstp, u_int rate) +{ + struct so_throttle_params *stp; + int rc; + vmem_addr_t fid; + + mtx_lock(&fti->fti_lock); + if ((fti->fti_flags & FTI_ACTIVATED) == 0) { + rc = ENXIO; + goto done; + } + + rc = vmem_alloc(fti->fid_arena, 1, M_FIRSTFIT | M_NOWAIT, &fid); + if (rc != 0) + goto done; + + stp = malloc(sizeof(*stp), M_FLOWTHROTTLE, M_ZERO | M_NOWAIT); + if (stp == NULL) { + vmem_free(fti->fid_arena, fid, 1); + rc = ENOMEM; + goto done; + } + + stp->magic = SO_FT_MAGIC; + stp->requested_rate = rate; + stp->fid = (u_int)fid; + stp->ridx = requested_rate_to_ridx(rate); + *pstp = stp; + + if (++fti->fids_in_use > fti->fids_peak) + fti->fids_peak = fti->fids_in_use; +done: + if (rc != 0) + fti->fid_alloc_failed++; + mtx_unlock(&fti->fti_lock); + return (rc); +} + +static void +free_flow_throttler(struct so_throttle_params *stp) +{ + + KASSERT(stp->magic == SO_FT_MAGIC, ("%s: bad magic", __func__)); + + mtx_lock(&fti->fti_lock); + KASSERT(fti->fti_flags & FTI_ACTIVATED, + ("%s: flow throttling not activated but fid %u in use.", __func__, + stp->fid)); + vmem_free(fti->fid_arena, (vmem_addr_t)stp->fid, 1); + fti->fids_in_use--; + mtx_unlock(&fti->fti_lock); + free(stp, M_FLOWTHROTTLE); +} + +static int +so_has_throttle_params(struct socket *so) +{ + u_int *u = so->so_emuldata; + uintptr_t p = (uintptr_t)so->so_emuldata; + +#ifdef INKERNEL + if (INKERNEL(p) && *u == SO_FT_MAGIC) + return (1); +#else + if (p && *u == SO_FT_MAGIC) + return (1); +#endif + + return (0); +} + +/* + * The u_int passed in is the rate in Kbps. 0 means the kernel is allowed to + * select and switch between different rates. + */ +int +do_setopt_tx_throttle(struct socket *so, struct sockopt *sopt) +{ + int rc; + u_int rate; + struct so_throttle_params *stp; + + rc = sooptcopyin(sopt, &rate, sizeof rate, sizeof rate); + if (rc != 0) + return (rc); + + /* + * XXX: Locking so_snd for access to so->so_emuldata is not consistent + * with _other_ users of so_emuldata. This is (temporarily) ok since + * this initial implementation of TX_THROTTLE isn't meant to coexist + * with such users. I think the right place to have so_throttle_params + * would be off a pointer in struct sockbuf. + */ + SOCKBUF_LOCK(&so->so_snd); + if (so_has_throttle_params(so)) { + stp = so->so_emuldata; + if (rate == INVALID_FLOW_RATE) { + free_flow_throttler(stp); + so->so_emuldata = NULL; + } else if (rate != stp->requested_rate) { + /* Change to the rate */ + mtx_lock(&fti->fti_lock); + stp->ridx = requested_rate_to_ridx(rate); + stp->requested_rate = rate; + mtx_unlock(&fti->fti_lock); + } else { + /* setsockopt with same parameters, ignore */ + } + } else if (so->so_emuldata == NULL) + rc = alloc_flow_throttler(&so->so_emuldata, rate); + else { + /* so_emuldata in use by someone else, leave alone */ + rc = EBUSY; + } + SOCKBUF_UNLOCK(&so->so_snd); + return (rc); +} + +int +do_getopt_tx_throttle(struct socket *so, struct sockopt *sopt) +{ + int rc; + u_int rate; + struct so_throttle_params *stp; + + SOCKBUF_LOCK(&so->so_snd); + if (!so_has_throttle_params(so)) { + rc = EINVAL; + goto done; + } + stp = so->so_emuldata; + + mtx_lock(&fti->fti_lock); + KASSERT(fti->fti_flags & FTI_ACTIVATED, + ("%s: flow throttling not activated but fid %u in use.", __func__, + stp->fid)); + rate = fti->flow_rate[stp->ridx]; + mtx_unlock(&fti->fti_lock); + rc = 0; +done: + SOCKBUF_UNLOCK(&so->so_snd); + if (rc == 0) + rc = sooptcopyout(sopt, &rate, sizeof(rate)); + return (rc); +} + +void +sofree_flow_throttler(struct socket *so) +{ + + SOCKBUF_LOCK(&so->so_snd); + if (so_has_throttle_params(so)) { + KASSERT(fti->fti_flags & FTI_ACTIVATED, + ("%s: flow throttling not activated but socket %p has " + "throttle parameters %p", __func__, so, so->so_emuldata)); + + free_flow_throttler(so->so_emuldata); + so->so_emuldata = NULL; + } + SOCKBUF_UNLOCK(&so->so_snd); +} + +void +add_flow_throttle_tag(struct mbuf *m, struct socket *so) +{ + struct so_throttle_params *stp; + struct flowthrottle_tag *ftag; + + M_ASSERTPKTHDR(m); + SOCKBUF_LOCK_ASSERT(&so->so_snd); + + if (!so_has_throttle_params(so)) + return; + + ftag = (void *)m_tag_alloc(MTAG_FT_COOKIE, MTAG_FT_TYPEID, + sizeof(*ftag), M_NOWAIT); + if (ftag == NULL) + return; + + stp = so->so_emuldata; + ftag->fid = stp->fid; + ftag->ridx = stp->ridx; + + m_tag_prepend(m, &ftag->tag); +} + +void +set_tx_throttle(struct socket *lso, struct socket *so) +{ + int rc; + struct so_throttle_params *stp; + + if (!so_has_throttle_params(lso)) + return; + + stp = lso->so_emuldata; + SOCKBUF_LOCK(&so->so_snd); + rc = alloc_flow_throttler(&so->so_emuldata, stp->requested_rate); + SOCKBUF_UNLOCK(&so->so_snd); + if (rc != 0) { + log(LOG_ERR, "%s: error %u while setting rate %uKbps " + "on child %p of socket %p\n", __func__, rc, + stp->requested_rate, so, lso); + } +} diff -r 62d4c2f971fb -r ccca6c93ab9b sys/net/flow_throttle.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/net/flow_throttle.h Thu May 29 14:02:21 2014 -0700 @@ -0,0 +1,81 @@ +/*- + * Copyright (c) 2013 Chelsio Communications, Inc. + * All rights reserved. + * Written by: Navdeep Parhar + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef _NET_FLOW_THROTTLE_H +#define _NET_FLOW_THROTTLE_H +#include + +#define INVALID_FLOW_RATE ((u_int)-1) + +#define MTAG_FT_COOKIE 20140401 +#define MTAG_FT_TYPEID 1 + +struct flowthrottle_tag { + struct m_tag tag; + u_int fid; + u_int ridx; + + uint8_t l2len; + uint8_t l4len; + uint16_t l3len; + uint8_t tsoff; /* in units of 4B relative to start of TCP hdr */ +}; + +struct socket; +struct sockopt; + +struct flow_throttling_provider { + STAILQ_ENTRY(flow_throttling_provider) link; + + const char *name; + void *cookie; /* opaque cookie */ + u_int nflows; /* max number of flows that can be throttled */ + u_int nrates; /* number of rates supported */ + u_int min_rate; /* minimum rate supported (Kbps) */ + u_int max_rate; /* maximum rate supported (Kbps) */ + + int (*set_throttle_rate)(void *, u_int, u_int); +}; + +struct so_throttle_params { + u_int magic; /* temporary, to detect so_emuldata clash */ + u_int requested_rate; /* rate requested, in Kbps. 0 means auto */ + u_int fid; /* unique flow identifier for the socket */ + u_int ridx; /* actual index into the global rate array */ +}; + +int register_flow_throttler(struct flow_throttling_provider *); +u_int ridx_to_throttle_rate(u_int); +int do_setopt_tx_throttle(struct socket *, struct sockopt *); +int do_getopt_tx_throttle(struct socket *, struct sockopt *); +void sofree_flow_throttler(struct socket *); +void add_flow_throttle_tag(struct mbuf *, struct socket *); +void set_tx_throttle(struct socket *, struct socket *); +#endif diff -r 62d4c2f971fb -r ccca6c93ab9b sys/netinet/tcp_output.c --- a/sys/netinet/tcp_output.c Thu May 29 14:02:00 2014 -0700 +++ b/sys/netinet/tcp_output.c Thu May 29 14:02:21 2014 -0700 @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -861,6 +862,7 @@ send: m->m_data += max_linkhdr; m->m_len = hdrlen; + add_flow_throttle_tag(m, so); /* * Start the m_copy functions from the closest mbuf diff -r 62d4c2f971fb -r ccca6c93ab9b sys/sys/socket.h --- a/sys/sys/socket.h Thu May 29 14:02:00 2014 -0700 +++ b/sys/sys/socket.h Thu May 29 14:02:21 2014 -0700 @@ -158,6 +158,7 @@ typedef __uintptr_t uintptr_t; #define SO_USER_COOKIE 0x1015 /* user cookie (dummynet etc.) */ #define SO_PROTOCOL 0x1016 /* get socket protocol (Linux name) */ #define SO_PROTOTYPE SO_PROTOCOL /* alias for SO_PROTOCOL (SunOS name) */ +#define SO_TX_THROTTLE 0x1017 /* allow tx to be throttled */ #endif /*