Changeset View
Changeset View
Standalone View
Standalone View
sys/netgraph/ng_car.c
/*- | /*- | ||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | ||||
* | * | ||||
* Copyright (c) 2005 Nuno Antunes <nuno.antunes@gmail.com> | * Copyright (c) 2005 Nuno Antunes <nuno.antunes@gmail.com> | ||||
* Copyright (c) 2007 Alexander Motin <mav@freebsd.org> | * Copyright (c) 2007 Alexander Motin <mav@freebsd.org> | ||||
* Copyright (c) 2019 Lutz Donnerhacke <lutz@donnerhacke.de> | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
Show All 15 Lines | |||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
/* | /* | ||||
* ng_car - An implementation of committed access rate for netgraph | * ng_car - An implementation of committed access rate for netgraph | ||||
* | * | ||||
* TODO: | * TODO: | ||||
* - Sanitize input config values (impose some limits) | * - Sanitize input config values (impose some limits) | ||||
* - Implement internal packet painting (possibly using mbuf tags) | |||||
* - Implement color-aware mode | |||||
* - Implement DSCP marking for IPv4 | * - Implement DSCP marking for IPv4 | ||||
* - Decouple functionality into a simple classifier (g/y/r) | |||||
* and various action nodes (i.e. shape, dcsp, pcp) | |||||
*/ | */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <netgraph/ng_message.h> | #include <netgraph/ng_message.h> | ||||
#include <netgraph/ng_parse.h> | #include <netgraph/ng_parse.h> | ||||
#include <netgraph/netgraph.h> | #include <netgraph/netgraph.h> | ||||
#include <netgraph/ng_car.h> | #include <netgraph/ng_car.h> | ||||
#include "qos.h" | |||||
#define NG_CAR_QUEUE_SIZE 100 /* Maximum queue size for SHAPE mode */ | #define NG_CAR_QUEUE_SIZE 100 /* Maximum queue size for SHAPE mode */ | ||||
#define NG_CAR_QUEUE_MIN_TH 8 /* Minimum RED threshold for SHAPE mode */ | #define NG_CAR_QUEUE_MIN_TH 8 /* Minimum RED threshold for SHAPE mode */ | ||||
/* Hook private info */ | /* Hook private info */ | ||||
struct hookinfo { | struct hookinfo { | ||||
hook_p hook; /* this (source) hook */ | hook_p hook; /* this (source) hook */ | ||||
hook_p dest; /* destination hook */ | hook_p dest; /* destination hook */ | ||||
▲ Show 20 Lines • Show All 195 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Data has arrived. | * Data has arrived. | ||||
*/ | */ | ||||
static int | static int | ||||
ng_car_rcvdata(hook_p hook, item_p item ) | ng_car_rcvdata(hook_p hook, item_p item ) | ||||
{ | { | ||||
struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook); | struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook); | ||||
struct mbuf *m; | struct mbuf *m; | ||||
struct m_qos_color *colp; | |||||
enum qos_color col; | |||||
int error = 0; | int error = 0; | ||||
u_int len; | u_int len; | ||||
/* If queue is not empty now then enqueue packet. */ | /* If queue is not empty now then enqueue packet. */ | ||||
if (hinfo->q_first != hinfo->q_last) { | if (hinfo->q_first != hinfo->q_last) { | ||||
ng_car_enqueue(hinfo, item); | ng_car_enqueue(hinfo, item); | ||||
return (0); | return (0); | ||||
} | } | ||||
m = NGI_M(item); | m = NGI_M(item); | ||||
#define NG_CAR_PERFORM_MATCH_ACTION(a) \ | #define NG_CAR_PERFORM_MATCH_ACTION(a,col) \ | ||||
melifaro: Can this be an inline function? | |||||
Done Inline ActionsThat would be a good idea, but will require considerable refactoring of the code. Due to access various locally scoped variables, the interface for such a function is much larger and harder to extend, than the current macro. I'd like to keep the patch small at this stage. May I keep the macro? donner: That would be a good idea, but will require considerable refactoring of the code. Due to access… | |||||
do { \ | do { \ | ||||
switch (a) { \ | switch (a) { \ | ||||
case NG_CAR_ACTION_FORWARD: \ | case NG_CAR_ACTION_FORWARD: \ | ||||
/* Do nothing. */ \ | /* Do nothing. */ \ | ||||
break; \ | break; \ | ||||
case NG_CAR_ACTION_MARK: \ | case NG_CAR_ACTION_MARK: \ | ||||
/* XXX find a way to mark packets (mbuf tag?) */ \ | if (colp == NULL) { \ | ||||
++hinfo->stats.errors; \ | colp = (void *)m_tag_alloc( \ | ||||
Done Inline ActionsDo we need to alloc tag for the default color? melifaro: Do we need to alloc tag for the default color? | |||||
Done Inline ActionsYes the mark action should always generate a tag. The whole idea of marking is to make the decision externally visible to other consumers. So the defaults of this module need to be made explicit. Yes, there are other types of customers (like ng_tag) in our current setup. donner: Yes the mark action should always generate a tag. The whole idea of marking is to make the… | |||||
M_QOS_COOKIE, M_QOS_COLOR, \ | |||||
MTAG_SIZE(m_qos_color), M_NOWAIT); \ | |||||
if (colp != NULL) \ | |||||
m_tag_prepend(m, &colp->tag); \ | |||||
} \ | |||||
if (colp != NULL) \ | |||||
colp->color = col; \ | |||||
break; \ | break; \ | ||||
case NG_CAR_ACTION_DROP: \ | case NG_CAR_ACTION_DROP: \ | ||||
default: \ | default: \ | ||||
/* Drop packet and return. */ \ | /* Drop packet and return. */ \ | ||||
NG_FREE_ITEM(item); \ | NG_FREE_ITEM(item); \ | ||||
++hinfo->stats.dropped_pkts; \ | ++hinfo->stats.dropped_pkts; \ | ||||
return (0); \ | return (0); \ | ||||
} \ | } \ | ||||
} while (0) | } while (0) | ||||
/* Packet is counted as 128 tokens for better resolution */ | /* Packet is counted as 128 tokens for better resolution */ | ||||
if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) { | if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) { | ||||
len = 128; | len = 128; | ||||
} else { | } else { | ||||
len = m->m_pkthdr.len; | len = m->m_pkthdr.len; | ||||
} | } | ||||
/* Determine current color of the packet (default green) */ | |||||
colp = (void *)m_tag_locate(m, M_QOS_COOKIE, M_QOS_COLOR, NULL); | |||||
if ((hinfo->conf.opt & NG_CAR_COLOR_AWARE) && (colp != NULL)) | |||||
col = colp->color; | |||||
else | |||||
col = QOS_COLOR_GREEN; | |||||
/* Check committed token bucket. */ | /* Check committed token bucket. */ | ||||
if (hinfo->tc - len >= 0) { | if (hinfo->tc - len >= 0 && col <= QOS_COLOR_GREEN) { | ||||
/* This packet is green. */ | /* This packet is green. */ | ||||
++hinfo->stats.green_pkts; | ++hinfo->stats.green_pkts; | ||||
hinfo->tc -= len; | hinfo->tc -= len; | ||||
NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action); | NG_CAR_PERFORM_MATCH_ACTION( | ||||
hinfo->conf.green_action, | |||||
QOS_COLOR_GREEN); | |||||
} else { | } else { | ||||
/* Refill only if not green without it. */ | /* Refill only if not green without it. */ | ||||
ng_car_refillhook(hinfo); | ng_car_refillhook(hinfo); | ||||
/* Check committed token bucket again after refill. */ | /* Check committed token bucket again after refill. */ | ||||
if (hinfo->tc - len >= 0) { | if (hinfo->tc - len >= 0 && col <= QOS_COLOR_GREEN) { | ||||
/* This packet is green */ | /* This packet is green */ | ||||
++hinfo->stats.green_pkts; | ++hinfo->stats.green_pkts; | ||||
hinfo->tc -= len; | hinfo->tc -= len; | ||||
NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action); | NG_CAR_PERFORM_MATCH_ACTION( | ||||
hinfo->conf.green_action, | |||||
QOS_COLOR_GREEN); | |||||
/* If not green and mode is SHAPE, enqueue packet. */ | /* If not green and mode is SHAPE, enqueue packet. */ | ||||
} else if (hinfo->conf.mode == NG_CAR_SHAPE) { | } else if (hinfo->conf.mode == NG_CAR_SHAPE) { | ||||
ng_car_enqueue(hinfo, item); | ng_car_enqueue(hinfo, item); | ||||
return (0); | return (0); | ||||
/* If not green and mode is RED, calculate probability. */ | /* If not green and mode is RED, calculate probability. */ | ||||
} else if (hinfo->conf.mode == NG_CAR_RED) { | } else if (hinfo->conf.mode == NG_CAR_RED) { | ||||
/* Is packet is bigger then extended burst? */ | /* Is packet is bigger then extended burst? */ | ||||
if (len - (hinfo->tc - len) > hinfo->conf.ebs) { | if (len - (hinfo->tc - len) > hinfo->conf.ebs || | ||||
col >= QOS_COLOR_RED) { | |||||
/* This packet is definitely red. */ | /* This packet is definitely red. */ | ||||
++hinfo->stats.red_pkts; | ++hinfo->stats.red_pkts; | ||||
hinfo->te = 0; | hinfo->te = 0; | ||||
NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); | NG_CAR_PERFORM_MATCH_ACTION( | ||||
hinfo->conf.red_action, | |||||
QOS_COLOR_RED); | |||||
/* Use token bucket to simulate RED-like drop | /* Use token bucket to simulate RED-like drop | ||||
probability. */ | probability. */ | ||||
} else if (hinfo->te + (len - hinfo->tc) < | } else if (hinfo->te + (len - hinfo->tc) < hinfo->conf.ebs && | ||||
hinfo->conf.ebs) { | col <= QOS_COLOR_YELLOW) { | ||||
/* This packet is yellow */ | /* This packet is yellow */ | ||||
++hinfo->stats.yellow_pkts; | ++hinfo->stats.yellow_pkts; | ||||
hinfo->te += len - hinfo->tc; | hinfo->te += len - hinfo->tc; | ||||
/* Go to negative tokens. */ | /* Go to negative tokens. */ | ||||
hinfo->tc -= len; | hinfo->tc -= len; | ||||
NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action); | NG_CAR_PERFORM_MATCH_ACTION( | ||||
hinfo->conf.yellow_action, | |||||
QOS_COLOR_YELLOW); | |||||
} else { | } else { | ||||
/* This packet is probably red. */ | /* This packet is probably red. */ | ||||
++hinfo->stats.red_pkts; | ++hinfo->stats.red_pkts; | ||||
hinfo->te = 0; | hinfo->te = 0; | ||||
NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); | NG_CAR_PERFORM_MATCH_ACTION( | ||||
hinfo->conf.red_action, | |||||
QOS_COLOR_RED); | |||||
} | } | ||||
/* If not green and mode is SINGLE/DOUBLE RATE. */ | /* If not green and mode is SINGLE/DOUBLE RATE. */ | ||||
} else { | } else { | ||||
/* Check extended token bucket. */ | /* Check extended token bucket. */ | ||||
if (hinfo->te - len >= 0) { | if (hinfo->te - len >= 0 && col <= QOS_COLOR_YELLOW) { | ||||
/* This packet is yellow */ | /* This packet is yellow */ | ||||
++hinfo->stats.yellow_pkts; | ++hinfo->stats.yellow_pkts; | ||||
hinfo->te -= len; | hinfo->te -= len; | ||||
NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action); | NG_CAR_PERFORM_MATCH_ACTION( | ||||
hinfo->conf.yellow_action, | |||||
QOS_COLOR_YELLOW); | |||||
} else { | } else { | ||||
/* This packet is red */ | /* This packet is red */ | ||||
++hinfo->stats.red_pkts; | ++hinfo->stats.red_pkts; | ||||
NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); | NG_CAR_PERFORM_MATCH_ACTION( | ||||
hinfo->conf.red_action, | |||||
QOS_COLOR_RED); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
#undef NG_CAR_PERFORM_MATCH_ACTION | #undef NG_CAR_PERFORM_MATCH_ACTION | ||||
NG_FWD_ITEM_HOOK(error, item, hinfo->dest); | NG_FWD_ITEM_HOOK(error, item, hinfo->dest); | ||||
if (error != 0) | if (error != 0) | ||||
▲ Show 20 Lines • Show All 337 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* Enqueue packet. | * Enqueue packet. | ||||
*/ | */ | ||||
static void | static void | ||||
ng_car_enqueue(struct hookinfo *hinfo, item_p item) | ng_car_enqueue(struct hookinfo *hinfo, item_p item) | ||||
{ | { | ||||
struct mbuf *m; | struct mbuf *m; | ||||
int len; | int len; | ||||
struct m_qos_color *colp; | |||||
enum qos_color col; | |||||
NGI_GET_M(item, m); | NGI_GET_M(item, m); | ||||
NG_FREE_ITEM(item); | NG_FREE_ITEM(item); | ||||
/* Determine current color of the packet (default green) */ | |||||
colp = (void *)m_tag_locate(m, M_QOS_COOKIE, M_QOS_COLOR, NULL); | |||||
if ((hinfo->conf.opt & NG_CAR_COLOR_AWARE) && (colp != NULL)) | |||||
col = colp->color; | |||||
else | |||||
col = QOS_COLOR_GREEN; | |||||
/* Lock queue mutex. */ | /* Lock queue mutex. */ | ||||
mtx_lock(&hinfo->q_mtx); | mtx_lock(&hinfo->q_mtx); | ||||
/* Calculate used queue length. */ | /* Calculate used queue length. */ | ||||
len = hinfo->q_last - hinfo->q_first; | len = hinfo->q_last - hinfo->q_first; | ||||
if (len < 0) | if (len < 0) | ||||
len += NG_CAR_QUEUE_SIZE; | len += NG_CAR_QUEUE_SIZE; | ||||
/* If queue is overflowed or we have no RED tokens. */ | /* If queue is overflowed or we have no RED tokens. */ | ||||
if ((len >= (NG_CAR_QUEUE_SIZE - 1)) || | if ((len >= (NG_CAR_QUEUE_SIZE - 1)) || | ||||
(hinfo->te + len >= NG_CAR_QUEUE_SIZE)) { | (hinfo->te + len >= NG_CAR_QUEUE_SIZE) || | ||||
(col >= QOS_COLOR_RED)) { | |||||
/* Drop packet. */ | /* Drop packet. */ | ||||
++hinfo->stats.red_pkts; | ++hinfo->stats.red_pkts; | ||||
++hinfo->stats.dropped_pkts; | ++hinfo->stats.dropped_pkts; | ||||
NG_FREE_M(m); | NG_FREE_M(m); | ||||
hinfo->te = 0; | hinfo->te = 0; | ||||
} else { | } else { | ||||
/* This packet is yellow. */ | /* This packet is yellow. */ | ||||
Show All 28 Lines |
Can this be an inline function?