#if defined(__FreeBSD__)
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/sockio.h>
#include <sys/socketvar.h>
#include <sys/malloc.h>
#include <sys/poll.h>
#include <sys/rwlock.h>
#include <sys/socket.h>
#include <sys/selinfo.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/bpf.h>
#include <machine/bus.h>
#include <sys/endian.h>
#include <sys/refcount.h>
#include <sys/smp.h>
#elif defined(linux)
#include "bsd_glue.h"
#elif defined(__APPLE__)
#warning OSX support is only partial
#include "osx_glue.h"
#elif defined(_WIN32)
#include "win_glue.h"
#else
#error Unsupported platform
#endif
#include <net/netmap.h>
#include <dev/netmap/netmap_kern.h>
#include <dev/netmap/netmap_mem2.h>
#include <dev/netmap/netmap_bdg.h>
const char*
netmap_bdg_name(struct netmap_vp_adapter *vp)
{
struct nm_bridge *b = vp->na_bdg;
if (b == NULL)
return NULL;
return b->bdg_basename;
}
#ifndef CONFIG_NET_NS
struct nm_bridge *nm_bridges;
#endif
static int
nm_is_id_char(const char c)
{
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
(c == '_');
}
static int
nm_bdg_name_validate(const char *name, size_t prefixlen)
{
int colon_pos = -1;
int i;
if (!name || strlen(name) < prefixlen) {
return -1;
}
for (i = 0; i < NM_BDG_IFNAMSIZ && name[i]; i++) {
if (name[i] == ':') {
colon_pos = i;
break;
} else if (!nm_is_id_char(name[i])) {
return -1;
}
}
if (strlen(name) - colon_pos > IFNAMSIZ) {
return -1;
}
return colon_pos;
}
struct nm_bridge *
nm_find_bridge(const char *name, int create, struct netmap_bdg_ops *ops)
{
int i, namelen;
struct nm_bridge *b = NULL, *bridges;
u_int num_bridges;
NMG_LOCK_ASSERT();
netmap_bns_getbridges(&bridges, &num_bridges);
namelen = nm_bdg_name_validate(name,
(ops != NULL ? strlen(ops->name) : 0));
if (namelen < 0) {
nm_prerr("invalid bridge name %s", name ? name : NULL);
return NULL;
}
for (i = 0; i < num_bridges; i++) {
struct nm_bridge *x = bridges + i;
if ((x->bdg_flags & NM_BDG_ACTIVE) + x->bdg_active_ports == 0) {
if (create && b == NULL)
b = x;
} else if (x->bdg_namelen != namelen) {
continue;
} else if (strncmp(name, x->bdg_basename, namelen) == 0) {
nm_prdis("found '%.*s' at %d", namelen, name, i);
b = x;
break;
}
}
if (i == num_bridges && b) {
nm_prdis("create new bridge %s with ports %d", b->bdg_basename,
b->bdg_active_ports);
b->ht = nm_os_malloc(sizeof(struct nm_hash_ent) * NM_BDG_HASH);
if (b->ht == NULL) {
nm_prerr("failed to allocate hash table");
return NULL;
}
strncpy(b->bdg_basename, name, namelen);
b->bdg_namelen = namelen;
b->bdg_active_ports = 0;
for (i = 0; i < NM_BDG_MAXPORTS; i++)
b->bdg_port_index[i] = i;
b->bdg_ops = b->bdg_saved_ops = *ops;
b->private_data = b->ht;
b->bdg_flags = 0;
NM_BNS_GET(b);
}
return b;
}
int
netmap_bdg_free(struct nm_bridge *b)
{
if ((b->bdg_flags & NM_BDG_ACTIVE) + b->bdg_active_ports != 0) {
return EBUSY;
}
nm_prdis("marking bridge %s as free", b->bdg_basename);
nm_os_free(b->ht);
memset(&b->bdg_ops, 0, sizeof(b->bdg_ops));
memset(&b->bdg_saved_ops, 0, sizeof(b->bdg_saved_ops));
b->bdg_flags = 0;
NM_BNS_PUT(b);
return 0;
}
int
netmap_bdg_update_private_data(const char *name, bdg_update_private_data_fn_t callback,
void *callback_data, void *auth_token)
{
void *private_data = NULL;
struct nm_bridge *b;
int error = 0;
NMG_LOCK();
b = nm_find_bridge(name, 0 , NULL);
if (!b) {
error = EINVAL;
goto unlock_update_priv;
}
if (!nm_bdg_valid_auth_token(b, auth_token)) {
error = EACCES;
goto unlock_update_priv;
}
BDG_WLOCK(b);
private_data = callback(b->private_data, callback_data, &error);
b->private_data = private_data;
BDG_WUNLOCK(b);
unlock_update_priv:
NMG_UNLOCK();
return error;
}
void
netmap_bdg_detach_common(struct nm_bridge *b, int hw, int sw)
{
int s_hw = hw, s_sw = sw;
int i, lim =b->bdg_active_ports;
uint32_t *tmp = b->tmp_bdg_port_index;
if (netmap_debug & NM_DEBUG_BDG)
nm_prinf("detach %d and %d (lim %d)", hw, sw, lim);
memcpy(b->tmp_bdg_port_index, b->bdg_port_index, sizeof(b->tmp_bdg_port_index));
for (i = 0; (hw >= 0 || sw >= 0) && i < lim; ) {
if (hw >= 0 && tmp[i] == hw) {
nm_prdis("detach hw %d at %d", hw, i);
lim--;
tmp[i] = tmp[lim];
tmp[lim] = hw;
hw = -1;
} else if (sw >= 0 && tmp[i] == sw) {
nm_prdis("detach sw %d at %d", sw, i);
lim--;
tmp[i] = tmp[lim];
tmp[lim] = sw;
sw = -1;
} else {
i++;
}
}
if (hw >= 0 || sw >= 0) {
nm_prerr("delete failed hw %d sw %d, should panic...", hw, sw);
}
BDG_WLOCK(b);
if (b->bdg_ops.dtor)
b->bdg_ops.dtor(b->bdg_ports[s_hw]);
b->bdg_ports[s_hw] = NULL;
if (s_sw >= 0) {
b->bdg_ports[s_sw] = NULL;
}
memcpy(b->bdg_port_index, b->tmp_bdg_port_index, sizeof(b->tmp_bdg_port_index));
b->bdg_active_ports = lim;
BDG_WUNLOCK(b);
nm_prdis("now %d active ports", lim);
netmap_bdg_free(b);
}
int
netmap_vp_bdg_ctl(struct nmreq_header *hdr, struct netmap_adapter *na)
{
struct netmap_vp_adapter *vpna = (struct netmap_vp_adapter *)na;
struct nm_bridge *b = vpna->na_bdg;
if (hdr->nr_reqtype == NETMAP_REQ_VALE_ATTACH) {
return 0;
}
if (b) {
netmap_set_all_rings(na, 0 );
netmap_bdg_detach_common(b, vpna->bdg_port, -1);
vpna->na_bdg = NULL;
netmap_set_all_rings(na, 1 );
}
netmap_adapter_put(na);
return 0;
}
int
netmap_default_bdg_attach(const char *name, struct netmap_adapter *na,
struct nm_bridge *b)
{
return NM_NEED_BWRAP;
}
int
netmap_get_bdg_na(struct nmreq_header *hdr, struct netmap_adapter **na,
struct netmap_mem_d *nmd, int create, struct netmap_bdg_ops *ops)
{
char *nr_name = hdr->nr_name;
const char *ifname;
if_t ifp = NULL;
int error = 0;
struct netmap_vp_adapter *vpna, *hostna = NULL;
struct nm_bridge *b;
uint32_t i, j;
uint32_t cand = NM_BDG_NOPORT, cand2 = NM_BDG_NOPORT;
int needed;
*na = NULL;
NMG_LOCK_ASSERT();
if (strncmp(nr_name, ops->name, strlen(ops->name) - 1)) {
return 0;
}
b = nm_find_bridge(nr_name, create, ops);
if (b == NULL) {
nm_prdis("no bridges available for '%s'", nr_name);
return (create ? ENOMEM : ENXIO);
}
if (strlen(nr_name) < b->bdg_namelen)
panic("x");
for (j = 0; j < b->bdg_active_ports; j++) {
i = b->bdg_port_index[j];
vpna = b->bdg_ports[i];
nm_prdis("checking %s", vpna->up.name);
if (!strcmp(vpna->up.name, nr_name)) {
netmap_adapter_get(&vpna->up);
nm_prdis("found existing if %s refs %d", nr_name)
*na = &vpna->up;
return 0;
}
}
if (!create)
return ENXIO;
needed = 2;
if (b->bdg_active_ports + needed >= NM_BDG_MAXPORTS) {
nm_prerr("bridge full %d, cannot create new port", b->bdg_active_ports);
return ENOMEM;
}
cand = b->bdg_port_index[b->bdg_active_ports];
cand2 = b->bdg_port_index[b->bdg_active_ports + 1];
nm_prdis("+++ bridge %s port %s used %d avail %d %d",
b->bdg_basename, ifname, b->bdg_active_ports, cand, cand2);
ifname = nr_name + b->bdg_namelen + 1;
ifp = ifunit_ref(ifname);
if (!ifp) {
if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) {
error = EINVAL;
goto out;
}
error = b->bdg_ops.vp_create(hdr, NULL, nmd, &vpna);
if (error) {
if (netmap_debug & NM_DEBUG_BDG)
nm_prerr("error %d", error);
goto out;
}
} else {
struct netmap_adapter *hw;
switch (hdr->nr_reqtype) {
case NETMAP_REQ_VALE_ATTACH:
case NETMAP_REQ_VALE_DETACH:
case NETMAP_REQ_VALE_POLLING_ENABLE:
case NETMAP_REQ_VALE_POLLING_DISABLE:
break;
default:
error = EINVAL;
goto out;
}
error = netmap_get_hw_na(ifp, nmd, &hw);
if (error || hw == NULL)
goto out;
error = hw->nm_bdg_attach(nr_name, hw, b);
if (error == NM_NEED_BWRAP) {
error = b->bdg_ops.bwrap_attach(nr_name, hw);
}
if (error)
goto out;
vpna = hw->na_vp;
hostna = hw->na_hostvp;
if (hdr->nr_reqtype == NETMAP_REQ_VALE_ATTACH) {
struct nmreq_vale_attach *areq =
(struct nmreq_vale_attach *)(uintptr_t)hdr->nr_body;
if (areq->reg.nr_mode != NR_REG_NIC_SW) {
hostna = NULL;
}
}
}
BDG_WLOCK(b);
vpna->bdg_port = cand;
nm_prdis("NIC %p to bridge port %d", vpna, cand);
b->bdg_ports[cand] = vpna;
vpna->na_bdg = b;
b->bdg_active_ports++;
if (hostna != NULL) {
b->bdg_ports[cand2] = hostna;
hostna->bdg_port = cand2;
hostna->na_bdg = b;
b->bdg_active_ports++;
nm_prdis("host %p to bridge port %d", hostna, cand2);
}
nm_prdis("if %s refs %d", ifname, vpna->up.na_refcount);
BDG_WUNLOCK(b);
*na = &vpna->up;
netmap_adapter_get(*na);
out:
if (ifp)
if_rele(ifp);
return error;
}
int
netmap_bdg_attach(struct nmreq_header *hdr, void *auth_token)
{
struct nmreq_vale_attach *req =
(struct nmreq_vale_attach *)(uintptr_t)hdr->nr_body;
struct netmap_vp_adapter * vpna;
struct netmap_adapter *na = NULL;
struct netmap_mem_d *nmd = NULL;
struct nm_bridge *b = NULL;
int error;
NMG_LOCK();
b = nm_find_bridge(hdr->nr_name, 0 , NULL);
if (b && !nm_bdg_valid_auth_token(b, auth_token)) {
error = EACCES;
goto unlock_exit;
}
if (req->reg.nr_mem_id) {
nmd = netmap_mem_find(req->reg.nr_mem_id);
if (nmd == NULL) {
error = EINVAL;
goto unlock_exit;
}
}
error = netmap_get_vale_na(hdr, &na, nmd, 0);
if (na) {
error = EBUSY;
goto unref_exit;
}
error = netmap_get_vale_na(hdr, &na,
nmd, 1 );
if (error) {
goto unlock_exit;
}
if (na == NULL) {
error = EINVAL;
goto unlock_exit;
}
if (NETMAP_OWNED_BY_ANY(na)) {
error = EBUSY;
goto unref_exit;
}
if (na->nm_bdg_ctl) {
error = na->nm_bdg_ctl(hdr, na);
if (error)
goto unref_exit;
nm_prdis("registered %s to netmap-mode", na->name);
}
vpna = (struct netmap_vp_adapter *)na;
req->port_index = vpna->bdg_port;
if (nmd)
netmap_mem_put(nmd);
NMG_UNLOCK();
return 0;
unref_exit:
netmap_adapter_put(na);
unlock_exit:
if (nmd)
netmap_mem_put(nmd);
NMG_UNLOCK();
return error;
}
int
nm_is_bwrap(struct netmap_adapter *na)
{
return na->nm_register == netmap_bwrap_reg;
}
int
netmap_bdg_detach(struct nmreq_header *hdr, void *auth_token)
{
int error;
NMG_LOCK();
error = netmap_bdg_detach_locked(hdr, auth_token);
NMG_UNLOCK();
return error;
}
int
netmap_bdg_detach_locked(struct nmreq_header *hdr, void *auth_token)
{
struct nmreq_vale_detach *nmreq_det = (void *)(uintptr_t)hdr->nr_body;
struct netmap_vp_adapter *vpna;
struct netmap_adapter *na;
struct nm_bridge *b = NULL;
int error;
b = nm_find_bridge(hdr->nr_name, 0 , NULL);
if (b && !nm_bdg_valid_auth_token(b, auth_token)) {
error = EACCES;
goto error_exit;
}
error = netmap_get_vale_na(hdr, &na, NULL, 0 );
if (error) {
goto error_exit;
}
if (na == NULL) {
error = EINVAL;
goto error_exit;
} else if (nm_is_bwrap(na) &&
((struct netmap_bwrap_adapter *)na)->na_polling_state) {
error = EBUSY;
goto unref_exit;
}
vpna = (struct netmap_vp_adapter *)na;
if (na->na_vp != vpna) {
error = EBUSY;
goto unref_exit;
}
nmreq_det->port_index = vpna->bdg_port;
if (na->nm_bdg_ctl) {
error = na->nm_bdg_ctl(hdr, na);
}
unref_exit:
netmap_adapter_put(na);
error_exit:
return error;
}
struct nm_bdg_polling_state;
struct
nm_bdg_kthread {
struct nm_kctx *nmk;
u_int qfirst;
u_int qlast;
struct nm_bdg_polling_state *bps;
};
struct nm_bdg_polling_state {
bool configured;
bool stopped;
struct netmap_bwrap_adapter *bna;
uint32_t mode;
u_int qfirst;
u_int qlast;
u_int cpu_from;
u_int ncpus;
struct nm_bdg_kthread *kthreads;
};
static void
netmap_bwrap_polling(void *data)
{
struct nm_bdg_kthread *nbk = data;
struct netmap_bwrap_adapter *bna;
u_int qfirst, qlast, i;
struct netmap_kring **kring0, *kring;
if (!nbk)
return;
qfirst = nbk->qfirst;
qlast = nbk->qlast;
bna = nbk->bps->bna;
kring0 = NMR(bna->hwna, NR_RX);
for (i = qfirst; i < qlast; i++) {
kring = kring0[i];
kring->nm_notify(kring, 0);
}
}
static int
nm_bdg_create_kthreads(struct nm_bdg_polling_state *bps)
{
struct nm_kctx_cfg kcfg;
int i, j;
bps->kthreads = nm_os_malloc(sizeof(struct nm_bdg_kthread) * bps->ncpus);
if (bps->kthreads == NULL)
return ENOMEM;
bzero(&kcfg, sizeof(kcfg));
kcfg.worker_fn = netmap_bwrap_polling;
for (i = 0; i < bps->ncpus; i++) {
struct nm_bdg_kthread *t = bps->kthreads + i;
int all = (bps->ncpus == 1 &&
bps->mode == NETMAP_POLLING_MODE_SINGLE_CPU);
int affinity = bps->cpu_from + i;
t->bps = bps;
t->qfirst = all ? bps->qfirst : affinity;
t->qlast = all ? bps->qlast : t->qfirst + 1;
if (netmap_verbose)
nm_prinf("kthread %d a:%u qf:%u ql:%u", i, affinity, t->qfirst,
t->qlast);
kcfg.type = i;
kcfg.worker_private = t;
t->nmk = nm_os_kctx_create(&kcfg, NULL);
if (t->nmk == NULL) {
goto cleanup;
}
nm_os_kctx_worker_setaff(t->nmk, affinity);
}
return 0;
cleanup:
for (j = 0; j < i; j++) {
struct nm_bdg_kthread *t = bps->kthreads + i;
nm_os_kctx_destroy(t->nmk);
}
nm_os_free(bps->kthreads);
return EFAULT;
}
static int
nm_bdg_polling_start_kthreads(struct nm_bdg_polling_state *bps)
{
int error, i, j;
if (!bps) {
nm_prerr("polling is not configured");
return EFAULT;
}
bps->stopped = false;
for (i = 0; i < bps->ncpus; i++) {
struct nm_bdg_kthread *t = bps->kthreads + i;
error = nm_os_kctx_worker_start(t->nmk);
if (error) {
nm_prerr("error in nm_kthread_start(): %d", error);
goto cleanup;
}
}
return 0;
cleanup:
for (j = 0; j < i; j++) {
struct nm_bdg_kthread *t = bps->kthreads + i;
nm_os_kctx_worker_stop(t->nmk);
}
bps->stopped = true;
return error;
}
static void
nm_bdg_polling_stop_delete_kthreads(struct nm_bdg_polling_state *bps)
{
int i;
if (!bps)
return;
for (i = 0; i < bps->ncpus; i++) {
struct nm_bdg_kthread *t = bps->kthreads + i;
nm_os_kctx_worker_stop(t->nmk);
nm_os_kctx_destroy(t->nmk);
}
bps->stopped = true;
}
static int
get_polling_cfg(struct nmreq_vale_polling *req, struct netmap_adapter *na,
struct nm_bdg_polling_state *bps)
{
unsigned int avail_cpus, core_from;
unsigned int qfirst, qlast;
uint32_t i = req->nr_first_cpu_id;
uint32_t req_cpus = req->nr_num_polling_cpus;
avail_cpus = nm_os_ncpus();
if (req_cpus == 0) {
nm_prerr("req_cpus must be > 0");
return EINVAL;
} else if (req_cpus >= avail_cpus) {
nm_prerr("Cannot use all the CPUs in the system");
return EINVAL;
}
if (req->nr_mode == NETMAP_POLLING_MODE_MULTI_CPU) {
if (i + req_cpus > nma_get_nrings(na, NR_RX)) {
nm_prerr("Rings %u-%u not in range (have %d rings)",
i, i + req_cpus, nma_get_nrings(na, NR_RX));
return EINVAL;
}
qfirst = i;
qlast = qfirst + req_cpus;
core_from = qfirst;
} else if (req->nr_mode == NETMAP_POLLING_MODE_SINGLE_CPU) {
if (req_cpus != 1) {
nm_prerr("ncpus must be 1 for NETMAP_POLLING_MODE_SINGLE_CPU "
"(was %d)", req_cpus);
return EINVAL;
}
qfirst = 0;
qlast = nma_get_nrings(na, NR_RX);
core_from = i;
} else {
nm_prerr("Invalid polling mode");
return EINVAL;
}
bps->mode = req->nr_mode;
bps->qfirst = qfirst;
bps->qlast = qlast;
bps->cpu_from = core_from;
bps->ncpus = req_cpus;
nm_prinf("%s qfirst %u qlast %u cpu_from %u ncpus %u",
req->nr_mode == NETMAP_POLLING_MODE_MULTI_CPU ?
"MULTI" : "SINGLE",
qfirst, qlast, core_from, req_cpus);
return 0;
}
static int
nm_bdg_ctl_polling_start(struct nmreq_vale_polling *req, struct netmap_adapter *na)
{
struct nm_bdg_polling_state *bps;
struct netmap_bwrap_adapter *bna;
int error;
bna = (struct netmap_bwrap_adapter *)na;
if (bna->na_polling_state) {
nm_prerr("ERROR adapter already in polling mode");
return EFAULT;
}
bps = nm_os_malloc(sizeof(*bps));
if (!bps)
return ENOMEM;
bps->configured = false;
bps->stopped = true;
if (get_polling_cfg(req, na, bps)) {
nm_os_free(bps);
return EINVAL;
}
if (nm_bdg_create_kthreads(bps)) {
nm_os_free(bps);
return EFAULT;
}
bps->configured = true;
bna->na_polling_state = bps;
bps->bna = bna;
nma_intr_enable(bna->hwna, 0);
error = nm_bdg_polling_start_kthreads(bps);
if (error) {
nm_prerr("ERROR nm_bdg_polling_start_kthread()");
nm_os_free(bps->kthreads);
nm_os_free(bps);
bna->na_polling_state = NULL;
nma_intr_enable(bna->hwna, 1);
}
return error;
}
static int
nm_bdg_ctl_polling_stop(struct netmap_adapter *na)
{
struct netmap_bwrap_adapter *bna = (struct netmap_bwrap_adapter *)na;
struct nm_bdg_polling_state *bps;
if (!bna->na_polling_state) {
nm_prerr("ERROR adapter is not in polling mode");
return EFAULT;
}
bps = bna->na_polling_state;
nm_bdg_polling_stop_delete_kthreads(bna->na_polling_state);
bps->configured = false;
nm_os_free(bps);
bna->na_polling_state = NULL;
nma_intr_enable(bna->hwna, 1);
return 0;
}
int
nm_bdg_polling(struct nmreq_header *hdr)
{
struct nmreq_vale_polling *req =
(struct nmreq_vale_polling *)(uintptr_t)hdr->nr_body;
struct netmap_adapter *na = NULL;
int error = 0;
NMG_LOCK();
error = netmap_get_vale_na(hdr, &na, NULL, 0);
if (na && !error) {
if (!nm_is_bwrap(na)) {
error = EOPNOTSUPP;
} else if (hdr->nr_reqtype == NETMAP_BDG_POLLING_ON) {
error = nm_bdg_ctl_polling_start(req, na);
if (!error)
netmap_adapter_get(na);
} else {
error = nm_bdg_ctl_polling_stop(na);
if (!error)
netmap_adapter_put(na);
}
netmap_adapter_put(na);
} else if (!na && !error) {
error = EINVAL;
}
NMG_UNLOCK();
return error;
}
int
netmap_bdg_regops(const char *name, struct netmap_bdg_ops *bdg_ops, void *private_data, void *auth_token)
{
struct nm_bridge *b;
int error = 0;
NMG_LOCK();
b = nm_find_bridge(name, 0 , NULL);
if (!b) {
error = ENXIO;
goto unlock_regops;
}
if (!nm_bdg_valid_auth_token(b, auth_token)) {
error = EACCES;
goto unlock_regops;
}
BDG_WLOCK(b);
if (!bdg_ops) {
bzero(b->ht, sizeof(struct nm_hash_ent) * NM_BDG_HASH);
b->bdg_ops = b->bdg_saved_ops;
b->private_data = b->ht;
} else {
b->private_data = private_data;
#define nm_bdg_override(m) if (bdg_ops->m) b->bdg_ops.m = bdg_ops->m
nm_bdg_override(lookup);
nm_bdg_override(config);
nm_bdg_override(dtor);
nm_bdg_override(vp_create);
nm_bdg_override(bwrap_attach);
#undef nm_bdg_override
}
BDG_WUNLOCK(b);
unlock_regops:
NMG_UNLOCK();
return error;
}
int
netmap_bdg_config(struct nm_ifreq *nr)
{
struct nm_bridge *b;
int error = EINVAL;
NMG_LOCK();
b = nm_find_bridge(nr->nifr_name, 0, NULL);
if (!b) {
NMG_UNLOCK();
return error;
}
NMG_UNLOCK();
BDG_RLOCK(b);
if (b->bdg_ops.config != NULL)
error = b->bdg_ops.config(nr);
BDG_RUNLOCK(b);
return error;
}
int
netmap_vp_reg(struct netmap_adapter *na, int onoff)
{
struct netmap_vp_adapter *vpna =
(struct netmap_vp_adapter*)na;
if (vpna->na_bdg)
BDG_WLOCK(vpna->na_bdg);
if (onoff) {
netmap_krings_mode_commit(na, onoff);
if (na->active_fds == 0)
na->na_flags |= NAF_NETMAP_ON;
} else {
if (na->active_fds == 0)
na->na_flags &= ~NAF_NETMAP_ON;
netmap_krings_mode_commit(na, onoff);
}
if (vpna->na_bdg)
BDG_WUNLOCK(vpna->na_bdg);
return 0;
}
static int
netmap_vp_rxsync_locked(struct netmap_kring *kring, int flags)
{
struct netmap_adapter *na = kring->na;
struct netmap_ring *ring = kring->ring;
u_int nm_i, lim = kring->nkr_num_slots - 1;
u_int head = kring->rhead;
int n;
if (head > lim) {
nm_prerr("ouch dangerous reset!!!");
n = netmap_ring_reinit(kring);
goto done;
}
nm_i = kring->nr_hwcur;
if (nm_i != head) {
for (n = 0; likely(nm_i != head); n++) {
struct netmap_slot *slot = &ring->slot[nm_i];
void *addr = NMB(na, slot);
if (addr == NETMAP_BUF_BASE(kring->na)) {
nm_prerr("bad buffer index %d, ignore ?",
slot->buf_idx);
}
slot->flags &= ~NS_BUF_CHANGED;
nm_i = nm_next(nm_i, lim);
}
kring->nr_hwcur = head;
}
n = 0;
done:
return n;
}
int
netmap_vp_rxsync(struct netmap_kring *kring, int flags)
{
int n;
mtx_lock(&kring->q_lock);
n = netmap_vp_rxsync_locked(kring, flags);
mtx_unlock(&kring->q_lock);
return n;
}
int
netmap_bwrap_attach(const char *nr_name, struct netmap_adapter *hwna,
struct netmap_bdg_ops *ops)
{
return ops->bwrap_attach(nr_name, hwna);
}
static void
netmap_bwrap_dtor(struct netmap_adapter *na)
{
struct netmap_bwrap_adapter *bna = (struct netmap_bwrap_adapter*)na;
struct netmap_adapter *hwna = bna->hwna;
struct nm_bridge *b = bna->up.na_bdg,
*bh = bna->host.na_bdg;
if (bna->host.up.nm_mem)
netmap_mem_put(bna->host.up.nm_mem);
if (b) {
netmap_bdg_detach_common(b, bna->up.bdg_port,
(bh ? bna->host.bdg_port : -1));
}
nm_prdis("na %p", na);
na->ifp = NULL;
bna->host.up.ifp = NULL;
hwna->na_vp = bna->saved_na_vp;
hwna->na_hostvp = NULL;
hwna->na_private = NULL;
hwna->na_flags &= ~NAF_BUSY;
netmap_adapter_put(hwna);
}
int
netmap_bwrap_intr_notify(struct netmap_kring *kring, int flags)
{
struct netmap_adapter *na = kring->na;
struct netmap_bwrap_adapter *bna = na->na_private;
struct netmap_kring *bkring;
struct netmap_vp_adapter *vpna = &bna->up;
u_int ring_nr = kring->ring_id;
int ret = NM_IRQ_COMPLETED;
int error;
if (netmap_debug & NM_DEBUG_RXINTR)
nm_prinf("%s %s 0x%x", na->name, kring->name, flags);
bkring = vpna->up.tx_rings[ring_nr];
if (nm_kr_tryget(kring, 0 , NULL)) {
return EIO;
}
if (netmap_debug & NM_DEBUG_RXINTR)
nm_prinf("%s head %d cur %d tail %d", na->name,
kring->rhead, kring->rcur, kring->rtail);
error = kring->nm_sync(kring, 0);
if (error)
goto put_out;
if (kring->nr_hwcur == kring->nr_hwtail) {
if (netmap_verbose)
nm_prlim(1, "interrupt with no packets on %s",
kring->name);
goto put_out;
}
bkring->rhead = bkring->rcur = kring->nr_hwtail;
bkring->nm_sync(bkring, flags);
kring->rhead = kring->rcur = kring->rtail = kring->nr_hwtail;
error = kring->nm_sync(kring, 0);
if (kring->rcur != kring->nr_hwtail) {
ret = NM_IRQ_RESCHED;
}
put_out:
nm_kr_put(kring);
return error ? error : ret;
}
int
netmap_bwrap_reg(struct netmap_adapter *na, int onoff)
{
struct netmap_bwrap_adapter *bna =
(struct netmap_bwrap_adapter *)na;
struct netmap_adapter *hwna = bna->hwna;
struct netmap_vp_adapter *hostna = &bna->host;
int error, i;
enum txrx t;
nm_prdis("%s %s", na->name, onoff ? "on" : "off");
if (onoff) {
hwna->na_lut = na->na_lut;
if (hostna->na_bdg) {
hostna->up.na_lut = na->na_lut;
}
}
for_rx_tx(t) {
for (i = 0; i < netmap_all_rings(na, t); i++) {
NMR(hwna, nm_txrx_swap(t))[i]->nr_pending_mode =
NMR(na, t)[i]->nr_pending_mode;
}
}
error = hwna->nm_register(hwna, onoff);
if (error)
return error;
for_rx_tx(t) {
for (i = 0; i < netmap_all_rings(na, t); i++) {
struct netmap_kring *kring = NMR(hwna, nm_txrx_swap(t))[i];
NMR(na, t)[i]->nr_mode = kring->nr_mode;
}
}
netmap_vp_reg(na, onoff);
if (hostna->na_bdg)
netmap_vp_reg(&hostna->up, onoff);
if (onoff) {
u_int i;
for (i = 0; i < hwna->num_rx_rings; i++) {
hwna->rx_rings[i]->save_notify = hwna->rx_rings[i]->nm_notify;
hwna->rx_rings[i]->nm_notify = bna->nm_intr_notify;
}
i = hwna->num_rx_rings;
for (; i < netmap_real_rings(hwna, NR_RX); i++) {
hwna->rx_rings[i]->save_notify =
hwna->rx_rings[i]->nm_notify;
if (hostna->na_bdg) {
hwna->rx_rings[i]->nm_notify =
netmap_bwrap_intr_notify;
na->tx_rings[i]->nm_sync = na->nm_txsync;
}
}
if (na->active_fds == 0)
na->na_flags |= NAF_NETMAP_ON;
} else {
u_int i;
if (na->active_fds == 0)
na->na_flags &= ~NAF_NETMAP_ON;
for (i = 0; i < netmap_all_rings(hwna, NR_RX); i++) {
hwna->rx_rings[i]->nm_notify =
hwna->rx_rings[i]->save_notify;
hwna->rx_rings[i]->save_notify = NULL;
}
hwna->na_lut.lut = NULL;
hwna->na_lut.plut = NULL;
hwna->na_lut.objtotal = 0;
hwna->na_lut.objsize = 0;
for_rx_tx(t) {
nma_set_host_nrings(hwna, t, 1);
}
}
return 0;
}
static int
netmap_bwrap_config(struct netmap_adapter *na, struct nm_config_info *info)
{
struct netmap_bwrap_adapter *bna =
(struct netmap_bwrap_adapter *)na;
struct netmap_adapter *hwna = bna->hwna;
int error;
error = netmap_mem_get_lut(hwna->nm_mem, &bna->host.up.na_lut);
if (error)
return error;
error = netmap_mem_get_lut(hwna->nm_mem, &hwna->na_lut);
if (error)
return error;
netmap_update_config(hwna);
info->num_tx_rings = hwna->num_rx_rings;
info->num_tx_descs = hwna->num_rx_desc;
info->num_rx_rings = hwna->num_tx_rings;
info->num_rx_descs = hwna->num_tx_desc;
info->rx_buf_maxsize = hwna->rx_buf_maxsize;
if (na->na_flags & NAF_HOST_RINGS) {
struct netmap_adapter *hostna = &bna->host.up;
enum txrx t;
if (na->na_flags & NAF_HOST_ALL) {
hostna->num_tx_rings = nma_get_nrings(hwna, NR_RX);
hostna->num_rx_rings = nma_get_nrings(hwna, NR_TX);
} else {
nm_bound_var(&hostna->num_tx_rings, 1, 1,
nma_get_nrings(hwna, NR_TX), NULL);
nm_bound_var(&hostna->num_rx_rings, 1, 1,
nma_get_nrings(hwna, NR_RX), NULL);
}
for_rx_tx(t) {
enum txrx r = nm_txrx_swap(t);
u_int nr = nma_get_nrings(hostna, t);
nma_set_host_nrings(na, t, nr);
if (nma_get_host_nrings(hwna, t) < nr) {
nma_set_host_nrings(hwna, t, nr);
}
nma_set_ndesc(hostna, t, nma_get_ndesc(hwna, r));
}
}
return 0;
}
static int
netmap_bwrap_bufcfg(struct netmap_kring *kring, uint64_t target)
{
struct netmap_adapter *na = kring->na;
struct netmap_bwrap_adapter *bna =
(struct netmap_bwrap_adapter *)na;
struct netmap_adapter *hwna = bna->hwna;
struct netmap_kring *hwkring;
enum txrx r;
int error;
r = nm_txrx_swap(kring->tx);
hwkring = NMR(hwna, r)[kring->ring_id];
hwkring->offset_mask = kring->offset_mask;
hwkring->offset_max = kring->offset_max;
hwkring->offset_gap = kring->offset_gap;
error = hwkring->nm_bufcfg(hwkring, target);
if (error)
return error;
kring->hwbuf_len = hwkring->hwbuf_len;
kring->buf_align = hwkring->buf_align;
return 0;
}
int
netmap_bwrap_krings_create_common(struct netmap_adapter *na)
{
struct netmap_bwrap_adapter *bna =
(struct netmap_bwrap_adapter *)na;
struct netmap_adapter *hwna = bna->hwna;
struct netmap_adapter *hostna = &bna->host.up;
int i, error = 0;
enum txrx t;
error = hwna->nm_krings_create(hwna);
if (error) {
return error;
}
for_rx_tx(t) {
for (i = 0; i < netmap_all_rings(hwna, t); i++) {
NMR(hwna, t)[i]->users++;
NMR(na, t)[i]->nr_kflags |= NKR_NEEDRING;
}
}
error = netmap_mem_rings_create(hwna);
if (error) {
goto err_dec_users;
}
for_rx_tx(t) {
enum txrx r = nm_txrx_swap(t);
for (i = 0; i < netmap_all_rings(hwna, r); i++) {
NMR(na, t)[i]->nkr_num_slots = NMR(hwna, r)[i]->nkr_num_slots;
NMR(na, t)[i]->ring = NMR(hwna, r)[i]->ring;
}
}
if (na->na_flags & NAF_HOST_RINGS) {
hostna->tx_rings = &na->tx_rings[na->num_tx_rings];
hostna->rx_rings = &na->rx_rings[na->num_rx_rings];
for_rx_tx(t) {
for (i = 0; i < nma_get_nrings(hostna, t); i++) {
NMR(hostna, t)[i]->na = hostna;
}
}
}
return 0;
err_dec_users:
for_rx_tx(t) {
for (i = 0; i < netmap_all_rings(hwna, t); i++) {
NMR(hwna, t)[i]->users--;
NMR(na, t)[i]->users--;
}
}
hwna->nm_krings_delete(hwna);
return error;
}
void
netmap_bwrap_krings_delete_common(struct netmap_adapter *na)
{
struct netmap_bwrap_adapter *bna =
(struct netmap_bwrap_adapter *)na;
struct netmap_adapter *hwna = bna->hwna;
enum txrx t;
int i;
nm_prdis("%s", na->name);
for_rx_tx(t) {
for (i = 0; i < netmap_all_rings(hwna, t); i++) {
NMR(hwna, t)[i]->users--;
NMR(na, t)[i]->users--;
}
}
netmap_mem_rings_delete(hwna);
hwna->nm_krings_delete(hwna);
}
int
netmap_bwrap_notify(struct netmap_kring *kring, int flags)
{
struct netmap_adapter *na = kring->na;
struct netmap_bwrap_adapter *bna = na->na_private;
struct netmap_adapter *hwna = bna->hwna;
u_int ring_n = kring->ring_id;
u_int lim = kring->nkr_num_slots - 1;
struct netmap_kring *hw_kring;
int error;
nm_prdis("%s: na %s hwna %s",
(kring ? kring->name : "NULL!"),
(na ? na->name : "NULL!"),
(hwna ? hwna->name : "NULL!"));
hw_kring = hwna->tx_rings[ring_n];
if (nm_kr_tryget(hw_kring, 0, NULL)) {
return ENXIO;
}
netmap_vp_rxsync(kring, flags);
nm_prdis("%s[%d] PRE rx(c%3d t%3d l%3d) ring(h%3d c%3d t%3d) tx(c%3d ht%3d t%3d)",
na->name, ring_n,
kring->nr_hwcur, kring->nr_hwtail, kring->nkr_hwlease,
kring->rhead, kring->rcur, kring->rtail,
hw_kring->nr_hwcur, hw_kring->nr_hwtail, hw_kring->rtail);
hw_kring->rhead = hw_kring->rcur = kring->nr_hwtail;
error = hw_kring->nm_sync(hw_kring, flags);
if (error)
goto put_out;
kring->rhead = kring->rcur = nm_next(hw_kring->nr_hwtail, lim);
netmap_vp_rxsync(kring, flags);
nm_prdis("%s[%d] PST rx(c%3d t%3d l%3d) ring(h%3d c%3d t%3d) tx(c%3d ht%3d t%3d)",
na->name, ring_n,
kring->nr_hwcur, kring->nr_hwtail, kring->nkr_hwlease,
kring->rhead, kring->rcur, kring->rtail,
hw_kring->nr_hwcur, hw_kring->nr_hwtail, hw_kring->rtail);
put_out:
nm_kr_put(hw_kring);
return error ? error : NM_IRQ_COMPLETED;
}
static int
netmap_bwrap_bdg_ctl(struct nmreq_header *hdr, struct netmap_adapter *na)
{
struct netmap_priv_d *npriv;
struct netmap_bwrap_adapter *bna = (struct netmap_bwrap_adapter*)na;
int error = 0;
if (hdr->nr_reqtype == NETMAP_REQ_VALE_ATTACH) {
struct nmreq_vale_attach *req =
(struct nmreq_vale_attach *)(uintptr_t)hdr->nr_body;
if (req->reg.nr_ringid != 0 ||
(req->reg.nr_mode != NR_REG_ALL_NIC &&
req->reg.nr_mode != NR_REG_NIC_SW)) {
return EINVAL;
}
if (NETMAP_OWNED_BY_ANY(na)) {
return EBUSY;
}
if (bna->na_kpriv) {
return 0;
}
npriv = netmap_priv_new();
if (npriv == NULL)
return ENOMEM;
npriv->np_ifp = na->ifp;
error = netmap_do_regif(npriv, na, hdr);
if (error) {
netmap_priv_delete(npriv);
netmap_mem_restore(bna->hwna);
return error;
}
bna->na_kpriv = npriv;
na->na_flags |= NAF_BUSY;
} else {
if (na->active_fds == 0)
return EINVAL;
netmap_priv_delete(bna->na_kpriv);
bna->na_kpriv = NULL;
na->na_flags &= ~NAF_BUSY;
netmap_mem_restore(bna->hwna);
}
return error;
}
int
netmap_bwrap_attach_common(struct netmap_adapter *na,
struct netmap_adapter *hwna)
{
struct netmap_bwrap_adapter *bna;
struct netmap_adapter *hostna = NULL;
int error = 0;
enum txrx t;
if (NETMAP_OWNED_BY_ANY(hwna)) {
nm_prerr("NIC %s busy, cannot attach to bridge", hwna->name);
return EBUSY;
}
bna = (struct netmap_bwrap_adapter *)na;
na->ifp = hwna->ifp;
if_ref(na->ifp);
na->na_private = bna;
for_rx_tx(t) {
enum txrx r = nm_txrx_swap(t);
nma_set_nrings(na, t, nma_get_nrings(hwna, r));
nma_set_ndesc(na, t, nma_get_ndesc(hwna, r));
}
na->nm_dtor = netmap_bwrap_dtor;
na->nm_config = netmap_bwrap_config;
na->nm_bufcfg = netmap_bwrap_bufcfg;
na->nm_bdg_ctl = netmap_bwrap_bdg_ctl;
na->pdev = hwna->pdev;
na->nm_mem = netmap_mem_get(hwna->nm_mem);
na->virt_hdr_len = hwna->virt_hdr_len;
na->rx_buf_maxsize = hwna->rx_buf_maxsize;
bna->hwna = hwna;
netmap_adapter_get(hwna);
hwna->na_private = bna;
bna->saved_na_vp = hwna->na_vp;
hwna->na_vp = &bna->up;
bna->up.up.na_vp = &(bna->up);
if (hwna->na_flags & NAF_HOST_RINGS) {
if (hwna->na_flags & NAF_SW_ONLY)
na->na_flags |= NAF_SW_ONLY;
na->na_flags |= NAF_HOST_RINGS;
hostna = &bna->host.up;
snprintf(hostna->name, sizeof(hostna->name), "%s^", na->name);
hostna->ifp = hwna->ifp;
hostna->nm_mem = netmap_mem_get(na->nm_mem);
hostna->na_private = bna;
hostna->na_vp = &bna->up;
na->na_hostvp = hwna->na_hostvp =
hostna->na_hostvp = &bna->host;
hostna->na_flags = NAF_BUSY;
hostna->rx_buf_maxsize = hwna->rx_buf_maxsize;
}
if (hwna->na_flags & NAF_MOREFRAG)
na->na_flags |= NAF_MOREFRAG;
nm_prdis("%s<->%s txr %d txd %d rxr %d rxd %d",
na->name, if_name(ifp),
na->num_tx_rings, na->num_tx_desc,
na->num_rx_rings, na->num_rx_desc);
error = netmap_attach_common(na);
if (error) {
goto err_put;
}
hwna->na_flags |= NAF_BUSY;
return 0;
err_put:
hwna->na_vp = hwna->na_hostvp = NULL;
netmap_adapter_put(hwna);
return error;
}
struct nm_bridge *
netmap_init_bridges2(u_int n)
{
int i;
struct nm_bridge *b;
b = nm_os_malloc(sizeof(struct nm_bridge) * n);
if (b == NULL)
return NULL;
for (i = 0; i < n; i++)
BDG_RWINIT(&b[i]);
return b;
}
void
netmap_uninit_bridges2(struct nm_bridge *b, u_int n)
{
int i;
if (b == NULL)
return;
for (i = 0; i < n; i++)
BDG_RWDESTROY(&b[i]);
nm_os_free(b);
}
int
netmap_init_bridges(void)
{
#ifdef CONFIG_NET_NS
return netmap_bns_register();
#else
nm_bridges = netmap_init_bridges2(vale_max_bridges);
if (nm_bridges == NULL)
return ENOMEM;
return 0;
#endif
}
void
netmap_uninit_bridges(void)
{
#ifdef CONFIG_NET_NS
netmap_bns_unregister();
#else
netmap_uninit_bridges2(nm_bridges, vale_max_bridges);
#endif
}