aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/netmap/netmap.c
diff options
context:
space:
mode:
authorVincenzo Maffione <vmaffione@FreeBSD.org>2021-03-29 16:22:48 +0000
committerVincenzo Maffione <vmaffione@FreeBSD.org>2021-03-29 16:29:01 +0000
commita6d768d845c173823785c71bb18b40074e7a8998 (patch)
tree6a110e4d72a883f4a96c55e3e5681393f170fbcb /sys/dev/netmap/netmap.c
parent9d81dd5404b3ad7108059d7065814d56a722a96c (diff)
netmap: add kernel support for the "offsets" feature
This feature enables applications to ask netmap to transmit or receive packets starting at a user-specified offset from the beginning of the netmap buffer. This is meant to ease those packet manipulation operations such as pushing or popping packet headers, that may be useful to implement software switches, routers and other packet processors. To use the feature, drivers (e.g., iflib, vtnet, etc.) must have explicit support. This change does not add support for any driver, but introduces the necessary kernel changes. However, offsets support is already included for VALE ports and pipes.
Diffstat (limited to 'sys/dev/netmap/netmap.c')
-rw-r--r--sys/dev/netmap/netmap.c258
1 files changed, 240 insertions, 18 deletions
diff --git a/sys/dev/netmap/netmap.c b/sys/dev/netmap/netmap.c
index f9698096b47a..18de5ef205eb 100644
--- a/sys/dev/netmap/netmap.c
+++ b/sys/dev/netmap/netmap.c
@@ -805,6 +805,14 @@ netmap_update_config(struct netmap_adapter *na)
static int netmap_txsync_to_host(struct netmap_kring *kring, int flags);
static int netmap_rxsync_from_host(struct netmap_kring *kring, int flags);
+static int
+netmap_default_bufcfg(struct netmap_kring *kring, uint64_t target)
+{
+ kring->hwbuf_len = target;
+ kring->buf_align = 0; /* no alignment */
+ return 0;
+}
+
/* create the krings array and initialize the fields common to all adapters.
* The array layout is this:
*
@@ -885,12 +893,16 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
kring->nr_pending_mode = NKR_NETMAP_OFF;
if (i < nma_get_nrings(na, t)) {
kring->nm_sync = (t == NR_TX ? na->nm_txsync : na->nm_rxsync);
+ kring->nm_bufcfg = na->nm_bufcfg;
+ if (kring->nm_bufcfg == NULL)
+ kring->nm_bufcfg = netmap_default_bufcfg;
} else {
if (!(na->na_flags & NAF_HOST_RINGS))
kring->nr_kflags |= NKR_FAKERING;
kring->nm_sync = (t == NR_TX ?
netmap_txsync_to_host:
netmap_rxsync_from_host);
+ kring->nm_bufcfg = netmap_default_bufcfg;
}
kring->nm_notify = na->nm_notify;
kring->rhead = kring->rcur = kring->nr_hwcur = 0;
@@ -969,17 +981,24 @@ netmap_hw_krings_delete(struct netmap_adapter *na)
netmap_krings_delete(na);
}
+void
+netmap_mem_restore(struct netmap_adapter *na)
+{
+ if (na->nm_mem_prev) {
+ netmap_mem_put(na->nm_mem);
+ na->nm_mem = na->nm_mem_prev;
+ na->nm_mem_prev = NULL;
+ }
+}
+
static void
netmap_mem_drop(struct netmap_adapter *na)
{
- int last = netmap_mem_deref(na->nm_mem, na);
/* if the native allocator had been overrided on regif,
* restore it now and drop the temporary one
*/
- if (last && na->nm_mem_prev) {
- netmap_mem_put(na->nm_mem);
- na->nm_mem = na->nm_mem_prev;
- na->nm_mem_prev = NULL;
+ if (netmap_mem_deref(na->nm_mem, na)) {
+ netmap_mem_restore(na);
}
}
@@ -1571,7 +1590,7 @@ netmap_get_na(struct nmreq_header *hdr,
if (error || *na != NULL)
goto out;
- /* try to see if this is a bridge port */
+ /* try to see if this is a vale port */
error = netmap_get_vale_na(hdr, na, nmd, create);
if (error)
goto out;
@@ -2232,6 +2251,198 @@ netmap_buf_size_validate(const struct netmap_adapter *na, unsigned mtu) {
return 0;
}
+/* Handle the offset option, if present in the hdr.
+ * Returns 0 on success, or an error.
+ */
+static int
+netmap_offsets_init(struct netmap_priv_d *priv, struct nmreq_header *hdr)
+{
+ struct nmreq_opt_offsets *opt;
+ struct netmap_adapter *na = priv->np_na;
+ struct netmap_kring *kring;
+ uint64_t mask = 0, bits = 0, maxbits = sizeof(uint64_t) * 8,
+ max_offset = 0, initial_offset = 0, min_gap = 0;
+ u_int i;
+ enum txrx t;
+ int error = 0;
+
+ opt = (struct nmreq_opt_offsets *)
+ nmreq_getoption(hdr, NETMAP_REQ_OPT_OFFSETS);
+ if (opt == NULL)
+ return 0;
+
+ if (!(na->na_flags & NAF_OFFSETS)) {
+ if (netmap_verbose)
+ nm_prerr("%s does not support offsets",
+ na->name);
+ error = EOPNOTSUPP;
+ goto out;
+ }
+
+ /* check sanity of the opt values */
+ max_offset = opt->nro_max_offset;
+ min_gap = opt->nro_min_gap;
+ initial_offset = opt->nro_initial_offset;
+ bits = opt->nro_offset_bits;
+
+ if (bits > maxbits) {
+ if (netmap_verbose)
+ nm_prerr("bits: %llu too large (max %llu)",
+ (unsigned long long)bits,
+ (unsigned long long)maxbits);
+ error = EINVAL;
+ goto out;
+ }
+ /* we take bits == 0 as a request to use the entire field */
+ if (bits == 0 || bits == maxbits) {
+ /* shifting a type by sizeof(type) is undefined */
+ bits = maxbits;
+ mask = 0xffffffffffffffff;
+ } else {
+ mask = (1ULL << bits) - 1;
+ }
+ if (max_offset > NETMAP_BUF_SIZE(na)) {
+ if (netmap_verbose)
+ nm_prerr("max offset %llu > buf size %u",
+ (unsigned long long)max_offset, NETMAP_BUF_SIZE(na));
+ error = EINVAL;
+ goto out;
+ }
+ if ((max_offset & mask) != max_offset) {
+ if (netmap_verbose)
+ nm_prerr("max offset %llu to large for %llu bits",
+ (unsigned long long)max_offset,
+ (unsigned long long)bits);
+ error = EINVAL;
+ goto out;
+ }
+ if (initial_offset > max_offset) {
+ if (netmap_verbose)
+ nm_prerr("initial offset %llu > max offset %llu",
+ (unsigned long long)initial_offset,
+ (unsigned long long)max_offset);
+ error = EINVAL;
+ goto out;
+ }
+
+ /* initialize the kring and ring fields. */
+ foreach_selected_ring(priv, t, i, kring) {
+ struct netmap_kring *kring = NMR(na, t)[i];
+ struct netmap_ring *ring = kring->ring;
+ u_int j;
+
+ /* it the ring is already in use we check that the
+ * new request is compatible with the existing one
+ */
+ if (kring->offset_mask) {
+ if ((kring->offset_mask & mask) != mask ||
+ kring->offset_max < max_offset) {
+ if (netmap_verbose)
+ nm_prinf("%s: cannot increase"
+ "offset mask and/or max"
+ "(current: mask=%llx,max=%llu",
+ kring->name,
+ (unsigned long long)kring->offset_mask,
+ (unsigned long long)kring->offset_max);
+ error = EBUSY;
+ goto out;
+ }
+ mask = kring->offset_mask;
+ max_offset = kring->offset_max;
+ } else {
+ kring->offset_mask = mask;
+ *(uint64_t *)(uintptr_t)&ring->offset_mask = mask;
+ kring->offset_max = max_offset;
+ kring->offset_gap = min_gap;
+ }
+
+ /* if there is an initial offset, put it into
+ * all the slots
+ *
+ * Note: we cannot change the offsets if the
+ * ring is already in use.
+ */
+ if (!initial_offset || kring->users > 1)
+ continue;
+
+ for (j = 0; j < kring->nkr_num_slots; j++) {
+ struct netmap_slot *slot = ring->slot + j;
+
+ nm_write_offset(kring, slot, initial_offset);
+ }
+ }
+
+out:
+ opt->nro_opt.nro_status = error;
+ if (!error) {
+ opt->nro_max_offset = max_offset;
+ }
+ return error;
+
+}
+
+static int
+netmap_compute_buf_len(struct netmap_priv_d *priv)
+{
+ enum txrx t;
+ u_int i;
+ struct netmap_kring *kring;
+ int error = 0;
+ unsigned mtu = 0;
+ struct netmap_adapter *na = priv->np_na;
+ uint64_t target, maxframe;
+
+ if (na->ifp != NULL)
+ mtu = nm_os_ifnet_mtu(na->ifp);
+
+ foreach_selected_ring(priv, t, i, kring) {
+
+ if (kring->users > 1)
+ continue;
+
+ target = NETMAP_BUF_SIZE(kring->na) -
+ kring->offset_max;
+ if (!kring->offset_gap)
+ kring->offset_gap =
+ NETMAP_BUF_SIZE(kring->na);
+ if (kring->offset_gap < target)
+ target = kring->offset_gap;
+
+ if (mtu) {
+ maxframe = mtu + ETH_HLEN +
+ ETH_FCS_LEN + VLAN_HLEN;
+ if (maxframe < target) {
+ target = kring->offset_gap;
+ }
+ }
+
+ error = kring->nm_bufcfg(kring, target);
+ if (error)
+ goto out;
+
+ *(uint64_t *)(uintptr_t)&kring->ring->buf_align = kring->buf_align;
+
+ if (mtu && t == NR_RX && kring->hwbuf_len < mtu) {
+ if (!(na->na_flags & NAF_MOREFRAG)) {
+ nm_prerr("error: large MTU (%d) needed "
+ "but %s does not support "
+ "NS_MOREFRAG", mtu,
+ na->name);
+ error = EINVAL;
+ goto out;
+ } else {
+ nm_prinf("info: netmap application on "
+ "%s needs to support "
+ "NS_MOREFRAG "
+ "(MTU=%u,buf_size=%llu)",
+ kring->name, mtu,
+ (unsigned long long)kring->hwbuf_len);
+ }
+ }
+ }
+out:
+ return error;
+}
/*
* possibly move the interface to netmap-mode.
@@ -2381,6 +2592,16 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
if (error)
goto err_rel_excl;
+ /* initialize offsets if requested */
+ error = netmap_offsets_init(priv, hdr);
+ if (error)
+ goto err_rel_excl;
+
+ /* compute and validate the buf lenghts */
+ error = netmap_compute_buf_len(priv);
+ if (error)
+ goto err_rel_excl;
+
/* in all cases, create a new netmap if */
nifp = netmap_mem_if_new(na, priv);
if (nifp == NULL) {
@@ -2713,17 +2934,12 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
}
#ifdef WITH_VALE
case NETMAP_REQ_VALE_ATTACH: {
- error = netmap_vale_attach(hdr, NULL /* userspace request */);
+ error = netmap_bdg_attach(hdr, NULL /* userspace request */);
break;
}
case NETMAP_REQ_VALE_DETACH: {
- error = netmap_vale_detach(hdr, NULL /* userspace request */);
- break;
- }
-
- case NETMAP_REQ_VALE_LIST: {
- error = netmap_vale_list(hdr);
+ error = netmap_bdg_detach(hdr, NULL /* userspace request */);
break;
}
@@ -2795,6 +3011,11 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
break;
}
+ case NETMAP_REQ_VALE_LIST: {
+ error = netmap_vale_list(hdr);
+ break;
+ }
+
case NETMAP_REQ_VALE_NEWIF: {
error = nm_vi_create(hdr);
break;
@@ -2804,13 +3025,13 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
error = nm_vi_destroy(hdr->nr_name);
break;
}
+#endif /* WITH_VALE */
case NETMAP_REQ_VALE_POLLING_ENABLE:
case NETMAP_REQ_VALE_POLLING_DISABLE: {
error = nm_bdg_polling(hdr);
break;
}
-#endif /* WITH_VALE */
case NETMAP_REQ_POOLS_INFO_GET: {
/* Get information from the memory allocator used for
* hdr->nr_name. */
@@ -3029,6 +3250,9 @@ nmreq_opt_size_by_type(uint32_t nro_reqtype, uint64_t nro_size)
case NETMAP_REQ_OPT_SYNC_KLOOP_MODE:
rv = sizeof(struct nmreq_opt_sync_kloop_mode);
break;
+ case NETMAP_REQ_OPT_OFFSETS:
+ rv = sizeof(struct nmreq_opt_offsets);
+ break;
}
/* subtract the common header */
return rv - sizeof(struct nmreq_option);
@@ -3733,16 +3957,14 @@ netmap_attach_common(struct netmap_adapter *na)
na->active_fds = 0;
if (na->nm_mem == NULL) {
- /* use the global allocator */
- na->nm_mem = netmap_mem_get(&nm_mem);
+ /* use iommu or global allocator */
+ na->nm_mem = netmap_mem_get_iommu(na);
}
-#ifdef WITH_VALE
if (na->nm_bdg_attach == NULL)
/* no special nm_bdg_attach callback. On VALE
* attach, we need to interpose a bwrap
*/
na->nm_bdg_attach = netmap_default_bdg_attach;
-#endif
return 0;
}