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:
Vincenzo Maffione 2021-03-29 16:38:37 +00:00
parent 660a47cb99
commit f8113f0a65
4 changed files with 144 additions and 15 deletions

View File

@ -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

View File

@ -47,6 +47,7 @@
static void
nmctx_default_error(struct nmctx *ctx, const char *errmsg)
{
(void)ctx;
fprintf(stderr, "%s\n", errmsg);
}

View File

@ -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;

View File

@ -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";
}