diff options
| author | Vincenzo Maffione <vmaffione@FreeBSD.org> | 2018-10-23 08:55:16 +0000 |
|---|---|---|
| committer | Vincenzo Maffione <vmaffione@FreeBSD.org> | 2018-10-23 08:55:16 +0000 |
| commit | 2a7db7a63de8af153b9a626780dd15ca26ae3596 (patch) | |
| tree | 37c944c0992d8c89dea1efe83fa7d85ae3aabd0f /sys/dev/netmap/netmap_vale.c | |
| parent | 60b905ae2f3a31efd6809c790b5a02f574489716 (diff) | |
Notes
Diffstat (limited to 'sys/dev/netmap/netmap_vale.c')
| -rw-r--r-- | sys/dev/netmap/netmap_vale.c | 2172 |
1 files changed, 215 insertions, 1957 deletions
diff --git a/sys/dev/netmap/netmap_vale.c b/sys/dev/netmap/netmap_vale.c index 94ffc2e8df7dd..2c526753272c7 100644 --- a/sys/dev/netmap/netmap_vale.c +++ b/sys/dev/netmap/netmap_vale.c @@ -27,37 +27,6 @@ */ -/* - * This module implements the VALE switch for netmap - ---- VALE SWITCH --- - -NMG_LOCK() serializes all modifications to switches and ports. -A switch cannot be deleted until all ports are gone. - -For each switch, an SX lock (RWlock on linux) protects -deletion of ports. When configuring or deleting a new port, the -lock is acquired in exclusive mode (after holding NMG_LOCK). -When forwarding, the lock is acquired in shared mode (without NMG_LOCK). -The lock is held throughout the entire forwarding cycle, -during which the thread may incur in a page fault. -Hence it is important that sleepable shared locks are used. - -On the rx ring, the per-port lock is grabbed initially to reserve -a number of slot in the ring, then the lock is released, -packets are copied from source to destination, and then -the lock is acquired again and the receive ring is updated. -(A similar thing is done on the tx ring for NIC and host stack -ports attached to the switch) - - */ - -/* - * OS-specific code that is used only within this file. - * Other OS-specific code that must be accessed by drivers - * is present in netmap_kern.h - */ - #if defined(__FreeBSD__) #include <sys/cdefs.h> /* prerequisite */ __FBSDID("$FreeBSD$"); @@ -81,18 +50,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> /* bus_dmamap_* */ #include <sys/endian.h> #include <sys/refcount.h> - - -#define BDG_RWLOCK_T struct rwlock // struct rwlock - -#define BDG_RWINIT(b) \ - rw_init_flags(&(b)->bdg_lock, "bdg lock", RW_NOWITNESS) -#define BDG_WLOCK(b) rw_wlock(&(b)->bdg_lock) -#define BDG_WUNLOCK(b) rw_wunlock(&(b)->bdg_lock) -#define BDG_RLOCK(b) rw_rlock(&(b)->bdg_lock) -#define BDG_RTRYLOCK(b) rw_try_rlock(&(b)->bdg_lock) -#define BDG_RUNLOCK(b) rw_runlock(&(b)->bdg_lock) -#define BDG_RWDESTROY(b) rw_destroy(&(b)->bdg_lock) +#include <sys/smp.h> #elif defined(linux) @@ -120,6 +78,7 @@ __FBSDID("$FreeBSD$"); #include <net/netmap.h> #include <dev/netmap/netmap_kern.h> #include <dev/netmap/netmap_mem2.h> +#include <dev/netmap/netmap_bdg.h> #ifdef WITH_VALE @@ -143,15 +102,11 @@ __FBSDID("$FreeBSD$"); #define NM_BDG_MAXRINGS 16 /* XXX unclear how many. */ #define NM_BDG_MAXSLOTS 4096 /* XXX same as above */ #define NM_BRIDGE_RINGSIZE 1024 /* in the device */ -#define NM_BDG_HASH 1024 /* forwarding table entries */ #define NM_BDG_BATCH 1024 /* entries in the forwarding buffer */ -#define NM_MULTISEG 64 /* max size of a chain of bufs */ /* actual size of the tables */ -#define NM_BDG_BATCH_MAX (NM_BDG_BATCH + NM_MULTISEG) +#define NM_BDG_BATCH_MAX (NM_BDG_BATCH + NETMAP_MAX_FRAGS) /* NM_FT_NULL terminates a list of slots in the ft */ #define NM_FT_NULL NM_BDG_BATCH_MAX -/* Default size for the Maximum Frame Size. */ -#define NM_BDG_MFS_DEFAULT 1514 /* @@ -168,8 +123,9 @@ SYSEND; static int netmap_vp_create(struct nmreq_header *hdr, struct ifnet *, struct netmap_mem_d *nmd, struct netmap_vp_adapter **); -static int netmap_vp_reg(struct netmap_adapter *na, int onoff); -static int netmap_bwrap_reg(struct netmap_adapter *, int onoff); +static int netmap_vp_bdg_attach(const char *, struct netmap_adapter *, + struct nm_bridge *); +static int netmap_vale_bwrap_attach(const char *, struct netmap_adapter *); /* * For each output interface, nm_bdg_q is used to construct a list. @@ -182,98 +138,16 @@ struct nm_bdg_q { uint32_t bq_len; /* number of buffers */ }; -/* XXX revise this */ -struct nm_hash_ent { - uint64_t mac; /* the top 2 bytes are the epoch */ - uint64_t ports; -}; - /* Holds the default callbacks */ -static struct netmap_bdg_ops default_bdg_ops = {netmap_bdg_learning, NULL, NULL}; - -/* - * nm_bridge is a descriptor for a VALE switch. - * Interfaces for a bridge are all in bdg_ports[]. - * The array has fixed size, an empty entry does not terminate - * the search, but lookups only occur on attach/detach so we - * don't mind if they are slow. - * - * The bridge is non blocking on the transmit ports: excess - * packets are dropped if there is no room on the output port. - * - * bdg_lock protects accesses to the bdg_ports array. - * This is a rw lock (or equivalent). - */ -#define NM_BDG_IFNAMSIZ IFNAMSIZ -struct nm_bridge { - /* XXX what is the proper alignment/layout ? */ - BDG_RWLOCK_T bdg_lock; /* protects bdg_ports */ - int bdg_namelen; - uint32_t bdg_active_ports; - char bdg_basename[NM_BDG_IFNAMSIZ]; - - /* Indexes of active ports (up to active_ports) - * and all other remaining ports. - */ - uint32_t bdg_port_index[NM_BDG_MAXPORTS]; - /* used by netmap_bdg_detach_common() */ - uint32_t tmp_bdg_port_index[NM_BDG_MAXPORTS]; - - struct netmap_vp_adapter *bdg_ports[NM_BDG_MAXPORTS]; - - /* - * Programmable lookup functions to figure out the destination port. - * It returns either of an index of the destination port, - * NM_BDG_BROADCAST to broadcast this packet, or NM_BDG_NOPORT not to - * forward this packet. ring_nr is the source ring index, and the - * function may overwrite this value to forward this packet to a - * different ring index. - * The function is set by netmap_bdg_regops(). - */ - struct netmap_bdg_ops *bdg_ops; - - /* - * Contains the data structure used by the bdg_ops.lookup function. - * By default points to *ht which is allocated on attach and used by the default lookup - * otherwise will point to the data structure received by netmap_bdg_regops(). - */ - void *private_data; - struct nm_hash_ent *ht; - - /* Currently used to specify if the bridge is still in use while empty and - * if it has been put in exclusive mode by an external module, see netmap_bdg_regops() - * and netmap_bdg_create(). - */ -#define NM_BDG_ACTIVE 1 -#define NM_BDG_EXCLUSIVE 2 - uint8_t bdg_flags; - - -#ifdef CONFIG_NET_NS - struct net *ns; -#endif /* CONFIG_NET_NS */ +struct netmap_bdg_ops vale_bdg_ops = { + .lookup = netmap_bdg_learning, + .config = NULL, + .dtor = NULL, + .vp_create = netmap_vp_create, + .bwrap_attach = netmap_vale_bwrap_attach, + .name = NM_BDG_NAME, }; -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 -/* - * XXX in principle nm_bridges could be created dynamically - * Right now we have a static array and deletions are protected - * by an exclusive lock. - */ -static struct nm_bridge *nm_bridges; -#endif /* !CONFIG_NET_NS */ - - /* * this is a slightly optimized copy routine which rounds * to multiple of 64 bytes and is often faster than dealing @@ -304,107 +178,6 @@ pkt_copy(void *_src, void *_dst, int l) } -static int -nm_is_id_char(const char c) -{ - return (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - (c == '_'); -} - -/* Validate the name of a VALE bridge port and return the - * position of the ":" character. */ -static int -nm_vale_name_validate(const char *name) -{ - int colon_pos = -1; - int i; - - if (!name || strlen(name) < strlen(NM_BDG_NAME)) { - 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) { - /* interface name too long */ - return -1; - } - - return colon_pos; -} - -/* - * locate a bridge among the existing ones. - * MUST BE CALLED WITH NMG_LOCK() - * - * a ':' in the name terminates the bridge name. Otherwise, just NM_NAME. - * We assume that this is called with a name of at least NM_NAME chars. - */ -static struct nm_bridge * -nm_find_bridge(const char *name, int create) -{ - int i, namelen; - struct nm_bridge *b = NULL, *bridges; - u_int num_bridges; - - NMG_LOCK_ASSERT(); - - netmap_bns_getbridges(&bridges, &num_bridges); - - namelen = nm_vale_name_validate(name); - if (namelen < 0) { - D("invalid bridge name %s", name ? name : NULL); - return NULL; - } - - /* lookup the name, remember empty slot if there is one */ - 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; /* record empty slot */ - } else if (x->bdg_namelen != namelen) { - continue; - } else if (strncmp(name, x->bdg_basename, namelen) == 0) { - ND("found '%.*s' at %d", namelen, name, i); - b = x; - break; - } - } - if (i == num_bridges && b) { /* name not found, can create entry */ - /* initialize the bridge */ - ND("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) { - D("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; - /* set the default function */ - b->bdg_ops = &default_bdg_ops; - b->private_data = b->ht; - b->bdg_flags = 0; - NM_BNS_GET(b); - } - return b; -} - - /* * Free the forwarding tables for rings attached to switch ports. */ @@ -464,99 +237,6 @@ nm_alloc_bdgfwd(struct netmap_adapter *na) return 0; } -static int -netmap_bdg_free(struct nm_bridge *b) -{ - if ((b->bdg_flags & NM_BDG_ACTIVE) + b->bdg_active_ports != 0) { - return EBUSY; - } - - ND("marking bridge %s as free", b->bdg_basename); - nm_os_free(b->ht); - b->bdg_ops = NULL; - b->bdg_flags = 0; - NM_BNS_PUT(b); - return 0; -} - - -/* remove from bridge b the ports in slots hw and sw - * (sw can be -1 if not needed) - */ -static 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; - - /* - New algorithm: - make a copy of bdg_port_index; - lookup NA(ifp)->bdg_port and SWNA(ifp)->bdg_port - in the array of bdg_port_index, replacing them with - entries from the bottom of the array; - decrement bdg_active_ports; - acquire BDG_WLOCK() and copy back the array. - */ - - if (netmap_verbose) - D("detach %d and %d (lim %d)", hw, sw, lim); - /* make a copy of the list of active ports, update it, - * and then copy back within BDG_WLOCK(). - */ - 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) { - ND("detach hw %d at %d", hw, i); - lim--; /* point to last active port */ - tmp[i] = tmp[lim]; /* swap with i */ - tmp[lim] = hw; /* now this is inactive */ - hw = -1; - } else if (sw >= 0 && tmp[i] == sw) { - ND("detach sw %d at %d", sw, i); - lim--; - tmp[i] = tmp[lim]; - tmp[lim] = sw; - sw = -1; - } else { - i++; - } - } - if (hw >= 0 || sw >= 0) { - D("XXX 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); - - ND("now %d active ports", lim); - netmap_bdg_free(b); -} - -static inline void * -nm_bdg_get_auth_token(struct nm_bridge *b) -{ - return b->ht; -} - -/* bridge not in exclusive mode ==> always valid - * bridge in exclusive mode (created through netmap_bdg_create()) ==> check authentication token - */ -static inline int -nm_bdg_valid_auth_token(struct nm_bridge *b, void *auth_token) -{ - return !(b->bdg_flags & NM_BDG_EXCLUSIVE) || b->ht == auth_token; -} - /* Allows external modules to create bridges in exclusive mode, * returns an authentication token that the external module will need * to provide during nm_bdg_ctl_{attach, detach}(), netmap_bdg_regops(), @@ -564,19 +244,19 @@ nm_bdg_valid_auth_token(struct nm_bridge *b, void *auth_token) * Successfully executed if ret != NULL and *return_status == 0. */ void * -netmap_bdg_create(const char *bdg_name, int *return_status) +netmap_vale_create(const char *bdg_name, int *return_status) { struct nm_bridge *b = NULL; void *ret = NULL; NMG_LOCK(); - b = nm_find_bridge(bdg_name, 0 /* don't create */); + b = nm_find_bridge(bdg_name, 0 /* don't create */, NULL); if (b) { *return_status = EEXIST; goto unlock_bdg_create; } - b = nm_find_bridge(bdg_name, 1 /* create */); + b = nm_find_bridge(bdg_name, 1 /* create */, &vale_bdg_ops); if (!b) { *return_status = ENOMEM; goto unlock_bdg_create; @@ -595,13 +275,13 @@ unlock_bdg_create: * netmap_bdg_create(), the bridge must be empty. */ int -netmap_bdg_destroy(const char *bdg_name, void *auth_token) +netmap_vale_destroy(const char *bdg_name, void *auth_token) { struct nm_bridge *b = NULL; int ret = 0; NMG_LOCK(); - b = nm_find_bridge(bdg_name, 0 /* don't create */); + b = nm_find_bridge(bdg_name, 0 /* don't create */, NULL); if (!b) { ret = ENXIO; goto unlock_bdg_free; @@ -629,27 +309,6 @@ unlock_bdg_free: -/* nm_bdg_ctl callback for VALE ports */ -static 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; /* nothing to do */ - } - if (b) { - netmap_set_all_rings(na, 0 /* disable */); - netmap_bdg_detach_common(b, vpna->bdg_port, -1); - vpna->na_bdg = NULL; - netmap_set_all_rings(na, 1 /* enable */); - } - /* I have took reference just for attach */ - netmap_adapter_put(na); - return 0; -} - /* nm_dtor callback for ephemeral VALE ports */ static void netmap_vp_dtor(struct netmap_adapter *na) @@ -664,7 +323,7 @@ netmap_vp_dtor(struct netmap_adapter *na) } if (na->ifp != NULL && !nm_iszombie(na)) { - WNA(na->ifp) = NULL; + NM_DETACH_NA(na->ifp); if (vpna->autodelete) { ND("releasing %s", na->ifp->if_xname); NMG_UNLOCK(); @@ -674,895 +333,6 @@ netmap_vp_dtor(struct netmap_adapter *na) } } -/* creates a persistent VALE port */ -int -nm_vi_create(struct nmreq_header *hdr) -{ - struct nmreq_vale_newif *req = - (struct nmreq_vale_newif *)(uintptr_t)hdr->nr_body; - int error = 0; - /* Build a nmreq_register out of the nmreq_vale_newif, - * so that we can call netmap_get_bdg_na(). */ - struct nmreq_register regreq; - bzero(®req, 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; - hdr->nr_reqtype = NETMAP_REQ_REGISTER; - hdr->nr_body = (uintptr_t)®req; - error = netmap_vi_create(hdr, 0 /* no autodelete */); - hdr->nr_reqtype = NETMAP_REQ_VALE_NEWIF; - hdr->nr_body = (uintptr_t)req; - /* Write back to the original struct. */ - req->nr_tx_slots = regreq.nr_tx_slots; - req->nr_rx_slots = regreq.nr_rx_slots; - req->nr_tx_rings = regreq.nr_tx_rings; - req->nr_rx_rings = regreq.nr_rx_rings; - req->nr_mem_id = regreq.nr_mem_id; - return error; -} - -/* remove a persistent VALE port from the system */ -int -nm_vi_destroy(const char *name) -{ - struct ifnet *ifp; - struct netmap_vp_adapter *vpna; - int error; - - ifp = ifunit_ref(name); - if (!ifp) - return ENXIO; - NMG_LOCK(); - /* make sure this is actually a VALE port */ - if (!NM_NA_VALID(ifp) || NA(ifp)->nm_register != netmap_vp_reg) { - error = EINVAL; - goto err; - } - - vpna = (struct netmap_vp_adapter *)NA(ifp); - - /* we can only destroy ports that were created via NETMAP_BDG_NEWIF */ - if (vpna->autodelete) { - error = EINVAL; - goto err; - } - - /* also make sure that nobody is using the inferface */ - if (NETMAP_OWNED_BY_ANY(&vpna->up) || - vpna->up.na_refcount > 1 /* any ref besides the one in nm_vi_create()? */) { - error = EBUSY; - goto err; - } - - NMG_UNLOCK(); - - D("destroying a persistent vale interface %s", ifp->if_xname); - /* Linux requires all the references are released - * before unregister - */ - netmap_detach(ifp); - if_rele(ifp); - nm_os_vi_detach(ifp); - return 0; - -err: - NMG_UNLOCK(); - if_rele(ifp); - return error; -} - -static int -nm_update_info(struct nmreq_register *req, struct netmap_adapter *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; - return netmap_mem_get_info(na->nm_mem, &req->nr_memsize, NULL, - &req->nr_mem_id); -} - -/* - * Create a virtual interface registered to the system. - * The interface will be attached to a bridge later. - */ -int -netmap_vi_create(struct nmreq_header *hdr, int autodelete) -{ - struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body; - struct ifnet *ifp; - struct netmap_vp_adapter *vpna; - struct netmap_mem_d *nmd = NULL; - int error; - - if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) { - return EINVAL; - } - - /* don't include VALE prefix */ - if (!strncmp(hdr->nr_name, NM_BDG_NAME, strlen(NM_BDG_NAME))) - return EINVAL; - if (strlen(hdr->nr_name) >= IFNAMSIZ) { - return EINVAL; - } - ifp = ifunit_ref(hdr->nr_name); - if (ifp) { /* already exist, cannot create new one */ - error = EEXIST; - NMG_LOCK(); - if (NM_NA_VALID(ifp)) { - int update_err = nm_update_info(req, NA(ifp)); - if (update_err) - error = update_err; - } - NMG_UNLOCK(); - if_rele(ifp); - return error; - } - error = nm_os_vi_persist(hdr->nr_name, &ifp); - if (error) - return error; - - NMG_LOCK(); - if (req->nr_mem_id) { - nmd = netmap_mem_find(req->nr_mem_id); - if (nmd == NULL) { - error = EINVAL; - goto err_1; - } - } - /* netmap_vp_create creates a struct netmap_vp_adapter */ - error = netmap_vp_create(hdr, ifp, nmd, &vpna); - if (error) { - D("error %d", error); - goto err_1; - } - /* persist-specific routines */ - vpna->up.nm_bdg_ctl = netmap_vp_bdg_ctl; - if (!autodelete) { - netmap_adapter_get(&vpna->up); - } else { - vpna->autodelete = 1; - } - NM_ATTACH_NA(ifp, &vpna->up); - /* return the updated info */ - error = nm_update_info(req, &vpna->up); - if (error) { - goto err_2; - } - ND("returning nr_mem_id %d", req->nr_mem_id); - if (nmd) - netmap_mem_put(nmd); - NMG_UNLOCK(); - ND("created %s", ifp->if_xname); - return 0; - -err_2: - netmap_detach(ifp); -err_1: - if (nmd) - netmap_mem_put(nmd); - NMG_UNLOCK(); - nm_os_vi_detach(ifp); - - return error; -} - -/* Try to get a reference to a netmap adapter attached to a VALE switch. - * If the adapter is found (or is created), this function returns 0, a - * non NULL pointer is returned into *na, and the caller holds a - * reference to the adapter. - * If an adapter is not found, then no reference is grabbed and the - * function returns an error code, or 0 if there is just a VALE prefix - * mismatch. Therefore the caller holds a reference when - * (*na != NULL && return == 0). - */ -int -netmap_get_bdg_na(struct nmreq_header *hdr, struct netmap_adapter **na, - struct netmap_mem_d *nmd, int create) -{ - char *nr_name = hdr->nr_name; - const char *ifname; - struct ifnet *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; /* default return value */ - - /* first try to see if this is a bridge port. */ - NMG_LOCK_ASSERT(); - if (strncmp(nr_name, NM_BDG_NAME, sizeof(NM_BDG_NAME) - 1)) { - return 0; /* no error, but no VALE prefix */ - } - - b = nm_find_bridge(nr_name, create); - if (b == NULL) { - ND("no bridges available for '%s'", nr_name); - return (create ? ENOMEM : ENXIO); - } - if (strlen(nr_name) < b->bdg_namelen) /* impossible */ - panic("x"); - - /* Now we are sure that name starts with the bridge's name, - * lookup the port in the bridge. We need to scan the entire - * list. It is not important to hold a WLOCK on the bridge - * during the search because NMG_LOCK already guarantees - * that there are no other possible writers. - */ - - /* lookup in the local list of ports */ - for (j = 0; j < b->bdg_active_ports; j++) { - i = b->bdg_port_index[j]; - vpna = b->bdg_ports[i]; - ND("checking %s", vpna->up.name); - if (!strcmp(vpna->up.name, nr_name)) { - netmap_adapter_get(&vpna->up); - ND("found existing if %s refs %d", nr_name) - *na = &vpna->up; - return 0; - } - } - /* not found, should we create it? */ - if (!create) - return ENXIO; - /* yes we should, see if we have space to attach entries */ - needed = 2; /* in some cases we only need 1 */ - if (b->bdg_active_ports + needed >= NM_BDG_MAXPORTS) { - D("bridge full %d, cannot create new port", b->bdg_active_ports); - return ENOMEM; - } - /* record the next two ports available, but do not allocate yet */ - cand = b->bdg_port_index[b->bdg_active_ports]; - cand2 = b->bdg_port_index[b->bdg_active_ports + 1]; - ND("+++ bridge %s port %s used %d avail %d %d", - b->bdg_basename, ifname, b->bdg_active_ports, cand, cand2); - - /* - * try see if there is a matching NIC with this name - * (after the bridge's name) - */ - ifname = nr_name + b->bdg_namelen + 1; - ifp = ifunit_ref(ifname); - if (!ifp) { - /* Create an ephemeral virtual port. - * This block contains all the ephemeral-specific logic. - */ - - if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) { - error = EINVAL; - goto out; - } - - /* bdg_netmap_attach creates a struct netmap_adapter */ - error = netmap_vp_create(hdr, NULL, nmd, &vpna); - if (error) { - D("error %d", error); - goto out; - } - /* shortcut - we can skip get_hw_na(), - * ownership check and nm_bdg_attach() - */ - - } else { - struct netmap_adapter *hw; - - /* the vale:nic syntax is only valid for some commands */ - 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; /* ok */ - default: - error = EINVAL; - goto out; - } - - error = netmap_get_hw_na(ifp, nmd, &hw); - if (error || hw == NULL) - goto out; - - /* host adapter might not be created */ - error = hw->nm_bdg_attach(nr_name, hw); - if (error) - goto out; - vpna = hw->na_vp; - hostna = hw->na_hostvp; - if (hdr->nr_reqtype == NETMAP_REQ_VALE_ATTACH) { - /* Check if we need to skip the host rings. */ - 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; - ND("NIC %p to bridge port %d", vpna, cand); - /* bind the port to the bridge (virtual ports are not active) */ - b->bdg_ports[cand] = vpna; - vpna->na_bdg = b; - b->bdg_active_ports++; - if (hostna != NULL) { - /* also bind the host stack to the bridge */ - b->bdg_ports[cand2] = hostna; - hostna->bdg_port = cand2; - hostna->na_bdg = b; - b->bdg_active_ports++; - ND("host %p to bridge port %d", hostna, cand2); - } - ND("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; -} - -/* Process NETMAP_REQ_VALE_ATTACH. - */ -int -nm_bdg_ctl_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; - struct netmap_mem_d *nmd = NULL; - struct nm_bridge *b = NULL; - int error; - - NMG_LOCK(); - /* permission check for modified bridges */ - b = nm_find_bridge(hdr->nr_name, 0 /* don't create */); - 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; - } - } - - /* check for existing one */ - error = netmap_get_bdg_na(hdr, &na, nmd, 0); - if (!error) { - error = EBUSY; - goto unref_exit; - } - error = netmap_get_bdg_na(hdr, &na, - nmd, 1 /* create if not exists */); - if (error) { /* no device */ - goto unlock_exit; - } - - if (na == NULL) { /* VALE prefix missing */ - error = EINVAL; - goto unlock_exit; - } - - if (NETMAP_OWNED_BY_ANY(na)) { - error = EBUSY; - goto unref_exit; - } - - if (na->nm_bdg_ctl) { - /* nop for VALE ports. The bwrap needs to put the hwna - * in netmap mode (see netmap_bwrap_bdg_ctl) - */ - error = na->nm_bdg_ctl(hdr, na); - if (error) - goto unref_exit; - ND("registered %s to netmap-mode", na->name); - } - vpna = (struct netmap_vp_adapter *)na; - req->port_index = vpna->bdg_port; - NMG_UNLOCK(); - return 0; - -unref_exit: - netmap_adapter_put(na); -unlock_exit: - NMG_UNLOCK(); - return error; -} - -static inline int -nm_is_bwrap(struct netmap_adapter *na) -{ - return na->nm_register == netmap_bwrap_reg; -} - -/* Process NETMAP_REQ_VALE_DETACH. - */ -int -nm_bdg_ctl_detach(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; - - NMG_LOCK(); - /* permission check for modified bridges */ - b = nm_find_bridge(hdr->nr_name, 0 /* don't create */); - if (b && !nm_bdg_valid_auth_token(b, auth_token)) { - error = EACCES; - goto unlock_exit; - } - - error = netmap_get_bdg_na(hdr, &na, NULL, 0 /* don't create */); - if (error) { /* no device, or another bridge or user owns the device */ - goto unlock_exit; - } - - if (na == NULL) { /* VALE prefix missing */ - error = EINVAL; - goto unlock_exit; - } else if (nm_is_bwrap(na) && - ((struct netmap_bwrap_adapter *)na)->na_polling_state) { - /* Don't detach a NIC with polling */ - error = EBUSY; - goto unref_exit; - } - - vpna = (struct netmap_vp_adapter *)na; - if (na->na_vp != vpna) { - /* trying to detach first attach of VALE persistent port attached - * to 2 bridges - */ - error = EBUSY; - goto unref_exit; - } - nmreq_det->port_index = vpna->bdg_port; - - if (na->nm_bdg_ctl) { - /* remove the port from bridge. The bwrap - * also needs to put the hwna in normal mode - */ - error = na->nm_bdg_ctl(hdr, na); - } - -unref_exit: - netmap_adapter_put(na); -unlock_exit: - NMG_UNLOCK(); - 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, int is_kthread) -{ - 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; - kcfg.use_kthread = 1; - 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 /* must be 0 */: affinity; - t->qlast = all ? bps->qlast : t->qfirst + 1; - D("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; -} - -/* A variant of ptnetmap_start_kthreads() */ -static int -nm_bdg_polling_start_kthreads(struct nm_bdg_polling_state *bps) -{ - int error, i, j; - - if (!bps) { - D("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) { - D("error in nm_kthread_start()"); - 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) { - D("req_cpus must be > 0"); - return EINVAL; - } else if (req_cpus >= avail_cpus) { - D("Cannot use all the CPUs in the system"); - return EINVAL; - } - - if (req->nr_mode == NETMAP_POLLING_MODE_MULTI_CPU) { - /* Use a separate core for each ring. If nr_num_polling_cpus>1 - * more consecutive rings are polled. - * For example, if nr_first_cpu_id=2 and nr_num_polling_cpus=2, - * ring 2 and 3 are polled by core 2 and 3, respectively. */ - if (i + req_cpus > nma_get_nrings(na, NR_RX)) { - D("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) { - /* Poll all the rings using a core specified by nr_first_cpu_id. - * the number of cores must be 1. */ - if (req_cpus != 1) { - D("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 { - D("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; - D("%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) { - D("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; - - /* disable interrupts if possible */ - nma_intr_enable(bna->hwna, 0); - /* start kthread now */ - error = nm_bdg_polling_start_kthreads(bps); - if (error) { - D("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) { - D("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; - /* reenable interrupts */ - 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_bdg_na(hdr, &na, NULL, /*create=*/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) { - /* Not VALE port. */ - error = EINVAL; - } - NMG_UNLOCK(); - - return error; -} - -/* Process NETMAP_REQ_VALE_LIST. */ -int -netmap_bdg_list(struct nmreq_header *hdr) -{ - struct nmreq_vale_list *req = - (struct nmreq_vale_list *)(uintptr_t)hdr->nr_body; - int namelen = strlen(hdr->nr_name); - struct nm_bridge *b, *bridges; - struct netmap_vp_adapter *vpna; - int error = 0, i, j; - u_int num_bridges; - - netmap_bns_getbridges(&bridges, &num_bridges); - - /* this is used to enumerate bridges and ports */ - if (namelen) { /* look up indexes of bridge and port */ - if (strncmp(hdr->nr_name, NM_BDG_NAME, - strlen(NM_BDG_NAME))) { - return EINVAL; - } - NMG_LOCK(); - b = nm_find_bridge(hdr->nr_name, 0 /* don't create */); - if (!b) { - NMG_UNLOCK(); - return ENOENT; - } - - req->nr_bridge_idx = b - bridges; /* bridge index */ - req->nr_port_idx = NM_BDG_NOPORT; - for (j = 0; j < b->bdg_active_ports; j++) { - i = b->bdg_port_index[j]; - vpna = b->bdg_ports[i]; - if (vpna == NULL) { - D("This should not happen"); - continue; - } - /* the former and the latter identify a - * virtual port and a NIC, respectively - */ - if (!strcmp(vpna->up.name, hdr->nr_name)) { - req->nr_port_idx = i; /* port index */ - break; - } - } - NMG_UNLOCK(); - } else { - /* return the first non-empty entry starting from - * bridge nr_arg1 and port nr_arg2. - * - * Users can detect the end of the same bridge by - * seeing the new and old value of nr_arg1, and can - * detect the end of all the bridge by error != 0 - */ - i = req->nr_bridge_idx; - j = req->nr_port_idx; - - NMG_LOCK(); - for (error = ENOENT; i < NM_BRIDGES; i++) { - b = bridges + i; - for ( ; j < NM_BDG_MAXPORTS; j++) { - if (b->bdg_ports[j] == NULL) - continue; - vpna = b->bdg_ports[j]; - /* write back the VALE switch name */ - strncpy(hdr->nr_name, vpna->up.name, - (size_t)IFNAMSIZ); - error = 0; - goto out; - } - j = 0; /* following bridges scan from 0 */ - } - out: - req->nr_bridge_idx = i; - req->nr_port_idx = j; - NMG_UNLOCK(); - } - - return error; -} - -/* Called by external kernel modules (e.g., Openvswitch). - * to set configure/lookup/dtor functions of a VALE instance. - * Register callbacks to the given bridge. 'name' may be just - * bridge's name (including ':' if it is not just NM_BDG_NAME). - * - * Called without NMG_LOCK. - */ - -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 /* don't create */); - 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) { - /* resetting the bridge */ - bzero(b->ht, sizeof(struct nm_hash_ent) * NM_BDG_HASH); - b->bdg_ops = &default_bdg_ops; - b->private_data = b->ht; - } else { - /* modifying the bridge */ - b->private_data = private_data; - b->bdg_ops = bdg_ops; - } - BDG_WUNLOCK(b); - -unlock_regops: - NMG_UNLOCK(); - return error; -} /* Called by external kernel modules (e.g., Openvswitch). * to modify the private data previously given to regops(). @@ -1579,7 +349,7 @@ nm_bdg_update_private_data(const char *name, bdg_update_private_data_fn_t callba int error = 0; NMG_LOCK(); - b = nm_find_bridge(name, 0 /* don't create */); + b = nm_find_bridge(name, 0 /* don't create */, NULL); if (!b) { error = EINVAL; goto unlock_update_priv; @@ -1598,27 +368,6 @@ unlock_update_priv: 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); - if (!b) { - NMG_UNLOCK(); - return error; - } - NMG_UNLOCK(); - /* Don't call config() with NMG_LOCK() held */ - BDG_RLOCK(b); - if (b->bdg_ops->config != NULL) - error = b->bdg_ops->config(nr); - BDG_RUNLOCK(b); - return error; -} - /* nm_krings_create callback for VALE ports. * Calls the standard netmap_krings_create, then adds leases on rx @@ -1798,52 +547,6 @@ nm_bridge_rthash(const uint8_t *addr) #undef mix -/* nm_register callback for VALE ports */ -static int -netmap_vp_reg(struct netmap_adapter *na, int onoff) -{ - struct netmap_vp_adapter *vpna = - (struct netmap_vp_adapter*)na; - enum txrx t; - int i; - - /* persistent ports may be put in netmap mode - * before being attached to a bridge - */ - if (vpna->na_bdg) - BDG_WLOCK(vpna->na_bdg); - if (onoff) { - for_rx_tx(t) { - for (i = 0; i < netmap_real_rings(na, t); i++) { - struct netmap_kring *kring = NMR(na, t)[i]; - - if (nm_kring_pending_on(kring)) - kring->nr_mode = NKR_NETMAP_ON; - } - } - if (na->active_fds == 0) - na->na_flags |= NAF_NETMAP_ON; - /* XXX on FreeBSD, persistent VALE ports should also - * toggle IFCAP_NETMAP in na->ifp (2014-03-16) - */ - } else { - if (na->active_fds == 0) - na->na_flags &= ~NAF_NETMAP_ON; - for_rx_tx(t) { - for (i = 0; i < netmap_real_rings(na, t); i++) { - struct netmap_kring *kring = NMR(na, t)[i]; - - if (nm_kring_pending_off(kring)) - kring->nr_mode = NKR_NETMAP_OFF; - } - } - } - if (vpna->na_bdg) - BDG_WUNLOCK(vpna->na_bdg); - return 0; -} - - /* * Lookup function for a learning bridge. * Update the hash table with the source address, @@ -2361,86 +1064,6 @@ done: } -/* rxsync code used by VALE ports nm_rxsync callback and also - * internally by the brwap - */ -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) { - D("ouch dangerous reset!!!"); - n = netmap_ring_reinit(kring); - goto done; - } - - /* First part, import newly received packets. */ - /* actually nothing to do here, they are already in the kring */ - - /* Second part, skip past packets that userspace has released. */ - nm_i = kring->nr_hwcur; - if (nm_i != head) { - /* consistency check, but nothing really important here */ - 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)) { /* bad buf */ - D("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; -} - -/* - * nm_rxsync callback for VALE ports - * user process reading from a VALE switch. - * Already protected against concurrent calls from userspace, - * but we must acquire the queue's lock to protect against - * writers on the same queue. - */ -static 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; -} - - -/* nm_bdg_attach callback for VALE ports - * The na_vp port is this same netmap_adapter. There is no host port. - */ -static int -netmap_vp_bdg_attach(const char *name, struct netmap_adapter *na) -{ - struct netmap_vp_adapter *vpna = (struct netmap_vp_adapter *)na; - - if (vpna->na_bdg) { - return netmap_bwrap_attach(name, na); - } - na->na_vp = vpna; - strncpy(na->name, name, sizeof(na->name)); - na->na_hostvp = NULL; - return 0; -} - /* create a netmap_vp_adapter that describes a VALE port. * Only persistent VALE ports have a non-null ifp. */ @@ -2536,635 +1159,270 @@ err: return error; } -/* Bridge wrapper code (bwrap). - * This is used to connect a non-VALE-port netmap_adapter (hwna) to a - * VALE switch. - * The main task is to swap the meaning of tx and rx rings to match the - * expectations of the VALE switch code (see nm_bdg_flush). - * - * The bwrap works by interposing a netmap_bwrap_adapter between the - * rest of the system and the hwna. The netmap_bwrap_adapter looks like - * a netmap_vp_adapter to the rest the system, but, internally, it - * translates all callbacks to what the hwna expects. - * - * Note that we have to intercept callbacks coming from two sides: - * - * - callbacks coming from the netmap module are intercepted by - * passing around the netmap_bwrap_adapter instead of the hwna - * - * - callbacks coming from outside of the netmap module only know - * about the hwna. This, however, only happens in interrupt - * handlers, where only the hwna->nm_notify callback is called. - * What the bwrap does is to overwrite the hwna->nm_notify callback - * with its own netmap_bwrap_intr_notify. - * XXX This assumes that the hwna->nm_notify callback was the - * standard netmap_notify(), as it is the case for nic adapters. - * Any additional action performed by hwna->nm_notify will not be - * performed by netmap_bwrap_intr_notify. - * - * Additionally, the bwrap can optionally attach the host rings pair - * of the wrapped adapter to a different port of the switch. - */ - - -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)); - } - - ND("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); - -} - - -/* - * Intr callback for NICs connected to a bridge. - * Simply ignore tx interrupts (maybe we could try to recover space ?) - * and pass received packets from nic to the bridge. - * - * XXX TODO check locking: this is called from the interrupt - * handler so we should make sure that the interface is not - * disconnected while passing down an interrupt. - * - * Note, no user process can access this NIC or the host stack. - * The only part of the ring that is significant are the slots, - * and head/cur/tail are set from the kring as needed - * (part as a receive ring, part as a transmit ring). - * - * callback that overwrites the hwna notify callback. - * Packets come from the outside or from the host stack and are put on an - * hwna rx ring. - * The bridge wrapper then sends the packets through the bridge. +/* nm_bdg_attach callback for VALE ports + * The na_vp port is this same netmap_adapter. There is no host port. */ static 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_verbose) - D("%s %s 0x%x", na->name, kring->name, flags); - - bkring = vpna->up.tx_rings[ring_nr]; - - /* make sure the ring is not disabled */ - if (nm_kr_tryget(kring, 0 /* can't sleep */, NULL)) { - return EIO; - } - - if (netmap_verbose) - D("%s head %d cur %d tail %d", na->name, - kring->rhead, kring->rcur, kring->rtail); - - /* simulate a user wakeup on the rx ring - * fetch packets that have arrived. - */ - error = kring->nm_sync(kring, 0); - if (error) - goto put_out; - if (kring->nr_hwcur == kring->nr_hwtail) { - if (netmap_verbose) - D("how strange, interrupt with no packets on %s", - na->name); - goto put_out; - } - - /* new packets are kring->rcur to kring->nr_hwtail, and the bkring - * had hwcur == bkring->rhead. So advance bkring->rhead to kring->nr_hwtail - * to push all packets out. - */ - bkring->rhead = bkring->rcur = kring->nr_hwtail; - - netmap_vp_txsync(bkring, flags); - - /* mark all buffers as released on this ring */ - kring->rhead = kring->rcur = kring->rtail = kring->nr_hwtail; - /* another call to actually release the buffers */ - error = kring->nm_sync(kring, 0); - - /* The second rxsync may have further advanced hwtail. If this happens, - * return NM_IRQ_RESCHED, otherwise just return NM_IRQ_COMPLETED. */ - if (kring->rcur != kring->nr_hwtail) { - ret = NM_IRQ_RESCHED; - } -put_out: - nm_kr_put(kring); - - return error ? error : ret; -} - - -/* nm_register callback for bwrap */ -static int -netmap_bwrap_reg(struct netmap_adapter *na, int onoff) +netmap_vp_bdg_attach(const char *name, struct netmap_adapter *na, + struct nm_bridge *b) { - 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; - - ND("%s %s", na->name, onoff ? "on" : "off"); - - if (onoff) { - /* netmap_do_regif has been called on the bwrap na. - * We need to pass the information about the - * memory allocator down to the hwna before - * putting it in netmap mode - */ - hwna->na_lut = na->na_lut; - - if (hostna->na_bdg) { - /* if the host rings have been attached to switch, - * we need to copy the memory allocator information - * in the hostna also - */ - hostna->up.na_lut = na->na_lut; - } - - } - - /* pass down the pending ring state information */ - for_rx_tx(t) { - for (i = 0; i < nma_get_nrings(na, t) + 1; i++) - NMR(hwna, t)[i]->nr_pending_mode = - NMR(na, t)[i]->nr_pending_mode; - } - - /* forward the request to the hwna */ - error = hwna->nm_register(hwna, onoff); - if (error) - return error; + struct netmap_vp_adapter *vpna = (struct netmap_vp_adapter *)na; - /* copy up the current ring state information */ - for_rx_tx(t) { - for (i = 0; i < nma_get_nrings(na, t) + 1; i++) { - struct netmap_kring *kring = NMR(hwna, t)[i]; - NMR(na, t)[i]->nr_mode = kring->nr_mode; - } + if (b->bdg_ops != &vale_bdg_ops) { + return NM_NEED_BWRAP; } - - /* impersonate a netmap_vp_adapter */ - netmap_vp_reg(na, onoff); - if (hostna->na_bdg) - netmap_vp_reg(&hostna->up, onoff); - - if (onoff) { - u_int i; - /* intercept the hwna nm_nofify callback on the hw rings */ - 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 = netmap_bwrap_intr_notify; - } - i = hwna->num_rx_rings; /* for safety */ - /* save the host ring notify unconditionally */ - hwna->rx_rings[i]->save_notify = hwna->rx_rings[i]->nm_notify; - if (hostna->na_bdg) { - /* also intercept the host ring notify */ - hwna->rx_rings[i]->nm_notify = netmap_bwrap_intr_notify; - } - 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; - - /* reset all notify callbacks (including host ring) */ - for (i = 0; i <= hwna->num_rx_rings; 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; - - /* pass ownership of the netmap rings to the hwna */ - for_rx_tx(t) { - for (i = 0; i < nma_get_nrings(na, t) + 1; i++) { - NMR(na, t)[i]->ring = NULL; - } - } - + if (vpna->na_bdg) { + return NM_NEED_BWRAP; } - + na->na_vp = vpna; + strncpy(na->name, name, sizeof(na->name)); + na->na_hostvp = NULL; return 0; } -/* nm_config callback for bwrap */ static int -netmap_bwrap_config(struct netmap_adapter *na, struct nm_config_info *info) +netmap_vale_bwrap_krings_create(struct netmap_adapter *na) { - struct netmap_bwrap_adapter *bna = - (struct netmap_bwrap_adapter *)na; - struct netmap_adapter *hwna = bna->hwna; int error; - /* Forward the request to the hwna. It may happen that nobody - * registered hwna yet, so netmap_mem_get_lut() may have not - * been called yet. */ - error = netmap_mem_get_lut(hwna->nm_mem, &hwna->na_lut); - if (error) - return error; - netmap_update_config(hwna); - /* swap the results and propagate */ - 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; - - return 0; -} - - -/* nm_krings_create callback for bwrap */ -static int -netmap_bwrap_krings_create(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; - - ND("%s", na->name); - /* impersonate a netmap_vp_adapter */ error = netmap_vp_krings_create(na); if (error) return error; - - /* also create the hwna krings */ - error = hwna->nm_krings_create(hwna); - if (error) { - goto err_del_vp_rings; - } - - /* increment the usage counter for all the hwna krings */ - for_rx_tx(t) { - for (i = 0; i < nma_get_nrings(hwna, t) + 1; i++) { - NMR(hwna, t)[i]->users++; - } - } - - /* now create the actual rings */ - error = netmap_mem_rings_create(hwna); + error = netmap_bwrap_krings_create_common(na); if (error) { - goto err_dec_users; - } - - /* cross-link the netmap rings - * The original number of rings comes from hwna, - * rx rings on one side equals tx rings on the other. - */ - for_rx_tx(t) { - enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */ - for (i = 0; i < nma_get_nrings(hwna, r) + 1; 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) { - /* the hostna rings are the host rings of the bwrap. - * The corresponding krings must point back to the - * hostna - */ - hostna->tx_rings = &na->tx_rings[na->num_tx_rings]; - hostna->tx_rings[0]->na = hostna; - hostna->rx_rings = &na->rx_rings[na->num_rx_rings]; - hostna->rx_rings[0]->na = hostna; - } - - return 0; - -err_dec_users: - for_rx_tx(t) { - NMR(hwna, t)[i]->users--; + netmap_vp_krings_delete(na); } - hwna->nm_krings_delete(hwna); -err_del_vp_rings: - netmap_vp_krings_delete(na); - return error; } - static void -netmap_bwrap_krings_delete(struct netmap_adapter *na) +netmap_vale_bwrap_krings_delete(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; - - ND("%s", na->name); - - /* decrement the usage counter for all the hwna krings */ - for_rx_tx(t) { - for (i = 0; i < nma_get_nrings(hwna, t) + 1; i++) { - NMR(hwna, t)[i]->users--; - } - } - - /* delete any netmap rings that are no longer needed */ - netmap_mem_rings_delete(hwna); - hwna->nm_krings_delete(hwna); + netmap_bwrap_krings_delete_common(na); netmap_vp_krings_delete(na); } - -/* notify method for the bridge-->hwna direction */ static 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; - - ND("%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; - } - - /* first step: simulate a user wakeup on the rx ring */ - netmap_vp_rxsync(kring, flags); - ND("%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, - ring->head, ring->cur, ring->tail, - hw_kring->nr_hwcur, hw_kring->nr_hwtail, hw_ring->rtail); - /* second step: the new packets are sent on the tx ring - * (which is actually the same ring) - */ - hw_kring->rhead = hw_kring->rcur = kring->nr_hwtail; - error = hw_kring->nm_sync(hw_kring, flags); - if (error) - goto put_out; - - /* third step: now we are back the rx ring */ - /* claim ownership on all hw owned bufs */ - kring->rhead = kring->rcur = nm_next(hw_kring->nr_hwtail, lim); /* skip past reserved slot */ - - /* fourth step: the user goes to sleep again, causing another rxsync */ - netmap_vp_rxsync(kring, flags); - ND("%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, - ring->head, ring->cur, ring->tail, - hw_kring->nr_hwcur, hw_kring->nr_hwtail, hw_kring->rtail); -put_out: - nm_kr_put(hw_kring); - - return error ? error : NM_IRQ_COMPLETED; -} - - -/* nm_bdg_ctl callback for the bwrap. - * Called on bridge-attach and detach, as an effect of vale-ctl -[ahd]. - * On attach, it needs to provide a fake netmap_priv_d structure and - * perform a netmap_do_regif() on the bwrap. This will put both the - * bwrap and the hwna in netmap mode, with the netmap rings shared - * and cross linked. Moroever, it will start intercepting interrupts - * directed to hwna. - */ -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)) { - /* We only support attaching all the NIC rings - * and/or the host stack. */ - return EINVAL; - } - if (NETMAP_OWNED_BY_ANY(na)) { - return EBUSY; - } - if (bna->na_kpriv) { - /* nothing to do */ - return 0; - } - npriv = netmap_priv_new(); - if (npriv == NULL) - return ENOMEM; - npriv->np_ifp = na->ifp; /* let the priv destructor release the ref */ - error = netmap_do_regif(npriv, na, req->reg.nr_mode, - req->reg.nr_ringid, req->reg.nr_flags); - if (error) { - netmap_priv_delete(npriv); - return error; - } - bna->na_kpriv = npriv; - na->na_flags |= NAF_BUSY; - } else { - if (na->active_fds == 0) /* not registered */ - return EINVAL; - netmap_priv_delete(bna->na_kpriv); - bna->na_kpriv = NULL; - na->na_flags &= ~NAF_BUSY; - } - - return error; -} - -/* attach a bridge wrapper to the 'real' device */ -int -netmap_bwrap_attach(const char *nr_name, struct netmap_adapter *hwna) +netmap_vale_bwrap_attach(const char *nr_name, struct netmap_adapter *hwna) { struct netmap_bwrap_adapter *bna; struct netmap_adapter *na = NULL; struct netmap_adapter *hostna = NULL; - int error = 0; - enum txrx t; - - /* make sure the NIC is not already in use */ - if (NETMAP_OWNED_BY_ANY(hwna)) { - D("NIC %s busy, cannot attach to bridge", hwna->name); - return EBUSY; - } + int error; bna = nm_os_malloc(sizeof(*bna)); if (bna == NULL) { return ENOMEM; } - na = &bna->up.up; - /* make bwrap ifp point to the real ifp */ - na->ifp = hwna->ifp; - if_ref(na->ifp); - na->na_private = bna; strncpy(na->name, nr_name, sizeof(na->name)); - /* fill the ring data for the bwrap adapter with rx/tx meanings - * swapped. The real cross-linking will be done during register, - * when all the krings will have been created. - */ - for_rx_tx(t) { - enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */ - 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_register = netmap_bwrap_reg; - // na->nm_txsync = netmap_bwrap_txsync; + na->nm_txsync = netmap_vp_txsync; // na->nm_rxsync = netmap_bwrap_rxsync; - na->nm_config = netmap_bwrap_config; - na->nm_krings_create = netmap_bwrap_krings_create; - na->nm_krings_delete = netmap_bwrap_krings_delete; + na->nm_krings_create = netmap_vale_bwrap_krings_create; + na->nm_krings_delete = netmap_vale_bwrap_krings_delete; na->nm_notify = netmap_bwrap_notify; - 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->up.retry = 1; /* XXX maybe this should depend on the hwna */ /* Set the mfs, needed on the VALE mismatch datapath. */ bna->up.mfs = NM_BDG_MFS_DEFAULT; - bna->hwna = hwna; - netmap_adapter_get(hwna); - hwna->na_private = bna; /* weak reference */ - 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^", nr_name); - hostna->ifp = hwna->ifp; - for_rx_tx(t) { - enum txrx r = nm_txrx_swap(t); - nma_set_nrings(hostna, t, 1); - nma_set_ndesc(hostna, t, nma_get_ndesc(hwna, r)); - } - // hostna->nm_txsync = netmap_bwrap_host_txsync; - // hostna->nm_rxsync = netmap_bwrap_host_rxsync; hostna->nm_notify = netmap_bwrap_notify; - 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; /* prevent NIOCREGIF */ - hostna->rx_buf_maxsize = hwna->rx_buf_maxsize; bna->host.mfs = NM_BDG_MFS_DEFAULT; } - ND("%s<->%s txr %d txd %d rxr %d rxd %d", - na->name, ifp->if_xname, - na->num_tx_rings, na->num_tx_desc, - na->num_rx_rings, na->num_rx_desc); - - error = netmap_attach_common(na); + error = netmap_bwrap_attach_common(na, hwna); if (error) { - goto err_free; + nm_os_free(bna); } - hwna->na_flags |= NAF_BUSY; - return 0; - -err_free: - hwna->na_vp = hwna->na_hostvp = NULL; - netmap_adapter_put(hwna); - nm_os_free(bna); return error; - } -struct nm_bridge * -netmap_init_bridges2(u_int n) +int +netmap_get_vale_na(struct nmreq_header *hdr, struct netmap_adapter **na, + struct netmap_mem_d *nmd, int create) { - int i; - struct nm_bridge *b; + return netmap_get_bdg_na(hdr, na, nmd, create, &vale_bdg_ops); +} + - 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; +/* creates a persistent VALE port */ +int +nm_vi_create(struct nmreq_header *hdr) +{ + struct nmreq_vale_newif *req = + (struct nmreq_vale_newif *)(uintptr_t)hdr->nr_body; + int error = 0; + /* Build a nmreq_register out of the nmreq_vale_newif, + * so that we can call netmap_get_bdg_na(). */ + struct nmreq_register regreq; + bzero(®req, 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; + hdr->nr_reqtype = NETMAP_REQ_REGISTER; + hdr->nr_body = (uintptr_t)®req; + error = netmap_vi_create(hdr, 0 /* no autodelete */); + hdr->nr_reqtype = NETMAP_REQ_VALE_NEWIF; + hdr->nr_body = (uintptr_t)req; + /* Write back to the original struct. */ + req->nr_tx_slots = regreq.nr_tx_slots; + req->nr_rx_slots = regreq.nr_rx_slots; + req->nr_tx_rings = regreq.nr_tx_rings; + req->nr_rx_rings = regreq.nr_rx_rings; + req->nr_mem_id = regreq.nr_mem_id; + return error; } -void -netmap_uninit_bridges2(struct nm_bridge *b, u_int n) +/* remove a persistent VALE port from the system */ +int +nm_vi_destroy(const char *name) { - int i; + struct ifnet *ifp; + struct netmap_vp_adapter *vpna; + int error; - if (b == NULL) - return; + ifp = ifunit_ref(name); + if (!ifp) + return ENXIO; + NMG_LOCK(); + /* make sure this is actually a VALE port */ + if (!NM_NA_VALID(ifp) || NA(ifp)->nm_register != netmap_vp_reg) { + error = EINVAL; + goto err; + } + + vpna = (struct netmap_vp_adapter *)NA(ifp); + + /* we can only destroy ports that were created via NETMAP_BDG_NEWIF */ + if (vpna->autodelete) { + error = EINVAL; + goto err; + } + + /* also make sure that nobody is using the inferface */ + if (NETMAP_OWNED_BY_ANY(&vpna->up) || + vpna->up.na_refcount > 1 /* any ref besides the one in nm_vi_create()? */) { + error = EBUSY; + goto err; + } + + NMG_UNLOCK(); + + D("destroying a persistent vale interface %s", ifp->if_xname); + /* Linux requires all the references are released + * before unregister + */ + netmap_detach(ifp); + if_rele(ifp); + nm_os_vi_detach(ifp); + return 0; - for (i = 0; i < n; i++) - BDG_RWDESTROY(&b[i]); - nm_os_free(b); +err: + NMG_UNLOCK(); + if_rele(ifp); + return error; } -int -netmap_init_bridges(void) +static int +nm_update_info(struct nmreq_register *req, struct netmap_adapter *na) { -#ifdef CONFIG_NET_NS - return netmap_bns_register(); -#else - nm_bridges = netmap_init_bridges2(NM_BRIDGES); - if (nm_bridges == NULL) - return ENOMEM; - return 0; -#endif + 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; + return netmap_mem_get_info(na->nm_mem, &req->nr_memsize, NULL, + &req->nr_mem_id); } -void -netmap_uninit_bridges(void) + +/* + * Create a virtual interface registered to the system. + * The interface will be attached to a bridge later. + */ +int +netmap_vi_create(struct nmreq_header *hdr, int autodelete) { -#ifdef CONFIG_NET_NS - netmap_bns_unregister(); -#else - netmap_uninit_bridges2(nm_bridges, NM_BRIDGES); -#endif + struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body; + struct ifnet *ifp; + struct netmap_vp_adapter *vpna; + struct netmap_mem_d *nmd = NULL; + int error; + + if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) { + return EINVAL; + } + + /* don't include VALE prefix */ + if (!strncmp(hdr->nr_name, NM_BDG_NAME, strlen(NM_BDG_NAME))) + return EINVAL; + if (strlen(hdr->nr_name) >= IFNAMSIZ) { + return EINVAL; + } + ifp = ifunit_ref(hdr->nr_name); + if (ifp) { /* already exist, cannot create new one */ + error = EEXIST; + NMG_LOCK(); + if (NM_NA_VALID(ifp)) { + int update_err = nm_update_info(req, NA(ifp)); + if (update_err) + error = update_err; + } + NMG_UNLOCK(); + if_rele(ifp); + return error; + } + error = nm_os_vi_persist(hdr->nr_name, &ifp); + if (error) + return error; + + NMG_LOCK(); + if (req->nr_mem_id) { + nmd = netmap_mem_find(req->nr_mem_id); + if (nmd == NULL) { + error = EINVAL; + goto err_1; + } + } + /* netmap_vp_create creates a struct netmap_vp_adapter */ + error = netmap_vp_create(hdr, ifp, nmd, &vpna); + if (error) { + D("error %d", error); + goto err_1; + } + /* persist-specific routines */ + vpna->up.nm_bdg_ctl = netmap_vp_bdg_ctl; + if (!autodelete) { + netmap_adapter_get(&vpna->up); + } else { + vpna->autodelete = 1; + } + NM_ATTACH_NA(ifp, &vpna->up); + /* return the updated info */ + error = nm_update_info(req, &vpna->up); + if (error) { + goto err_2; + } + ND("returning nr_mem_id %d", req->nr_mem_id); + if (nmd) + netmap_mem_put(nmd); + NMG_UNLOCK(); + ND("created %s", ifp->if_xname); + return 0; + +err_2: + netmap_detach(ifp); +err_1: + if (nmd) + netmap_mem_put(nmd); + NMG_UNLOCK(); + nm_os_vi_detach(ifp); + + return error; } + #endif /* WITH_VALE */ |
