libnetmap: add support for the offset features
The companion libnetmap changes for the "offsets" kernel support added
in a6d768d845
. This includes code to parse the "@offset=NNN"
option that can be appended to the port name by any nmport_* application.
Example:
# pkt-gen -i 'netmap:em0@offset=16'
This commit is contained in:
parent
660a47cb99
commit
f8113f0a65
@ -151,6 +151,22 @@ struct nmem_d;
|
||||
* causing netmap to take the corresponding values from
|
||||
* the priv_{if,ring,buf}_{num,size} sysctls.
|
||||
*
|
||||
* offset (multi-key)
|
||||
* reserve (part of) the ptr fields as an offset field
|
||||
* and write an initial offset into them.
|
||||
*
|
||||
* The keys are:
|
||||
*
|
||||
* bits number of bits of ptr to use
|
||||
* *initial initial offset value
|
||||
*
|
||||
* initial must be assigned. If bits is omitted, it
|
||||
* defaults to the entire ptr field. The max offset is set
|
||||
* at the same value as the initial offset. Note that the
|
||||
* actual values may be increased by the kernel.
|
||||
*
|
||||
* This option is disabled by default (see
|
||||
* nmport_enable_option() below)
|
||||
*/
|
||||
|
||||
|
||||
@ -398,6 +414,47 @@ int nmport_extmem_from_file(struct nmport_d *d, const char *fname);
|
||||
struct nmreq_pools_info* nmport_extmem_getinfo(struct nmport_d *d);
|
||||
|
||||
|
||||
/* nmport_offset - use offsets for this port
|
||||
* @initial the initial offset for all the slots
|
||||
* @maxoff the maximum offset
|
||||
* @bits the number of bits of slot->ptr to use for the offsets
|
||||
* @mingap the minimum gap betwen offsets (in shared buffers)
|
||||
*
|
||||
* With this option the lower @bits bits of the ptr field in the netmap_slot
|
||||
* can be used to specify an offset into the buffer. All offsets will be set
|
||||
* to the @initial value by netmap.
|
||||
*
|
||||
* The offset field can be read and updated using the bitmask found in
|
||||
* ring->offset_mask after a successful register. netmap_user.h contains
|
||||
* some helper macros (NETMAP_ROFFSET, NETMAP_WOFFSET and NETMAP_BUF_OFFSET).
|
||||
*
|
||||
* For RX rings, the user writes the offset o in an empty slot before passing
|
||||
* it to netmap; then, netmap will write the incoming packet at an offset o' >=
|
||||
* o in the buffer. o' may be larger than o because of, e.g., alignment
|
||||
* constrains. If o' > o netmap will also update the offset field in the slot.
|
||||
* Note that large offsets may cause the port to split the packet over several
|
||||
* slots, setting the NS_MOREFRAG flag accordingly.
|
||||
*
|
||||
* For TX rings, the user may prepare the packet to send at an offset o into
|
||||
* the buffer and write o in the offset field. Netmap will send the packets
|
||||
* starting o bytes in the buffer. Note that the address of the packet must
|
||||
* comply with any alignment constraints that the port may have, or the result
|
||||
* will be undefined. The user may read the alignment constraint in the new
|
||||
* ring->buf_align field. It is also possibile that empty slots already come
|
||||
* with a non-zero offset o specified in the offset field. In this case, the
|
||||
* user will have to write the packet at an offset o' >= o.
|
||||
*
|
||||
* The user must also declare the @maxoff offset that she is going to use. Any
|
||||
* offset larger than this will be truncated.
|
||||
*
|
||||
* The user may also declare a @mingap (ignored if zero) if she plans to use
|
||||
* offsets to share the same buffer among several slots. Netmap will guarantee
|
||||
* that it will never write more than @mingap bytes for each slot, irrespective
|
||||
* of the buffer length.
|
||||
*/
|
||||
int nmport_offset(struct nmport_d *d, uint64_t initial, uint64_t maxoff,
|
||||
uint64_t bits, uint64_t mingap);
|
||||
|
||||
/* enable/disable options
|
||||
*
|
||||
* These functions can be used to disable options that the application cannot
|
||||
|
@ -47,6 +47,7 @@
|
||||
static void
|
||||
nmctx_default_error(struct nmctx *ctx, const char *errmsg)
|
||||
{
|
||||
(void)ctx;
|
||||
fprintf(stderr, "%s\n", errmsg);
|
||||
}
|
||||
|
||||
|
@ -178,6 +178,7 @@ struct nmport_extmem_from_file_cleanup_d {
|
||||
void nmport_extmem_from_file_cleanup(struct nmport_cleanup_d *c,
|
||||
struct nmport_d *d)
|
||||
{
|
||||
(void)d;
|
||||
struct nmport_extmem_from_file_cleanup_d *cc =
|
||||
(struct nmport_extmem_from_file_cleanup_d *)c;
|
||||
|
||||
@ -247,6 +248,59 @@ nmport_extmem_getinfo(struct nmport_d *d)
|
||||
return &d->extmem->nro_info;
|
||||
}
|
||||
|
||||
struct nmport_offset_cleanup_d {
|
||||
struct nmport_cleanup_d up;
|
||||
struct nmreq_opt_offsets *opt;
|
||||
};
|
||||
|
||||
static void
|
||||
nmport_offset_cleanup(struct nmport_cleanup_d *c,
|
||||
struct nmport_d *d)
|
||||
{
|
||||
struct nmport_offset_cleanup_d *cc =
|
||||
(struct nmport_offset_cleanup_d *)c;
|
||||
|
||||
nmreq_remove_option(&d->hdr, &cc->opt->nro_opt);
|
||||
nmctx_free(d->ctx, cc->opt);
|
||||
}
|
||||
|
||||
int
|
||||
nmport_offset(struct nmport_d *d, uint64_t initial,
|
||||
uint64_t maxoff, uint64_t bits, uint64_t mingap)
|
||||
{
|
||||
struct nmctx *ctx = d->ctx;
|
||||
struct nmreq_opt_offsets *opt;
|
||||
struct nmport_offset_cleanup_d *clnup = NULL;
|
||||
|
||||
clnup = nmctx_malloc(ctx, sizeof(*clnup));
|
||||
if (clnup == NULL) {
|
||||
nmctx_ferror(ctx, "cannot allocate cleanup descriptor");
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
opt = nmctx_malloc(ctx, sizeof(*opt));
|
||||
if (opt == NULL) {
|
||||
nmctx_ferror(ctx, "%s: cannot allocate offset option", d->hdr.nr_name);
|
||||
nmctx_free(ctx, clnup);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
memset(opt, 0, sizeof(*opt));
|
||||
opt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_OFFSETS;
|
||||
opt->nro_offset_bits = bits;
|
||||
opt->nro_initial_offset = initial;
|
||||
opt->nro_max_offset = maxoff;
|
||||
opt->nro_min_gap = mingap;
|
||||
nmreq_push_option(&d->hdr, &opt->nro_opt);
|
||||
|
||||
clnup->up.cleanup = nmport_offset_cleanup;
|
||||
clnup->opt = opt;
|
||||
nmport_push_cleanup(d, &clnup->up);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* head of the list of options */
|
||||
static struct nmreq_opt_parser *nmport_opt_parsers;
|
||||
|
||||
@ -327,6 +381,9 @@ NPOPT_DECL(conf, 0)
|
||||
NPKEY_DECL(conf, host_rx_rings, 0)
|
||||
NPKEY_DECL(conf, tx_slots, 0)
|
||||
NPKEY_DECL(conf, rx_slots, 0)
|
||||
NPOPT_DECL(offset, NMREQ_OPTF_DISABLED)
|
||||
NPKEY_DECL(offset, initial, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
|
||||
NPKEY_DECL(offset, bits, 0)
|
||||
|
||||
|
||||
static int
|
||||
@ -432,6 +489,23 @@ NPOPT_PARSER(conf)(struct nmreq_parse_ctx *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
NPOPT_PARSER(offset)(struct nmreq_parse_ctx *p)
|
||||
{
|
||||
struct nmport_d *d;
|
||||
uint64_t initial, bits;
|
||||
|
||||
d = p->token;
|
||||
|
||||
initial = atoi(nmport_key(p, offset, initial));
|
||||
bits = 0;
|
||||
if (nmport_key(p, offset, bits) != NULL)
|
||||
bits = atoi(nmport_key(p, offset, bits));
|
||||
|
||||
return nmport_offset(d, initial, initial, bits, 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nmport_disable_option(const char *opt)
|
||||
{
|
||||
@ -586,7 +660,7 @@ nmport_mmap(struct nmport_d *d)
|
||||
struct nmctx *ctx = d->ctx;
|
||||
struct nmem_d *m = NULL;
|
||||
u_int num_tx, num_rx;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
if (d->mmap_done) {
|
||||
errno = EINVAL;
|
||||
@ -643,7 +717,7 @@ nmport_mmap(struct nmport_d *d)
|
||||
num_tx = d->reg.nr_tx_rings + d->nifp->ni_host_tx_rings;
|
||||
for (i = 0; i < num_tx && !d->nifp->ring_ofs[i]; i++)
|
||||
;
|
||||
d->first_tx_ring = i;
|
||||
d->cur_tx_ring = d->first_tx_ring = i;
|
||||
for ( ; i < num_tx && d->nifp->ring_ofs[i]; i++)
|
||||
;
|
||||
d->last_tx_ring = i - 1;
|
||||
@ -651,7 +725,7 @@ nmport_mmap(struct nmport_d *d)
|
||||
num_rx = d->reg.nr_rx_rings + d->nifp->ni_host_rx_rings;
|
||||
for (i = 0; i < num_rx && !d->nifp->ring_ofs[i + num_tx]; i++)
|
||||
;
|
||||
d->first_rx_ring = i;
|
||||
d->cur_rx_ring = d->first_rx_ring = i;
|
||||
for ( ; i < num_rx && d->nifp->ring_ofs[i + num_tx]; i++)
|
||||
;
|
||||
d->last_rx_ring = i - 1;
|
||||
|
@ -603,9 +603,10 @@ nmreq_options_decode(const char *opt, struct nmreq_opt_parser parsers[],
|
||||
struct nmreq_option *
|
||||
nmreq_find_option(struct nmreq_header *h, uint32_t t)
|
||||
{
|
||||
struct nmreq_option *o = NULL;
|
||||
struct nmreq_option *o;
|
||||
|
||||
nmreq_foreach_option(h, o) {
|
||||
for (o = (struct nmreq_option *)h->nr_options; o != NULL;
|
||||
o = (struct nmreq_option *)o->nro_next) {
|
||||
if (o->nro_reqtype == t)
|
||||
break;
|
||||
}
|
||||
@ -632,14 +633,8 @@ nmreq_free_options(struct nmreq_header *h)
|
||||
{
|
||||
struct nmreq_option *o, *next;
|
||||
|
||||
/*
|
||||
* Note: can't use nmreq_foreach_option() here; it frees the
|
||||
* list as it's walking and nmreq_foreach_option() isn't
|
||||
* modification-safe.
|
||||
*/
|
||||
for (o = (struct nmreq_option *)(uintptr_t)h->nr_options; o != NULL;
|
||||
o = next) {
|
||||
next = (struct nmreq_option *)(uintptr_t)o->nro_next;
|
||||
for (o = (struct nmreq_option *)h->nr_options; o != NULL; o = next) {
|
||||
next = (struct nmreq_option *)o->nro_next;
|
||||
free(o);
|
||||
}
|
||||
}
|
||||
@ -656,6 +651,8 @@ nmreq_option_name(uint32_t nro_reqtype)
|
||||
return "csb";
|
||||
case NETMAP_REQ_OPT_SYNC_KLOOP_MODE:
|
||||
return "sync-kloop-mode";
|
||||
case NETMAP_REQ_OPT_OFFSETS:
|
||||
return "offsets";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user