summaryrefslogtreecommitdiffstats
path: root/sys/netinet
diff options
context:
space:
mode:
authorluigi <luigi@FreeBSD.org>2010-03-11 22:42:33 +0000
committerluigi <luigi@FreeBSD.org>2010-03-11 22:42:33 +0000
commit0d5da117aaa01aa61b32dccc13e04e1f70a12694 (patch)
treee6f1d2b66922a1812a35f7a7370f23e7ef8db336 /sys/netinet
parentafbdfb0b2018300c51fb2c5ad922a48d47f39376 (diff)
downloadFreeBSD-src-0d5da117aaa01aa61b32dccc13e04e1f70a12694.zip
FreeBSD-src-0d5da117aaa01aa61b32dccc13e04e1f70a12694.tar.gz
implement listing of a subset of pipes/queues/schedulers.
The filtering of the output is done in the kernel instead of userland to reduce the amount of data transfered.
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/ipfw/ip_dn_private.h15
-rw-r--r--sys/netinet/ipfw/ip_dummynet.c195
2 files changed, 147 insertions, 63 deletions
diff --git a/sys/netinet/ipfw/ip_dn_private.h b/sys/netinet/ipfw/ip_dn_private.h
index 0f66fef..1e74f08 100644
--- a/sys/netinet/ipfw/ip_dn_private.h
+++ b/sys/netinet/ipfw/ip_dn_private.h
@@ -359,13 +359,24 @@ struct dn_queue *ipdn_q_find(struct dn_fsk *, struct dn_sch_inst *,
struct ipfw_flow_id *);
struct dn_sch_inst *ipdn_si_find(struct dn_schk *, struct ipfw_flow_id *);
-/* helper structure to copy objects returned to userland */
+/*
+ * copy_range is a template for requests for ranges of pipes/queues/scheds.
+ * The number of ranges is variable and can be derived by o.len.
+ * As a default, we use a small number of entries so that the struct
+ * fits easily on the stack and is sufficient for most common requests.
+ */
+#define DEFAULT_RANGES 5
+struct copy_range {
+ struct dn_id o;
+ uint32_t r[ 2 * DEFAULT_RANGES ];
+};
+
struct copy_args {
char **start;
char *end;
int flags;
int type;
- int extra; /* extra filtering */
+ struct copy_range *extra; /* extra filtering */
};
struct sockopt;
diff --git a/sys/netinet/ipfw/ip_dummynet.c b/sys/netinet/ipfw/ip_dummynet.c
index 1c0fc2c..7a63705 100644
--- a/sys/netinet/ipfw/ip_dummynet.c
+++ b/sys/netinet/ipfw/ip_dummynet.c
@@ -787,7 +787,7 @@ copy_obj(char **start, char *end, void *_o, const char *msg, int i)
int have = end - *start;
if (have < o->len || o->len == 0 || o->type == 0) {
- D("ERROR type %d %s %d have %d need %d",
+ D("(WARN) type %d %s %d have %d need %d",
o->type, msg, i, have, o->len);
return 1;
}
@@ -954,43 +954,64 @@ static int
copy_data_helper(void *_o, void *_arg)
{
struct copy_args *a = _arg;
+ uint32_t *r = a->extra->r; /* start of first range */
+ uint32_t *lim; /* first invalid pointer */
+ int n;
- if (a->type == DN_LINK || /* pipe show */
- a->type == DN_SCH) { /* sched show */
- struct dn_schk *s = _o; /* we get only schedulers */
- if (a->type == DN_SCH && s->sch.sched_nr >= DN_MAX_ID)
- return 0; /* not valid scheduler */
- if (a->type == DN_LINK && s->sch.sched_nr <= DN_MAX_ID)
- return 0; /* not valid pipe */
- if (a->flags & DN_C_LINK) {
- if (copy_obj(a->start, a->end, &s->link,
- "link", s->sch.sched_nr))
- return DNHT_SCAN_END;
- if (copy_profile(a, s->profile))
- return DNHT_SCAN_END;
- if (copy_flowset(a, s->fs, 0))
- return DNHT_SCAN_END;
- }
- if (a->flags & DN_C_SCH) {
- if (copy_obj(a->start, a->end, &s->sch,
- "sched", s->sch.sched_nr))
- return DNHT_SCAN_END;
+ lim = (uint32_t *)((char *)(a->extra) + a->extra->o.len);
- /* list all attached flowsets */
- if (copy_fsk_list(a, s, 0))
- return DNHT_SCAN_END;
- }
- if (a->flags & DN_C_FLOW) {
- copy_si(a, s, 0);
+ if (a->type == DN_LINK || a->type == DN_SCH) {
+ /* pipe|sched show, we receive a dn_schk */
+ struct dn_schk *s = _o;
+
+ n = s->sch.sched_nr;
+ if (a->type == DN_SCH && n >= DN_MAX_ID)
+ return 0; /* not a scheduler */
+ if (a->type == DN_LINK && n <= DN_MAX_ID)
+ return 0; /* not a pipe */
+
+ /* see if the object is within one of our ranges */
+ for (;r < lim; r += 2) {
+ if (n < r[0] || n > r[1])
+ continue;
+ /* Found a valid entry, copy and we are done */
+ if (a->flags & DN_C_LINK) {
+ if (copy_obj(a->start, a->end,
+ &s->link, "link", n))
+ return DNHT_SCAN_END;
+ if (copy_profile(a, s->profile))
+ return DNHT_SCAN_END;
+ if (copy_flowset(a, s->fs, 0))
+ return DNHT_SCAN_END;
+ }
+ if (a->flags & DN_C_SCH) {
+ if (copy_obj(a->start, a->end,
+ &s->sch, "sched", n))
+ return DNHT_SCAN_END;
+ /* list all attached flowsets */
+ if (copy_fsk_list(a, s, 0))
+ return DNHT_SCAN_END;
+ }
+ if (a->flags & DN_C_FLOW)
+ copy_si(a, s, 0);
+ break;
}
- }
- if (a->type == DN_FS) { /* queue show, skip internal flowsets */
+ } else if (a->type == DN_FS) {
+ /* queue show, skip internal flowsets */
struct dn_fsk *fs = _o;
- if (fs->fs.fs_nr >= DN_MAX_ID)
+
+ n = fs->fs.fs_nr;
+ if (n >= DN_MAX_ID)
return 0;
- if (copy_flowset(a, fs, 0))
- return DNHT_SCAN_END;
- copy_q(a, fs, 0);
+ /* see if the object is within one of our ranges */
+ for (;r < lim; r += 2) {
+ if (n < r[0] || n > r[1])
+ continue;
+ if (copy_flowset(a, fs, 0))
+ return DNHT_SCAN_END;
+ copy_q(a, fs, 0);
+ break; /* we are done */
+ }
}
return 0;
}
@@ -1690,7 +1711,7 @@ do_config(void *p, int l)
}
static int
-compute_space(struct dn_id *cmd, int *to_copy)
+compute_space(struct dn_id *cmd, struct copy_args *a)
{
int x = 0, need = 0;
int profile_size = sizeof(struct dn_profile) -
@@ -1746,7 +1767,7 @@ compute_space(struct dn_id *cmd, int *to_copy)
need = dn_compat_calc_size(dn_cfg);
break;
}
- *to_copy = x;
+ a->flags = x;
if (x & DN_C_SCH) {
need += dn_cfg.schk_count * sizeof(struct dn_sch) / 2;
/* NOT also, each fs might be attached to a sched */
@@ -1775,61 +1796,105 @@ dummynet_get(struct sockopt *sopt, void **compat)
int have, i, need, error;
char *start = NULL, *buf;
size_t sopt_valsize;
- struct dn_id cmd;
+ struct dn_id *cmd;
struct copy_args a;
+ struct copy_range r;
+ int l = sizeof(struct dn_id);
+
+ bzero(&a, sizeof(a));
+ bzero(&r, sizeof(r));
/* save and restore original sopt_valsize around copyin */
sopt_valsize = sopt->sopt_valsize;
+
+ cmd = &r.o;
+
if (!compat) {
- error = sooptcopyin(sopt, &cmd, sizeof(cmd), sizeof(cmd));
- if (error)
- return error;
+ /* copy at least an oid, and possibly a full object */
+ error = sooptcopyin(sopt, cmd, sizeof(r), sizeof(*cmd));
sopt->sopt_valsize = sopt_valsize;
+ if (error)
+ goto done;
+ l = cmd->len;
#ifdef EMULATE_SYSCTL
/* sysctl emulation. */
- if (cmd.type == DN_SYSCTL_GET)
+ if (cmd->type == DN_SYSCTL_GET)
return kesysctl_emu_get(sopt);
#endif
- } else {
+ if (l > sizeof(r)) {
+ /* request larger than default, allocate buffer */
+ cmd = malloc(l, M_DUMMYNET, M_WAIT);
+ if (cmd == NULL)
+ return ENOMEM; //XXX
+ error = sooptcopyin(sopt, cmd, l, l);
+ sopt->sopt_valsize = sopt_valsize;
+ if (error)
+ goto done;
+ }
+ } else { /* compatibility */
error = 0;
- cmd.type = DN_CMD_GET;
- cmd.len = sizeof(struct dn_id);
- cmd.subtype = DN_GET_COMPAT;
- // cmd.id = sopt_valsize;
+ cmd->type = DN_CMD_GET;
+ cmd->len = sizeof(struct dn_id);
+ cmd->subtype = DN_GET_COMPAT;
+ // cmd->id = sopt_valsize;
D("compatibility mode");
}
+ a.extra = (struct copy_range *)cmd;
+ if (cmd->len == sizeof(*cmd)) { /* no range, create a default */
+ uint32_t *rp = (uint32_t *)(cmd + 1);
+ cmd->len += 2* sizeof(uint32_t);
+ rp[0] = 1;
+ rp[1] = DN_MAX_ID - 1;
+ if (cmd->subtype == DN_LINK) {
+ rp[0] += DN_MAX_ID;
+ rp[1] += DN_MAX_ID;
+ }
+ }
/* Count space (under lock) and allocate (outside lock).
* Exit with lock held if we manage to get enough buffer.
* Try a few times then give up.
*/
for (have = 0, i = 0; i < 10; i++) {
DN_BH_WLOCK();
- need = compute_space(&cmd, &a.flags);
+ need = compute_space(cmd, &a);
+
+ /* if there is a range, ignore value from compute_space() */
+ if (l > sizeof(*cmd))
+ need = sopt_valsize - sizeof(*cmd);
+
if (need < 0) {
DN_BH_WUNLOCK();
- return EINVAL;
+ error = EINVAL;
+ goto done;
}
- need += sizeof(cmd);
- cmd.id = need;
+ need += sizeof(*cmd);
+ cmd->id = need;
if (have >= need)
break;
+
DN_BH_WUNLOCK();
if (start)
free(start, M_DUMMYNET);
start = NULL;
if (need > sopt_valsize)
break;
+
have = need;
start = malloc(have, M_DUMMYNET, M_WAITOK | M_ZERO);
- if (start == NULL)
- return ENOMEM;
+ if (start == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
}
+
if (start == NULL) {
if (compat) {
*compat = NULL;
- return 1; // XXX
+ error = 1; // XXX
+ } else {
+ error = sooptcopyout(sopt, cmd, sizeof(*cmd));
}
- return sooptcopyout(sopt, &cmd, sizeof(cmd));
+ goto done;
}
ND("have %d:%d sched %d, %d:%d links %d, %d:%d flowsets %d, "
"%d:%d si %d, %d:%d queues %d",
@@ -1839,10 +1904,12 @@ dummynet_get(struct sockopt *sopt, void **compat)
dn_cfg.si_count, sizeof(struct dn_flow), DN_SCH_I,
dn_cfg.queue_count, sizeof(struct dn_queue), DN_QUEUE);
sopt->sopt_valsize = sopt_valsize;
- a.type = cmd.subtype;
+ a.type = cmd->subtype;
+
if (compat == NULL) {
- bcopy(&cmd, start, sizeof(cmd));
- buf = start + sizeof(cmd);
+ bcopy(cmd, start, sizeof(*cmd));
+ ((struct dn_id*)(start))->len = sizeof(struct dn_id);
+ buf = start + sizeof(*cmd);
} else
buf = start;
a.start = &buf;
@@ -1853,19 +1920,26 @@ dummynet_get(struct sockopt *sopt, void **compat)
dn_ht_scan(dn_cfg.schedhash, copy_data_helper_compat, &a);
a.type = DN_COMPAT_QUEUE;
dn_ht_scan(dn_cfg.fshash, copy_data_helper_compat, &a);
- } else if (a.type == DN_FS)
+ } else if (a.type == DN_FS) {
dn_ht_scan(dn_cfg.fshash, copy_data_helper, &a);
- else
+ } else {
dn_ht_scan(dn_cfg.schedhash, copy_data_helper, &a);
+ }
DN_BH_WUNLOCK();
+
if (compat) {
*compat = start;
sopt->sopt_valsize = buf - start;
/* free() is done by ip_dummynet_compat() */
+ start = NULL; //XXX hack
} else {
error = sooptcopyout(sopt, start, buf - start);
- free(start, M_DUMMYNET);
}
+done:
+ if (cmd && cmd != &r.o)
+ free(cmd, M_DUMMYNET);
+ if (start)
+ free(start, M_DUMMYNET);
return error;
}
@@ -1945,8 +2019,7 @@ drain_queue_fs_cb(void *_fs, void *arg)
dn_ht_scan_bucket(fs->qht, &fs->drain_bucket,
drain_queue_cb, NULL);
fs->drain_bucket++;
- }
- else {
+ } else {
/* No hash table for this flowset, null the pointer
* if the queue is deleted
*/
OpenPOWER on IntegriCloud