aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/netmap/netmap.c
diff options
context:
space:
mode:
authorVincenzo Maffione <vmaffione@FreeBSD.org>2018-04-12 07:20:50 +0000
committerVincenzo Maffione <vmaffione@FreeBSD.org>2018-04-12 07:20:50 +0000
commit2ff91c175eca50b7d0d9da6b31eae4109c034137 (patch)
tree15a4f8847a8cabd782f67326125c48fed4fdd27b /sys/dev/netmap/netmap.c
parent66def52613043a86172a2ebe6feab214258fa2fa (diff)
Notes
Diffstat (limited to 'sys/dev/netmap/netmap.c')
-rw-r--r--sys/dev/netmap/netmap.c1073
1 files changed, 730 insertions, 343 deletions
diff --git a/sys/dev/netmap/netmap.c b/sys/dev/netmap/netmap.c
index 3c5551bad156..d6230dfb8ebe 100644
--- a/sys/dev/netmap/netmap.c
+++ b/sys/dev/netmap/netmap.c
@@ -262,7 +262,7 @@ ports attached to the switch)
*
* Any network interface known to the system (including a persistent VALE
* port) can be attached to a VALE switch by issuing the
- * NETMAP_BDG_ATTACH subcommand. After the attachment, persistent VALE ports
+ * NETMAP_REQ_VALE_ATTACH command. After the attachment, persistent VALE ports
* look exactly like ephemeral VALE ports (as created in step 2 above). The
* attachment of other interfaces, instead, requires the creation of a
* netmap_bwrap_adapter. Moreover, the attached interface must be put in
@@ -591,9 +591,9 @@ void
netmap_set_ring(struct netmap_adapter *na, u_int ring_id, enum txrx t, int stopped)
{
if (stopped)
- netmap_disable_ring(NMR(na, t) + ring_id, stopped);
+ netmap_disable_ring(NMR(na, t)[ring_id], stopped);
else
- NMR(na, t)[ring_id].nkr_stopped = 0;
+ NMR(na, t)[ring_id]->nkr_stopped = 0;
}
@@ -745,39 +745,42 @@ nm_dump_buf(char *p, int len, int lim, char *dst)
int
netmap_update_config(struct netmap_adapter *na)
{
- u_int txr, txd, rxr, rxd;
+ struct nm_config_info info;
- txr = txd = rxr = rxd = 0;
+ bzero(&info, sizeof(info));
if (na->nm_config == NULL ||
- na->nm_config(na, &txr, &txd, &rxr, &rxd))
- {
+ na->nm_config(na, &info)) {
/* take whatever we had at init time */
- txr = na->num_tx_rings;
- txd = na->num_tx_desc;
- rxr = na->num_rx_rings;
- rxd = na->num_rx_desc;
+ info.num_tx_rings = na->num_tx_rings;
+ info.num_tx_descs = na->num_tx_desc;
+ info.num_rx_rings = na->num_rx_rings;
+ info.num_rx_descs = na->num_rx_desc;
+ info.rx_buf_maxsize = na->rx_buf_maxsize;
}
- if (na->num_tx_rings == txr && na->num_tx_desc == txd &&
- na->num_rx_rings == rxr && na->num_rx_desc == rxd)
+ if (na->num_tx_rings == info.num_tx_rings &&
+ na->num_tx_desc == info.num_tx_descs &&
+ na->num_rx_rings == info.num_rx_rings &&
+ na->num_rx_desc == info.num_rx_descs &&
+ na->rx_buf_maxsize == info.rx_buf_maxsize)
return 0; /* nothing changed */
- if (netmap_verbose || na->active_fds > 0) {
- D("stored config %s: txring %d x %d, rxring %d x %d",
- na->name,
- na->num_tx_rings, na->num_tx_desc,
- na->num_rx_rings, na->num_rx_desc);
- D("new config %s: txring %d x %d, rxring %d x %d",
- na->name, txr, txd, rxr, rxd);
- }
if (na->active_fds == 0) {
- D("configuration changed (but fine)");
- na->num_tx_rings = txr;
- na->num_tx_desc = txd;
- na->num_rx_rings = rxr;
- na->num_rx_desc = rxd;
+ D("configuration changed for %s: txring %d x %d, "
+ "rxring %d x %d, rxbufsz %d",
+ na->name, na->num_tx_rings, na->num_tx_desc,
+ na->num_rx_rings, na->num_rx_desc, na->rx_buf_maxsize);
+ na->num_tx_rings = info.num_tx_rings;
+ na->num_tx_desc = info.num_tx_descs;
+ na->num_rx_rings = info.num_rx_rings;
+ na->num_rx_desc = info.num_rx_descs;
+ na->rx_buf_maxsize = info.rx_buf_maxsize;
return 0;
}
- D("configuration changed while active, this is bad...");
+ D("WARNING: configuration changed for %s while active: "
+ "txring %d x %d, rxring %d x %d, rxbufsz %d",
+ na->name, info.num_tx_rings, info.num_tx_descs,
+ info.num_rx_rings, info.num_rx_descs,
+ info.rx_buf_maxsize);
return 1;
}
@@ -827,7 +830,9 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
n[NR_TX] = na->num_tx_rings + 1;
n[NR_RX] = na->num_rx_rings + 1;
- len = (n[NR_TX] + n[NR_RX]) * sizeof(struct netmap_kring) + tailroom;
+ len = (n[NR_TX] + n[NR_RX]) *
+ (sizeof(struct netmap_kring) + sizeof(struct netmap_kring *))
+ + tailroom;
na->tx_rings = nm_os_malloc((size_t)len);
if (na->tx_rings == NULL) {
@@ -835,6 +840,14 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
return ENOMEM;
}
na->rx_rings = na->tx_rings + n[NR_TX];
+ na->tailroom = na->rx_rings + n[NR_RX];
+
+ /* link the krings in the krings array */
+ kring = (struct netmap_kring *)((char *)na->tailroom + tailroom);
+ for (i = 0; i < n[NR_TX] + n[NR_RX]; i++) {
+ na->tx_rings[i] = kring;
+ kring++;
+ }
/*
* All fields in krings are 0 except the one initialized below.
@@ -843,9 +856,10 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
for_rx_tx(t) {
ndesc = nma_get_ndesc(na, t);
for (i = 0; i < n[t]; i++) {
- kring = &NMR(na, t)[i];
+ kring = NMR(na, t)[i];
bzero(kring, sizeof(*kring));
kring->na = na;
+ kring->notify_na = na;
kring->ring_id = i;
kring->tx = t;
kring->nkr_num_slots = ndesc;
@@ -854,6 +868,8 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
if (i < nma_get_nrings(na, t)) {
kring->nm_sync = (t == NR_TX ? na->nm_txsync : na->nm_rxsync);
} 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);
@@ -874,7 +890,6 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
nm_os_selinfo_init(&na->si[t]);
}
- na->tailroom = na->rx_rings + n[NR_RX];
return 0;
}
@@ -885,7 +900,7 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
void
netmap_krings_delete(struct netmap_adapter *na)
{
- struct netmap_kring *kring = na->tx_rings;
+ struct netmap_kring **kring = na->tx_rings;
enum txrx t;
if (na->tx_rings == NULL) {
@@ -898,8 +913,8 @@ netmap_krings_delete(struct netmap_adapter *na)
/* we rely on the krings layout described above */
for ( ; kring != na->tailroom; kring++) {
- mtx_destroy(&kring->q_lock);
- nm_os_selinfo_uninit(&kring->si);
+ mtx_destroy(&(*kring)->q_lock);
+ nm_os_selinfo_uninit(&(*kring)->si);
}
nm_os_free(na->tx_rings);
na->tx_rings = na->rx_rings = na->tailroom = NULL;
@@ -915,7 +930,7 @@ netmap_krings_delete(struct netmap_adapter *na)
void
netmap_hw_krings_delete(struct netmap_adapter *na)
{
- struct mbq *q = &na->rx_rings[na->num_rx_rings].rx_queue;
+ struct mbq *q = &na->rx_rings[na->num_rx_rings]->rx_queue;
ND("destroy sw mbq with len %d", mbq_len(q));
mbq_purge(q);
@@ -1196,7 +1211,7 @@ nm_may_forward_down(struct netmap_kring *kring, int sync_flags)
static u_int
netmap_sw_to_nic(struct netmap_adapter *na)
{
- struct netmap_kring *kring = &na->rx_rings[na->num_rx_rings];
+ struct netmap_kring *kring = na->rx_rings[na->num_rx_rings];
struct netmap_slot *rxslot = kring->ring->slot;
u_int i, rxcur = kring->nr_hwcur;
u_int const head = kring->rhead;
@@ -1205,7 +1220,7 @@ netmap_sw_to_nic(struct netmap_adapter *na)
/* scan rings to find space, then fill as much as possible */
for (i = 0; i < na->num_tx_rings; i++) {
- struct netmap_kring *kdst = &na->tx_rings[i];
+ struct netmap_kring *kdst = na->tx_rings[i];
struct netmap_ring *rdst = kdst->ring;
u_int const dst_lim = kdst->nkr_num_slots - 1;
@@ -1443,7 +1458,7 @@ assign_mem:
* MUST BE CALLED UNDER NMG_LOCK()
*
* Get a refcounted reference to a netmap adapter attached
- * to the interface specified by nmr.
+ * to the interface specified by req.
* This is always called in the execution of an ioctl().
*
* Return ENXIO if the interface specified by the request does
@@ -1453,13 +1468,15 @@ assign_mem:
* could not be allocated.
* If successful, hold a reference to the netmap adapter.
*
- * If the interface specified by nmr is a system one, also keep
+ * If the interface specified by req is a system one, also keep
* a reference to it and return a valid *ifp.
*/
int
-netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
- struct ifnet **ifp, struct netmap_mem_d *nmd, int create)
+netmap_get_na(struct nmreq_header *hdr,
+ struct netmap_adapter **na, struct ifnet **ifp,
+ struct netmap_mem_d *nmd, int create)
{
+ struct nmreq_register *req = (struct nmreq_register *)hdr->nr_body;
int error = 0;
struct netmap_adapter *ret = NULL;
int nmd_ref = 0;
@@ -1467,13 +1484,24 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
*na = NULL; /* default return value */
*ifp = NULL;
+ if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) {
+ return EINVAL;
+ }
+
+ if (req->nr_mode == NR_REG_PIPE_MASTER ||
+ req->nr_mode == NR_REG_PIPE_SLAVE) {
+ /* Do not accept deprecated pipe modes. */
+ D("Deprecated pipe nr_mode, use xx{yy or xx}yy syntax");
+ return EINVAL;
+ }
+
NMG_LOCK_ASSERT();
/* if the request contain a memid, try to find the
* corresponding memory region
*/
- if (nmd == NULL && nmr->nr_arg2) {
- nmd = netmap_mem_find(nmr->nr_arg2);
+ if (nmd == NULL && req->nr_mem_id) {
+ nmd = netmap_mem_find(req->nr_mem_id);
if (nmd == NULL)
return EINVAL;
/* keep the rereference */
@@ -1492,22 +1520,22 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
*/
/* try to see if this is a ptnetmap port */
- error = netmap_get_pt_host_na(nmr, na, nmd, create);
+ error = netmap_get_pt_host_na(hdr, na, nmd, create);
if (error || *na != NULL)
goto out;
/* try to see if this is a monitor port */
- error = netmap_get_monitor_na(nmr, na, nmd, create);
+ error = netmap_get_monitor_na(hdr, na, nmd, create);
if (error || *na != NULL)
goto out;
/* try to see if this is a pipe port */
- error = netmap_get_pipe_na(nmr, na, nmd, create);
+ error = netmap_get_pipe_na(hdr, na, nmd, create);
if (error || *na != NULL)
goto out;
/* try to see if this is a bridge port */
- error = netmap_get_bdg_na(nmr, na, nmd, create);
+ error = netmap_get_bdg_na(hdr, na, nmd, create);
if (error)
goto out;
@@ -1520,7 +1548,7 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
* This may still be a tap, a veth/epair, or even a
* persistent VALE port.
*/
- *ifp = ifunit_ref(nmr->nr_name);
+ *ifp = ifunit_ref(hdr->nr_name);
if (*ifp == NULL) {
error = ENXIO;
goto out;
@@ -1765,42 +1793,27 @@ netmap_ring_reinit(struct netmap_kring *kring)
*
*/
int
-netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags)
+netmap_interp_ringid(struct netmap_priv_d *priv, uint32_t nr_mode,
+ uint16_t nr_ringid, uint64_t nr_flags)
{
struct netmap_adapter *na = priv->np_na;
- u_int j, i = ringid & NETMAP_RING_MASK;
- u_int reg = flags & NR_REG_MASK;
int excluded_direction[] = { NR_TX_RINGS_ONLY, NR_RX_RINGS_ONLY };
enum txrx t;
+ u_int j;
- if (reg == NR_REG_DEFAULT) {
- /* convert from old ringid to flags */
- if (ringid & NETMAP_SW_RING) {
- reg = NR_REG_SW;
- } else if (ringid & NETMAP_HW_RING) {
- reg = NR_REG_ONE_NIC;
- } else {
- reg = NR_REG_ALL_NIC;
- }
- D("deprecated API, old ringid 0x%x -> ringid %x reg %d", ringid, i, reg);
- }
-
- if ((flags & NR_PTNETMAP_HOST) && ((reg != NR_REG_ALL_NIC &&
- reg != NR_REG_PIPE_MASTER && reg != NR_REG_PIPE_SLAVE) ||
- flags & (NR_RX_RINGS_ONLY|NR_TX_RINGS_ONLY))) {
+ if ((nr_flags & NR_PTNETMAP_HOST) && ((nr_mode != NR_REG_ALL_NIC) ||
+ nr_flags & (NR_RX_RINGS_ONLY|NR_TX_RINGS_ONLY))) {
D("Error: only NR_REG_ALL_NIC supported with netmap passthrough");
return EINVAL;
}
for_rx_tx(t) {
- if (flags & excluded_direction[t]) {
+ if (nr_flags & excluded_direction[t]) {
priv->np_qfirst[t] = priv->np_qlast[t] = 0;
continue;
}
- switch (reg) {
+ switch (nr_mode) {
case NR_REG_ALL_NIC:
- case NR_REG_PIPE_MASTER:
- case NR_REG_PIPE_SLAVE:
priv->np_qfirst[t] = 0;
priv->np_qlast[t] = nma_get_nrings(na, t);
ND("ALL/PIPE: %s %d %d", nm_txrx2str(t),
@@ -1812,20 +1825,21 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
D("host rings not supported");
return EINVAL;
}
- priv->np_qfirst[t] = (reg == NR_REG_SW ?
+ priv->np_qfirst[t] = (nr_mode == NR_REG_SW ?
nma_get_nrings(na, t) : 0);
priv->np_qlast[t] = nma_get_nrings(na, t) + 1;
- ND("%s: %s %d %d", reg == NR_REG_SW ? "SW" : "NIC+SW",
+ ND("%s: %s %d %d", nr_mode == NR_REG_SW ? "SW" : "NIC+SW",
nm_txrx2str(t),
priv->np_qfirst[t], priv->np_qlast[t]);
break;
case NR_REG_ONE_NIC:
- if (i >= na->num_tx_rings && i >= na->num_rx_rings) {
- D("invalid ring id %d", i);
+ if (nr_ringid >= na->num_tx_rings &&
+ nr_ringid >= na->num_rx_rings) {
+ D("invalid ring id %d", nr_ringid);
return EINVAL;
}
/* if not enough rings, use the first one */
- j = i;
+ j = nr_ringid;
if (j >= nma_get_nrings(na, t))
j = 0;
priv->np_qfirst[t] = j;
@@ -1834,11 +1848,11 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
priv->np_qfirst[t], priv->np_qlast[t]);
break;
default:
- D("invalid regif type %d", reg);
+ D("invalid regif type %d", nr_mode);
return EINVAL;
}
}
- priv->np_flags = (flags & ~NR_REG_MASK) | reg;
+ priv->np_flags = nr_flags | nr_mode; // TODO
/* Allow transparent forwarding mode in the host --> nic
* direction only if all the TX hw rings have been opened. */
@@ -1854,7 +1868,7 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
priv->np_qlast[NR_TX],
priv->np_qfirst[NR_RX],
priv->np_qlast[NR_RX],
- i);
+ nr_ringid);
}
return 0;
}
@@ -1865,18 +1879,19 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
* for all rings is the same as a single ring.
*/
static int
-netmap_set_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags)
+netmap_set_ringid(struct netmap_priv_d *priv, uint32_t nr_mode,
+ uint16_t nr_ringid, uint64_t nr_flags)
{
struct netmap_adapter *na = priv->np_na;
int error;
enum txrx t;
- error = netmap_interp_ringid(priv, ringid, flags);
+ error = netmap_interp_ringid(priv, nr_mode, nr_ringid, nr_flags);
if (error) {
return error;
}
- priv->np_txpoll = (ringid & NETMAP_NO_TX_POLL) ? 0 : 1;
+ priv->np_txpoll = (nr_flags & NR_NO_TX_POLL) ? 0 : 1;
/* optimization: count the users registered for more than
* one ring, which are the ones sleeping on the global queue.
@@ -1933,7 +1948,7 @@ netmap_krings_get(struct netmap_priv_d *priv)
*/
for_rx_tx(t) {
for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
- kring = &NMR(na, t)[i];
+ kring = NMR(na, t)[i];
if ((kring->nr_kflags & NKR_EXCLUSIVE) ||
(kring->users && excl))
{
@@ -1948,7 +1963,7 @@ netmap_krings_get(struct netmap_priv_d *priv)
*/
for_rx_tx(t) {
for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
- kring = &NMR(na, t)[i];
+ kring = NMR(na, t)[i];
kring->users++;
if (excl)
kring->nr_kflags |= NKR_EXCLUSIVE;
@@ -1979,10 +1994,9 @@ netmap_krings_put(struct netmap_priv_d *priv)
priv->np_qfirst[NR_RX],
priv->np_qlast[MR_RX]);
-
for_rx_tx(t) {
for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
- kring = &NMR(na, t)[i];
+ kring = NMR(na, t)[i];
if (excl)
kring->nr_kflags &= ~NKR_EXCLUSIVE;
kring->users--;
@@ -1992,6 +2006,12 @@ netmap_krings_put(struct netmap_priv_d *priv)
}
}
+static int
+nm_priv_rx_enabled(struct netmap_priv_d *priv)
+{
+ return (priv->np_qfirst[NR_RX] != priv->np_qlast[NR_RX]);
+}
+
/*
* possibly move the interface to netmap-mode.
* If success it returns a pointer to netmap_if, otherwise NULL.
@@ -2064,16 +2084,14 @@ netmap_krings_put(struct netmap_priv_d *priv)
*/
int
netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
- uint16_t ringid, uint32_t flags)
+ uint32_t nr_mode, uint16_t nr_ringid, uint64_t nr_flags)
{
struct netmap_if *nifp = NULL;
int error;
NMG_LOCK_ASSERT();
- /* ring configuration may have changed, fetch from the card */
- netmap_update_config(na);
priv->np_na = na; /* store the reference */
- error = netmap_set_ringid(priv, ringid, flags);
+ error = netmap_set_ringid(priv, nr_mode, nr_ringid, nr_flags);
if (error)
goto err;
error = netmap_mem_finalize(na->nm_mem, na);
@@ -2081,27 +2099,38 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
goto err;
if (na->active_fds == 0) {
+
+ /* cache the allocator info in the na */
+ error = netmap_mem_get_lut(na->nm_mem, &na->na_lut);
+ if (error)
+ goto err_drop_mem;
+ ND("lut %p bufs %u size %u", na->na_lut.lut, na->na_lut.objtotal,
+ na->na_lut.objsize);
+
+ /* ring configuration may have changed, fetch from the card */
+ netmap_update_config(na);
+
/*
* If this is the first registration of the adapter,
* perform sanity checks and create the in-kernel view
* of the netmap rings (the netmap krings).
*/
- if (na->ifp) {
+ if (na->ifp && nm_priv_rx_enabled(priv)) {
/* This netmap adapter is attached to an ifnet. */
unsigned nbs = netmap_mem_bufsize(na->nm_mem);
unsigned mtu = nm_os_ifnet_mtu(na->ifp);
- /* The maximum amount of bytes that a single
- * receive or transmit NIC descriptor can hold. */
- unsigned hw_max_slot_len = 4096;
- if (mtu <= hw_max_slot_len) {
+ ND("mtu %d rx_buf_maxsize %d netmap_buf_size %d",
+ mtu, na->rx_buf_maxsize, nbs);
+
+ if (mtu <= na->rx_buf_maxsize) {
/* The MTU fits a single NIC slot. We only
* Need to check that netmap buffers are
* large enough to hold an MTU. NS_MOREFRAG
* cannot be used in this case. */
if (nbs < mtu) {
nm_prerr("error: netmap buf size (%u) "
- "< device MTU (%u)", nbs, mtu);
+ "< device MTU (%u)\n", nbs, mtu);
error = EINVAL;
goto err_drop_mem;
}
@@ -2114,22 +2143,22 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
if (!(na->na_flags & NAF_MOREFRAG)) {
nm_prerr("error: large MTU (%d) needed "
"but %s does not support "
- "NS_MOREFRAG", mtu,
+ "NS_MOREFRAG\n", mtu,
na->ifp->if_xname);
error = EINVAL;
goto err_drop_mem;
- } else if (nbs < hw_max_slot_len) {
+ } else if (nbs < na->rx_buf_maxsize) {
nm_prerr("error: using NS_MOREFRAG on "
"%s requires netmap buf size "
- ">= %u", na->ifp->if_xname,
- hw_max_slot_len);
+ ">= %u\n", na->ifp->if_xname,
+ na->rx_buf_maxsize);
error = EINVAL;
goto err_drop_mem;
} else {
nm_prinf("info: netmap application on "
"%s needs to support "
"NS_MOREFRAG "
- "(MTU=%u,netmap_buf_size=%u)",
+ "(MTU=%u,netmap_buf_size=%u)\n",
na->ifp->if_xname, mtu, nbs);
}
}
@@ -2141,7 +2170,7 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
*/
error = na->nm_krings_create(na);
if (error)
- goto err_drop_mem;
+ goto err_put_lut;
}
@@ -2165,21 +2194,12 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
goto err_del_rings;
}
- if (na->active_fds == 0) {
- /* cache the allocator info in the na */
- error = netmap_mem_get_lut(na->nm_mem, &na->na_lut);
- if (error)
- goto err_del_if;
- ND("lut %p bufs %u size %u", na->na_lut.lut, na->na_lut.objtotal,
- na->na_lut.objsize);
- }
-
if (nm_kring_pending(priv)) {
/* Some kring is switching mode, tell the adapter to
* react on this. */
error = na->nm_register(na, 1);
if (error)
- goto err_put_lut;
+ goto err_del_if;
}
/* Commit the reference. */
@@ -2195,9 +2215,6 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
return 0;
-err_put_lut:
- if (na->active_fds == 0)
- memset(&na->na_lut, 0, sizeof(na->na_lut));
err_del_if:
netmap_mem_if_delete(na, nifp);
err_del_rings:
@@ -2207,6 +2224,9 @@ err_rel_excl:
err_del_krings:
if (na->active_fds == 0)
na->nm_krings_delete(na);
+err_put_lut:
+ if (na->active_fds == 0)
+ memset(&na->na_lut, 0, sizeof(na->na_lut));
err_drop_mem:
netmap_mem_drop(na);
err:
@@ -2242,246 +2262,367 @@ ring_timestamp_set(struct netmap_ring *ring)
}
}
+static int nmreq_copyin(struct nmreq_header *, int);
+static int nmreq_copyout(struct nmreq_header *, int);
+static int nmreq_checkoptions(struct nmreq_header *);
/*
* ioctl(2) support for the "netmap" device.
*
* Following a list of accepted commands:
- * - NIOCGINFO
+ * - NIOCCTRL device control API
+ * - NIOCTXSYNC sync TX rings
+ * - NIOCRXSYNC sync RX rings
* - SIOCGIFADDR just for convenience
- * - NIOCREGIF
- * - NIOCTXSYNC
- * - NIOCRXSYNC
+ * - NIOCGINFO deprecated (legacy API)
+ * - NIOCREGIF deprecated (legacy API)
*
* Return 0 on success, errno otherwise.
*/
int
-netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread *td)
+netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
+ struct thread *td, int nr_body_is_user)
{
struct mbq q; /* packets from RX hw queues to host stack */
- struct nmreq *nmr = (struct nmreq *) data;
struct netmap_adapter *na = NULL;
struct netmap_mem_d *nmd = NULL;
struct ifnet *ifp = NULL;
int error = 0;
u_int i, qfirst, qlast;
struct netmap_if *nifp;
- struct netmap_kring *krings;
+ struct netmap_kring **krings;
int sync_flags;
enum txrx t;
- if (cmd == NIOCGINFO || cmd == NIOCREGIF) {
- /* truncate name */
- nmr->nr_name[sizeof(nmr->nr_name) - 1] = '\0';
- if (nmr->nr_version != NETMAP_API) {
- D("API mismatch for %s got %d need %d",
- nmr->nr_name,
- nmr->nr_version, NETMAP_API);
- nmr->nr_version = NETMAP_API;
+ switch (cmd) {
+ case NIOCCTRL: {
+ struct nmreq_header *hdr = (struct nmreq_header *)data;
+
+ if (hdr->nr_version != NETMAP_API) {
+ D("API mismatch for reqtype %d: got %d need %d",
+ hdr->nr_version,
+ hdr->nr_version, NETMAP_API);
+ hdr->nr_version = NETMAP_API;
}
- if (nmr->nr_version < NETMAP_MIN_API ||
- nmr->nr_version > NETMAP_MAX_API) {
+ if (hdr->nr_version < NETMAP_MIN_API ||
+ hdr->nr_version > NETMAP_MAX_API) {
return EINVAL;
}
- }
- switch (cmd) {
- case NIOCGINFO: /* return capabilities etc */
- if (nmr->nr_cmd == NETMAP_BDG_LIST) {
- error = netmap_bdg_ctl(nmr, NULL);
- break;
+ /* Make a kernel-space copy of the user-space nr_body.
+ * For convenince, the nr_body pointer and the pointers
+ * in the options list will be replaced with their
+ * kernel-space counterparts. The original pointers are
+ * saved internally and later restored by nmreq_copyout
+ */
+ error = nmreq_copyin(hdr, nr_body_is_user);
+ if (error) {
+ return error;
}
- NMG_LOCK();
- do {
- /* memsize is always valid */
- u_int memflags;
- uint64_t memsize;
+ /* Sanitize hdr->nr_name. */
+ hdr->nr_name[sizeof(hdr->nr_name) - 1] = '\0';
+
+ switch (hdr->nr_reqtype) {
+ case NETMAP_REQ_REGISTER: {
+ struct nmreq_register *req =
+ (struct nmreq_register *)hdr->nr_body;
+ /* Protect access to priv from concurrent requests. */
+ NMG_LOCK();
+ do {
+ u_int memflags;
+#ifdef WITH_EXTMEM
+ struct nmreq_option *opt;
+#endif /* WITH_EXTMEM */
+
+ if (priv->np_nifp != NULL) { /* thread already registered */
+ error = EBUSY;
+ break;
+ }
+
+#ifdef WITH_EXTMEM
+ opt = nmreq_findoption((struct nmreq_option *)hdr->nr_options,
+ NETMAP_REQ_OPT_EXTMEM);
+ if (opt != NULL) {
+ struct nmreq_opt_extmem *e =
+ (struct nmreq_opt_extmem *)opt;
+
+ error = nmreq_checkduplicate(opt);
+ if (error) {
+ opt->nro_status = error;
+ break;
+ }
+ nmd = netmap_mem_ext_create(e->nro_usrptr,
+ &e->nro_info, &error);
+ opt->nro_status = error;
+ if (nmd == NULL)
+ break;
+ }
+#endif /* WITH_EXTMEM */
+
+ if (nmd == NULL && req->nr_mem_id) {
+ /* find the allocator and get a reference */
+ nmd = netmap_mem_find(req->nr_mem_id);
+ if (nmd == NULL) {
+ error = EINVAL;
+ break;
+ }
+ }
+ /* find the interface and a reference */
+ error = netmap_get_na(hdr, &na, &ifp, nmd,
+ 1 /* create */); /* keep reference */
+ if (error)
+ break;
+ if (NETMAP_OWNED_BY_KERN(na)) {
+ error = EBUSY;
+ break;
+ }
+
+ if (na->virt_hdr_len && !(req->nr_flags & NR_ACCEPT_VNET_HDR)) {
+ error = EIO;
+ break;
+ }
- if (nmr->nr_name[0] != '\0') {
+ error = netmap_do_regif(priv, na, req->nr_mode,
+ req->nr_ringid, req->nr_flags);
+ if (error) { /* reg. failed, release priv and ref */
+ break;
+ }
+ nifp = priv->np_nifp;
+ priv->np_td = td; /* for debugging purposes */
- /* get a refcount */
- error = netmap_get_na(nmr, &na, &ifp, NULL, 1 /* create */);
+ /* return the offset of the netmap_if object */
+ req->nr_rx_rings = na->num_rx_rings;
+ req->nr_tx_rings = na->num_tx_rings;
+ req->nr_rx_slots = na->num_rx_desc;
+ req->nr_tx_slots = na->num_tx_desc;
+ error = netmap_mem_get_info(na->nm_mem, &req->nr_memsize, &memflags,
+ &req->nr_mem_id);
if (error) {
- na = NULL;
- ifp = NULL;
+ netmap_do_unregif(priv);
break;
}
- nmd = na->nm_mem; /* get memory allocator */
- } else {
- nmd = netmap_mem_find(nmr->nr_arg2 ? nmr->nr_arg2 : 1);
- if (nmd == NULL) {
- error = EINVAL;
+ if (memflags & NETMAP_MEM_PRIVATE) {
+ *(uint32_t *)(uintptr_t)&nifp->ni_flags |= NI_PRIV_MEM;
+ }
+ for_rx_tx(t) {
+ priv->np_si[t] = nm_si_user(priv, t) ?
+ &na->si[t] : &NMR(na, t)[priv->np_qfirst[t]]->si;
+ }
+
+ if (req->nr_extra_bufs) {
+ if (netmap_verbose)
+ D("requested %d extra buffers",
+ req->nr_extra_bufs);
+ req->nr_extra_bufs = netmap_extra_alloc(na,
+ &nifp->ni_bufs_head, req->nr_extra_bufs);
+ if (netmap_verbose)
+ D("got %d extra buffers", req->nr_extra_bufs);
+ }
+ req->nr_offset = netmap_mem_if_offset(na->nm_mem, nifp);
+
+ error = nmreq_checkoptions(hdr);
+ if (error) {
+ netmap_do_unregif(priv);
break;
}
+
+ /* store ifp reference so that priv destructor may release it */
+ priv->np_ifp = ifp;
+ } while (0);
+ if (error) {
+ netmap_unget_na(na, ifp);
}
+ /* release the reference from netmap_mem_find() or
+ * netmap_mem_ext_create()
+ */
+ if (nmd)
+ netmap_mem_put(nmd);
+ NMG_UNLOCK();
+ break;
+ }
- error = netmap_mem_get_info(nmd, &memsize, &memflags,
- &nmr->nr_arg2);
- if (error)
- break;
- nmr->nr_memsize = (uint32_t)memsize;
- if (na == NULL) /* only memory info */
- break;
- nmr->nr_offset = 0;
- nmr->nr_rx_slots = nmr->nr_tx_slots = 0;
- netmap_update_config(na);
- nmr->nr_rx_rings = na->num_rx_rings;
- nmr->nr_tx_rings = na->num_tx_rings;
- nmr->nr_rx_slots = na->num_rx_desc;
- nmr->nr_tx_slots = na->num_tx_desc;
- } while (0);
- netmap_unget_na(na, ifp);
- NMG_UNLOCK();
- break;
+ case NETMAP_REQ_PORT_INFO_GET: {
+ struct nmreq_port_info_get *req =
+ (struct nmreq_port_info_get *)hdr->nr_body;
- case NIOCREGIF:
- /*
- * If nmr->nr_cmd is not zero, this NIOCREGIF is not really
- * a regif operation, but a different one, specified by the
- * value of nmr->nr_cmd.
- */
- i = nmr->nr_cmd;
- if (i == NETMAP_BDG_ATTACH || i == NETMAP_BDG_DETACH
- || i == NETMAP_BDG_VNET_HDR
- || i == NETMAP_BDG_NEWIF
- || i == NETMAP_BDG_DELIF
- || i == NETMAP_BDG_POLLING_ON
- || i == NETMAP_BDG_POLLING_OFF) {
- /* possibly attach/detach NIC and VALE switch */
- error = netmap_bdg_ctl(nmr, NULL);
+ NMG_LOCK();
+ do {
+ u_int memflags;
+
+ if (hdr->nr_name[0] != '\0') {
+ /* Build a nmreq_register out of the nmreq_port_info_get,
+ * so that we can call netmap_get_na(). */
+ struct nmreq_register regreq;
+ bzero(&regreq, sizeof(regreq));
+ regreq.nr_tx_slots = req->nr_tx_slots;
+ regreq.nr_rx_slots = req->nr_rx_slots;
+ regreq.nr_tx_rings = req->nr_tx_rings;
+ regreq.nr_rx_rings = req->nr_rx_rings;
+ regreq.nr_mem_id = req->nr_mem_id;
+
+ /* get a refcount */
+ hdr->nr_reqtype = NETMAP_REQ_REGISTER;
+ hdr->nr_body = (uint64_t)&regreq;
+ error = netmap_get_na(hdr, &na, &ifp, NULL, 1 /* create */);
+ hdr->nr_reqtype = NETMAP_REQ_PORT_INFO_GET; /* reset type */
+ hdr->nr_body = (uint64_t)req; /* reset nr_body */
+ if (error) {
+ na = NULL;
+ ifp = NULL;
+ break;
+ }
+ nmd = na->nm_mem; /* get memory allocator */
+ } else {
+ nmd = netmap_mem_find(req->nr_mem_id ? req->nr_mem_id : 1);
+ if (nmd == NULL) {
+ error = EINVAL;
+ break;
+ }
+ }
+
+ error = netmap_mem_get_info(nmd, &req->nr_memsize, &memflags,
+ &req->nr_mem_id);
+ if (error)
+ break;
+ if (na == NULL) /* only memory info */
+ break;
+ req->nr_offset = 0;
+ req->nr_rx_slots = req->nr_tx_slots = 0;
+ netmap_update_config(na);
+ req->nr_rx_rings = na->num_rx_rings;
+ req->nr_tx_rings = na->num_tx_rings;
+ req->nr_rx_slots = na->num_rx_desc;
+ req->nr_tx_slots = na->num_tx_desc;
+ } while (0);
+ netmap_unget_na(na, ifp);
+ NMG_UNLOCK();
break;
- } else if (i == NETMAP_PT_HOST_CREATE || i == NETMAP_PT_HOST_DELETE) {
- /* forward the command to the ptnetmap subsystem */
- error = ptnetmap_ctl(nmr, priv->np_na);
+ }
+#ifdef WITH_VALE
+ case NETMAP_REQ_VALE_ATTACH: {
+ error = nm_bdg_ctl_attach(hdr, NULL /* userspace request */);
break;
- } else if (i == NETMAP_VNET_HDR_GET) {
- /* get vnet-header length for this netmap port */
+ }
+
+ case NETMAP_REQ_VALE_DETACH: {
+ error = nm_bdg_ctl_detach(hdr, NULL /* userspace request */);
+ break;
+ }
+
+ case NETMAP_REQ_VALE_LIST: {
+ error = netmap_bdg_list(hdr);
+ break;
+ }
+
+ case NETMAP_REQ_PORT_HDR_SET: {
+ struct nmreq_port_hdr *req =
+ (struct nmreq_port_hdr *)hdr->nr_body;
+ /* Build a nmreq_register out of the nmreq_port_hdr,
+ * so that we can call netmap_get_bdg_na(). */
+ struct nmreq_register regreq;
+ bzero(&regreq, sizeof(regreq));
+ /* For now we only support virtio-net headers, and only for
+ * VALE ports, but this may change in future. Valid lengths
+ * for the virtio-net header are 0 (no header), 10 and 12. */
+ if (req->nr_hdr_len != 0 &&
+ req->nr_hdr_len != sizeof(struct nm_vnet_hdr) &&
+ req->nr_hdr_len != 12) {
+ error = EINVAL;
+ break;
+ }
+ NMG_LOCK();
+ hdr->nr_reqtype = NETMAP_REQ_REGISTER;
+ hdr->nr_body = (uint64_t)&regreq;
+ error = netmap_get_bdg_na(hdr, &na, NULL, 0);
+ hdr->nr_reqtype = NETMAP_REQ_PORT_HDR_SET;
+ hdr->nr_body = (uint64_t)req;
+ if (na && !error) {
+ struct netmap_vp_adapter *vpna =
+ (struct netmap_vp_adapter *)na;
+ na->virt_hdr_len = req->nr_hdr_len;
+ if (na->virt_hdr_len) {
+ vpna->mfs = NETMAP_BUF_SIZE(na);
+ }
+ D("Using vnet_hdr_len %d for %p", na->virt_hdr_len, na);
+ netmap_adapter_put(na);
+ } else if (!na) {
+ error = ENXIO;
+ }
+ NMG_UNLOCK();
+ break;
+ }
+
+ case NETMAP_REQ_PORT_HDR_GET: {
+ /* Get vnet-header length for this netmap port */
+ struct nmreq_port_hdr *req =
+ (struct nmreq_port_hdr *)hdr->nr_body;
+ /* Build a nmreq_register out of the nmreq_port_hdr,
+ * so that we can call netmap_get_bdg_na(). */
+ struct nmreq_register regreq;
struct ifnet *ifp;
+ bzero(&regreq, sizeof(regreq));
NMG_LOCK();
- error = netmap_get_na(nmr, &na, &ifp, NULL, 0);
+ hdr->nr_reqtype = NETMAP_REQ_REGISTER;
+ hdr->nr_body = (uint64_t)&regreq;
+ error = netmap_get_na(hdr, &na, &ifp, NULL, 0);
+ hdr->nr_reqtype = NETMAP_REQ_PORT_HDR_GET;
+ hdr->nr_body = (uint64_t)req;
if (na && !error) {
- nmr->nr_arg1 = na->virt_hdr_len;
+ req->nr_hdr_len = na->virt_hdr_len;
}
netmap_unget_na(na, ifp);
NMG_UNLOCK();
break;
- } else if (i == NETMAP_POOLS_INFO_GET) {
- /* get information from the memory allocator */
+ }
+
+ case NETMAP_REQ_VALE_NEWIF: {
+ error = nm_vi_create(hdr);
+ break;
+ }
+
+ case NETMAP_REQ_VALE_DELIF: {
+ error = nm_vi_destroy(hdr->nr_name);
+ break;
+ }
+
+ 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: {
+ struct nmreq_pools_info *req =
+ (struct nmreq_pools_info *)hdr->nr_body;
+ /* Get information from the memory allocator. This
+ * netmap device must already be bound to a port.
+ * Note that hdr->nr_name is ignored. */
NMG_LOCK();
if (priv->np_na && priv->np_na->nm_mem) {
struct netmap_mem_d *nmd = priv->np_na->nm_mem;
- error = netmap_mem_pools_info_get(nmr, nmd);
+ error = netmap_mem_pools_info_get(req, nmd);
} else {
error = EINVAL;
}
NMG_UNLOCK();
break;
- } else if (i == NETMAP_POOLS_CREATE) {
- nmd = netmap_mem_ext_create(nmr, &error);
- if (nmd == NULL)
- break;
- /* reset the fields used by POOLS_CREATE to
- * avoid confusing the rest of the code
- */
- nmr->nr_cmd = 0;
- nmr->nr_arg1 = 0;
- nmr->nr_arg2 = 0;
- nmr->nr_arg3 = 0;
- } else if (i != 0) {
- D("nr_cmd must be 0 not %d", i);
+ }
+
+ default: {
error = EINVAL;
break;
}
-
- /* protect access to priv from concurrent NIOCREGIF */
- NMG_LOCK();
- do {
- u_int memflags;
- uint64_t memsize;
-
- if (priv->np_nifp != NULL) { /* thread already registered */
- error = EBUSY;
- break;
- }
-
- if (nmr->nr_arg2) {
- /* find the allocator and get a reference */
- nmd = netmap_mem_find(nmr->nr_arg2);
- if (nmd == NULL) {
- error = EINVAL;
- break;
- }
- }
- /* find the interface and a reference */
- error = netmap_get_na(nmr, &na, &ifp, nmd,
- 1 /* create */); /* keep reference */
- if (error)
- break;
- if (NETMAP_OWNED_BY_KERN(na)) {
- error = EBUSY;
- break;
- }
-
- if (na->virt_hdr_len && !(nmr->nr_flags & NR_ACCEPT_VNET_HDR)) {
- error = EIO;
- break;
- }
-
- error = netmap_do_regif(priv, na, nmr->nr_ringid, nmr->nr_flags);
- if (error) { /* reg. failed, release priv and ref */
- break;
- }
- nifp = priv->np_nifp;
- priv->np_td = td; // XXX kqueue, debugging only
-
- /* return the offset of the netmap_if object */
- nmr->nr_rx_rings = na->num_rx_rings;
- nmr->nr_tx_rings = na->num_tx_rings;
- nmr->nr_rx_slots = na->num_rx_desc;
- nmr->nr_tx_slots = na->num_tx_desc;
- error = netmap_mem_get_info(na->nm_mem, &memsize, &memflags,
- &nmr->nr_arg2);
- if (error) {
- netmap_do_unregif(priv);
- break;
- }
- nmr->nr_memsize = (uint32_t)memsize;
- if (memflags & NETMAP_MEM_PRIVATE) {
- *(uint32_t *)(uintptr_t)&nifp->ni_flags |= NI_PRIV_MEM;
- }
- for_rx_tx(t) {
- priv->np_si[t] = nm_si_user(priv, t) ?
- &na->si[t] : &NMR(na, t)[priv->np_qfirst[t]].si;
- }
-
- if (nmr->nr_arg3) {
- if (netmap_verbose)
- D("requested %d extra buffers", nmr->nr_arg3);
- nmr->nr_arg3 = netmap_extra_alloc(na,
- &nifp->ni_bufs_head, nmr->nr_arg3);
- if (netmap_verbose)
- D("got %d extra buffers", nmr->nr_arg3);
- }
- nmr->nr_offset = netmap_mem_if_offset(na->nm_mem, nifp);
-
- /* store ifp reference so that priv destructor may release it */
- priv->np_ifp = ifp;
- } while (0);
- if (error) {
- netmap_unget_na(na, ifp);
}
- /* release the reference from netmap_mem_find() or
- * netmap_mem_ext_create()
- */
- if (nmd)
- netmap_mem_put(nmd);
- NMG_UNLOCK();
+ /* Write back request body to userspace and reset the
+ * user-space pointer. */
+ error = nmreq_copyout(hdr, error);
break;
+ }
case NIOCTXSYNC:
- case NIOCRXSYNC:
+ case NIOCRXSYNC: {
nifp = priv->np_nifp;
if (nifp == NULL) {
@@ -2506,7 +2647,7 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread
sync_flags = priv->np_sync_flags;
for (i = qfirst; i < qlast; i++) {
- struct netmap_kring *kring = krings + i;
+ struct netmap_kring *kring = krings[i];
struct netmap_ring *ring = kring->ring;
if (unlikely(nm_kr_tryget(kring, 1, &error))) {
@@ -2549,51 +2690,292 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread
}
break;
+ }
-#ifdef WITH_VALE
- case NIOCCONFIG:
- error = netmap_bdg_config(nmr);
- break;
-#endif
-#ifdef __FreeBSD__
- case FIONBIO:
- case FIOASYNC:
- ND("FIONBIO/FIOASYNC are no-ops");
+ default: {
+ return netmap_ioctl_legacy(priv, cmd, data, td);
break;
+ }
+ }
+
+ return (error);
+}
- case BIOCIMMEDIATE:
- case BIOCGHDRCMPLT:
- case BIOCSHDRCMPLT:
- case BIOCSSEESENT:
- D("ignore BIOCIMMEDIATE/BIOCSHDRCMPLT/BIOCSHDRCMPLT/BIOCSSEESENT");
+size_t
+nmreq_size_by_type(uint16_t nr_reqtype)
+{
+ switch (nr_reqtype) {
+ case NETMAP_REQ_REGISTER:
+ return sizeof(struct nmreq_register);
+ case NETMAP_REQ_PORT_INFO_GET:
+ return sizeof(struct nmreq_port_info_get);
+ case NETMAP_REQ_VALE_ATTACH:
+ return sizeof(struct nmreq_vale_attach);
+ case NETMAP_REQ_VALE_DETACH:
+ return sizeof(struct nmreq_vale_detach);
+ case NETMAP_REQ_VALE_LIST:
+ return sizeof(struct nmreq_vale_list);
+ case NETMAP_REQ_PORT_HDR_SET:
+ case NETMAP_REQ_PORT_HDR_GET:
+ return sizeof(struct nmreq_port_hdr);
+ case NETMAP_REQ_VALE_NEWIF:
+ return sizeof(struct nmreq_vale_newif);
+ case NETMAP_REQ_VALE_DELIF:
+ return 0;
+ case NETMAP_REQ_VALE_POLLING_ENABLE:
+ case NETMAP_REQ_VALE_POLLING_DISABLE:
+ return sizeof(struct nmreq_vale_polling);
+ case NETMAP_REQ_POOLS_INFO_GET:
+ return sizeof(struct nmreq_pools_info);
+ }
+ return 0;
+}
+
+static size_t
+nmreq_opt_size_by_type(uint16_t nro_reqtype)
+{
+ size_t rv = sizeof(struct nmreq_option);
+#ifdef NETMAP_REQ_OPT_DEBUG
+ if (nro_reqtype & NETMAP_REQ_OPT_DEBUG)
+ return (nro_reqtype & ~NETMAP_REQ_OPT_DEBUG);
+#endif /* NETMAP_REQ_OPT_DEBUG */
+ switch (nro_reqtype) {
+#ifdef WITH_EXTMEM
+ case NETMAP_REQ_OPT_EXTMEM:
+ rv = sizeof(struct nmreq_opt_extmem);
break;
+#endif /* WITH_EXTMEM */
+ }
+ /* subtract the common header */
+ return rv - sizeof(struct nmreq_option);
+}
- default: /* allow device-specific ioctls */
- {
- struct ifnet *ifp = ifunit_ref(nmr->nr_name);
- if (ifp == NULL) {
- error = ENXIO;
- } else {
- struct socket so;
+int
+nmreq_copyin(struct nmreq_header *hdr, int nr_body_is_user)
+{
+ size_t rqsz, optsz, bufsz;
+ int error;
+ char *ker = NULL, *p;
+ struct nmreq_option **next, *src;
+ struct nmreq_option buf;
+ uint64_t *ptrs;
+
+ if (hdr->nr_reserved)
+ return EINVAL;
+
+ if (!nr_body_is_user)
+ return 0;
+
+ hdr->nr_reserved = nr_body_is_user;
+
+ /* compute the total size of the buffer */
+ rqsz = nmreq_size_by_type(hdr->nr_reqtype);
+ if (rqsz > NETMAP_REQ_MAXSIZE) {
+ error = EMSGSIZE;
+ goto out_err;
+ }
+ if ((rqsz && hdr->nr_body == (uint64_t)NULL) ||
+ (!rqsz && hdr->nr_body != (uint64_t)NULL)) {
+ /* Request body expected, but not found; or
+ * request body found but unexpected. */
+ error = EINVAL;
+ goto out_err;
+ }
- bzero(&so, sizeof(so));
- so.so_vnet = ifp->if_vnet;
- // so->so_proto not null.
- error = ifioctl(&so, cmd, data, td);
- if_rele(ifp);
+ bufsz = 2 * sizeof(void *) + rqsz;
+ optsz = 0;
+ for (src = (struct nmreq_option *)hdr->nr_options; src;
+ src = (struct nmreq_option *)buf.nro_next)
+ {
+ error = copyin(src, &buf, sizeof(*src));
+ if (error)
+ goto out_err;
+ optsz += sizeof(*src);
+ optsz += nmreq_opt_size_by_type(buf.nro_reqtype);
+ if (rqsz + optsz > NETMAP_REQ_MAXSIZE) {
+ error = EMSGSIZE;
+ goto out_err;
}
- break;
- }
+ bufsz += optsz + sizeof(void *);
+ }
-#else /* linux */
- default:
- error = EOPNOTSUPP;
-#endif /* linux */
+ ker = nm_os_malloc(bufsz);
+ if (ker == NULL) {
+ error = ENOMEM;
+ goto out_err;
}
+ p = ker;
- return (error);
+ /* make a copy of the user pointers */
+ ptrs = (uint64_t*)p;
+ *ptrs++ = hdr->nr_body;
+ *ptrs++ = hdr->nr_options;
+ p = (char *)ptrs;
+
+ /* copy the body */
+ error = copyin((void *)hdr->nr_body, p, rqsz);
+ if (error)
+ goto out_restore;
+ /* overwrite the user pointer with the in-kernel one */
+ hdr->nr_body = (uint64_t)p;
+ p += rqsz;
+
+ /* copy the options */
+ next = (struct nmreq_option **)&hdr->nr_options;
+ src = *next;
+ while (src) {
+ struct nmreq_option *opt;
+
+ /* copy the option header */
+ ptrs = (uint64_t *)p;
+ opt = (struct nmreq_option *)(ptrs + 1);
+ error = copyin(src, opt, sizeof(*src));
+ if (error)
+ goto out_restore;
+ /* make a copy of the user next pointer */
+ *ptrs = opt->nro_next;
+ /* overwrite the user pointer with the in-kernel one */
+ *next = opt;
+
+ /* initialize the option as not supported.
+ * Recognized options will update this field.
+ */
+ opt->nro_status = EOPNOTSUPP;
+
+ p = (char *)(opt + 1);
+
+ /* copy the option body */
+ optsz = nmreq_opt_size_by_type(opt->nro_reqtype);
+ if (optsz) {
+ /* the option body follows the option header */
+ error = copyin(src + 1, p, optsz);
+ if (error)
+ goto out_restore;
+ p += optsz;
+ }
+
+ /* move to next option */
+ next = (struct nmreq_option **)&opt->nro_next;
+ src = *next;
+ }
+ return 0;
+
+out_restore:
+ ptrs = (uint64_t *)ker;
+ hdr->nr_body = *ptrs++;
+ hdr->nr_options = *ptrs++;
+ hdr->nr_reserved = 0;
+ nm_os_free(ker);
+out_err:
+ return error;
}
+static int
+nmreq_copyout(struct nmreq_header *hdr, int rerror)
+{
+ struct nmreq_option *src, *dst;
+ void *ker = (void *)hdr->nr_body, *bufstart;
+ uint64_t *ptrs;
+ size_t bodysz;
+ int error;
+
+ if (!hdr->nr_reserved)
+ return rerror;
+
+ /* restore the user pointers in the header */
+ ptrs = (uint64_t *)ker - 2;
+ bufstart = ptrs;
+ hdr->nr_body = *ptrs++;
+ src = (struct nmreq_option *)hdr->nr_options;
+ hdr->nr_options = *ptrs;
+
+ if (!rerror) {
+ /* copy the body */
+ bodysz = nmreq_size_by_type(hdr->nr_reqtype);
+ error = copyout(ker, (void *)hdr->nr_body, bodysz);
+ if (error) {
+ rerror = error;
+ goto out;
+ }
+ }
+
+ /* copy the options */
+ dst = (struct nmreq_option *)hdr->nr_options;
+ while (src) {
+ size_t optsz;
+ uint64_t next;
+
+ /* restore the user pointer */
+ next = src->nro_next;
+ ptrs = (uint64_t *)src - 1;
+ src->nro_next = *ptrs;
+
+ /* always copy the option header */
+ error = copyout(src, dst, sizeof(*src));
+ if (error) {
+ rerror = error;
+ goto out;
+ }
+
+ /* copy the option body only if there was no error */
+ if (!rerror && !src->nro_status) {
+ optsz = nmreq_opt_size_by_type(src->nro_reqtype);
+ if (optsz) {
+ error = copyout(src + 1, dst + 1, optsz);
+ if (error) {
+ rerror = error;
+ goto out;
+ }
+ }
+ }
+ src = (struct nmreq_option *)next;
+ dst = (struct nmreq_option *)*ptrs;
+ }
+
+
+out:
+ hdr->nr_reserved = 0;
+ nm_os_free(bufstart);
+ return rerror;
+}
+
+struct nmreq_option *
+nmreq_findoption(struct nmreq_option *opt, uint16_t reqtype)
+{
+ for ( ; opt; opt = (struct nmreq_option *)opt->nro_next)
+ if (opt->nro_reqtype == reqtype)
+ return opt;
+ return NULL;
+}
+
+int
+nmreq_checkduplicate(struct nmreq_option *opt) {
+ uint16_t type = opt->nro_reqtype;
+ int dup = 0;
+
+ while ((opt = nmreq_findoption((struct nmreq_option *)opt->nro_next,
+ type))) {
+ dup++;
+ opt->nro_status = EINVAL;
+ }
+ return (dup ? EINVAL : 0);
+}
+
+static int
+nmreq_checkoptions(struct nmreq_header *hdr)
+{
+ struct nmreq_option *opt;
+ /* return error if there is still any option
+ * marked as not supported
+ */
+
+ for (opt = (struct nmreq_option *)hdr->nr_options; opt;
+ opt = (struct nmreq_option *)opt->nro_next)
+ if (opt->nro_status == EOPNOTSUPP)
+ return EOPNOTSUPP;
+
+ return 0;
+}
/*
* select(2) and poll(2) handlers for the "netmap" device.
@@ -2680,7 +3062,7 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
if (want_tx) {
enum txrx t = NR_TX;
for (i = priv->np_qfirst[t]; want[t] && i < priv->np_qlast[t]; i++) {
- kring = &NMR(na, t)[i];
+ kring = NMR(na, t)[i];
/* XXX compare ring->cur and kring->tail */
if (!nm_ring_empty(kring->ring)) {
revents |= want[t];
@@ -2692,7 +3074,7 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
enum txrx t = NR_RX;
want_rx = 0; /* look for a reason to run the handlers */
for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
- kring = &NMR(na, t)[i];
+ kring = NMR(na, t)[i];
if (kring->ring->cur == kring->ring->tail /* try fetch new buffers */
|| kring->rhead != kring->ring->head /* release buffers */) {
want_rx = 1;
@@ -2706,9 +3088,9 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
#ifdef linux
/* The selrecord must be unconditional on linux. */
nm_os_selrecord(sr, check_all_tx ?
- &na->si[NR_TX] : &na->tx_rings[priv->np_qfirst[NR_TX]].si);
+ &na->si[NR_TX] : &na->tx_rings[priv->np_qfirst[NR_TX]]->si);
nm_os_selrecord(sr, check_all_rx ?
- &na->si[NR_RX] : &na->rx_rings[priv->np_qfirst[NR_RX]].si);
+ &na->si[NR_RX] : &na->rx_rings[priv->np_qfirst[NR_RX]]->si);
#endif /* linux */
/*
@@ -2728,16 +3110,16 @@ flush_tx:
for (i = priv->np_qfirst[NR_TX]; i < priv->np_qlast[NR_TX]; i++) {
int found = 0;
- kring = &na->tx_rings[i];
+ kring = na->tx_rings[i];
ring = kring->ring;
/*
* Don't try to txsync this TX ring if we already found some
* space in some of the TX rings (want_tx == 0) and there are no
* TX slots in this ring that need to be flushed to the NIC
- * (cur == hwcur).
+ * (head == hwcur).
*/
- if (!send_down && !want_tx && ring->cur == kring->nr_hwcur)
+ if (!send_down && !want_tx && ring->head == kring->nr_hwcur)
continue;
if (nm_kr_tryget(kring, 1, &revents))
@@ -2774,7 +3156,7 @@ flush_tx:
if (want_tx && retry_tx && sr) {
#ifndef linux
nm_os_selrecord(sr, check_all_tx ?
- &na->si[NR_TX] : &na->tx_rings[priv->np_qfirst[NR_TX]].si);
+ &na->si[NR_TX] : &na->tx_rings[priv->np_qfirst[NR_TX]]->si);
#endif /* !linux */
retry_tx = 0;
goto flush_tx;
@@ -2791,7 +3173,7 @@ do_retry_rx:
for (i = priv->np_qfirst[NR_RX]; i < priv->np_qlast[NR_RX]; i++) {
int found = 0;
- kring = &na->rx_rings[i];
+ kring = na->rx_rings[i];
ring = kring->ring;
if (unlikely(nm_kr_tryget(kring, 1, &revents)))
@@ -2835,7 +3217,7 @@ do_retry_rx:
#ifndef linux
if (retry_rx && sr) {
nm_os_selrecord(sr, check_all_rx ?
- &na->si[NR_RX] : &na->rx_rings[priv->np_qfirst[NR_RX]].si);
+ &na->si[NR_RX] : &na->rx_rings[priv->np_qfirst[NR_RX]]->si);
}
#endif /* !linux */
if (send_down || retry_rx) {
@@ -2871,7 +3253,7 @@ nma_intr_enable(struct netmap_adapter *na, int onoff)
for_rx_tx(t) {
for (i = 0; i < nma_get_nrings(na, t); i++) {
- struct netmap_kring *kring = &NMR(na, t)[i];
+ struct netmap_kring *kring = NMR(na, t)[i];
int on = !(kring->nr_kflags & NKR_NOINTR);
if (!!onoff != !!on) {
@@ -2907,7 +3289,7 @@ nma_intr_enable(struct netmap_adapter *na, int onoff)
static int
netmap_notify(struct netmap_kring *kring, int flags)
{
- struct netmap_adapter *na = kring->na;
+ struct netmap_adapter *na = kring->notify_na;
enum txrx t = kring->tx;
nm_os_selwakeup(&kring->si);
@@ -2934,6 +3316,11 @@ netmap_attach_common(struct netmap_adapter *na)
return EINVAL;
}
+ if (!na->rx_buf_maxsize) {
+ /* Set a conservative default (larger is safer). */
+ na->rx_buf_maxsize = PAGE_SIZE;
+ }
+
#ifdef __FreeBSD__
if (na->na_flags & NAF_HOST_RINGS && na->ifp) {
na->if_input = na->ifp->if_input; /* for netmap_send_up */
@@ -3149,7 +3536,7 @@ netmap_hw_krings_create(struct netmap_adapter *na)
int ret = netmap_krings_create(na, 0);
if (ret == 0) {
/* initialize the mbq for the sw rx ring */
- mbq_safe_init(&na->rx_rings[na->num_rx_rings].rx_queue);
+ mbq_safe_init(&na->rx_rings[na->num_rx_rings]->rx_queue);
ND("initialized sw rx queue %d", na->num_rx_rings);
}
return ret;
@@ -3213,7 +3600,7 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
struct mbq *q;
int busy;
- kring = &na->rx_rings[na->num_rx_rings];
+ kring = na->rx_rings[na->num_rx_rings];
// XXX [Linux] we do not need this lock
// if we follow the down/configure/up protocol -gl
// mtx_lock(&na->core_lock);
@@ -3228,7 +3615,7 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
if (txr >= na->num_tx_rings) {
txr %= na->num_tx_rings;
}
- tx_kring = &NMR(na, NR_TX)[txr];
+ tx_kring = NMR(na, NR_TX)[txr];
if (tx_kring->nr_mode == NKR_NETMAP_OFF) {
return MBUF_TRANSMIT(na, ifp, m);
@@ -3316,7 +3703,7 @@ netmap_reset(struct netmap_adapter *na, enum txrx tx, u_int n,
if (n >= na->num_tx_rings)
return NULL;
- kring = na->tx_rings + n;
+ kring = na->tx_rings[n];
if (kring->nr_pending_mode == NKR_NETMAP_OFF) {
kring->nr_mode = NKR_NETMAP_OFF;
@@ -3328,7 +3715,7 @@ netmap_reset(struct netmap_adapter *na, enum txrx tx, u_int n,
} else {
if (n >= na->num_rx_rings)
return NULL;
- kring = na->rx_rings + n;
+ kring = na->rx_rings[n];
if (kring->nr_pending_mode == NKR_NETMAP_OFF) {
kring->nr_mode = NKR_NETMAP_OFF;
@@ -3396,7 +3783,7 @@ netmap_common_irq(struct netmap_adapter *na, u_int q, u_int *work_done)
if (q >= nma_get_nrings(na, t))
return NM_IRQ_PASS; // not a physical queue
- kring = NMR(na, t) + q;
+ kring = NMR(na, t)[q];
if (kring->nr_mode == NKR_NETMAP_OFF) {
return NM_IRQ_PASS;