Changeset View
Changeset View
Standalone View
Standalone View
sys/net/if_epair.c
Show First 20 Lines • Show All 379 Lines • ▼ Show 20 Lines | #ifdef ALTQ | ||||
} | } | ||||
IF_UNLOCK(&ifp->if_snd); | IF_UNLOCK(&ifp->if_snd); | ||||
#endif | #endif | ||||
error = epair_menq(m, oifp->if_softc); | error = epair_menq(m, oifp->if_softc); | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | |||||
epair_qflush(struct ifnet *ifp __unused) | |||||
{ | |||||
} | |||||
static int | static int | ||||
epair_media_change(struct ifnet *ifp __unused) | epair_media_change(struct ifnet *ifp __unused) | ||||
{ | { | ||||
/* Do nothing. */ | /* Do nothing. */ | ||||
return (0); | return (0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | epair_clone_add(struct if_clone *ifc, struct epair_softc *scb) | ||||
/* Copy epairNa etheraddr and change the last byte. */ | /* Copy epairNa etheraddr and change the last byte. */ | ||||
memcpy(eaddr, scb->oifp->if_hw_addr, ETHER_ADDR_LEN); | memcpy(eaddr, scb->oifp->if_hw_addr, ETHER_ADDR_LEN); | ||||
eaddr[5] = 0x0b; | eaddr[5] = 0x0b; | ||||
ether_ifattach(ifp, eaddr); | ether_ifattach(ifp, eaddr); | ||||
if_clone_addif(ifc, ifp); | if_clone_addif(ifc, ifp); | ||||
} | } | ||||
static struct epair_softc * | |||||
alloc_sc(struct if_clone *ifc) | |||||
zlei: I think `epair_alloc_sc()` will prevent potential naming pollution.
Although it is modified… | |||||
{ | |||||
struct epair_softc *sc; | |||||
struct ifnet *ifp = if_alloc(IFT_ETHER); | |||||
if (ifp == NULL) | |||||
return (NULL); | |||||
sc = malloc(sizeof(struct epair_softc), M_EPAIR, M_WAITOK | M_ZERO); | |||||
sc->ifp = ifp; | |||||
sc->num_queues = epair_tasks.tasks; | |||||
sc->queues = mallocarray(sc->num_queues, sizeof(struct epair_queue), | |||||
M_EPAIR, M_WAITOK); | |||||
for (int i = 0; i < sc->num_queues; i++) { | |||||
struct epair_queue *q = &sc->queues[i]; | |||||
q->id = i; | |||||
q->rxring[0] = buf_ring_alloc(RXRSIZE, M_EPAIR, M_WAITOK, NULL); | |||||
q->rxring[1] = buf_ring_alloc(RXRSIZE, M_EPAIR, M_WAITOK, NULL); | |||||
q->ridx = 0; | |||||
q->state = 0; | |||||
q->sc = sc; | |||||
NET_TASK_INIT(&q->tx_task, 0, epair_tx_start_deferred, q); | |||||
} | |||||
/* Initialise pseudo media types. */ | |||||
ifmedia_init(&sc->media, 0, epair_media_change, epair_media_status); | |||||
ifmedia_add(&sc->media, IFM_ETHER | IFM_10G_T, 0, NULL); | |||||
ifmedia_set(&sc->media, IFM_ETHER | IFM_10G_T); | |||||
return (sc); | |||||
} | |||||
static void | |||||
setup_ifp(struct epair_softc *sc, char *name, int unit) | |||||
{ | |||||
struct ifnet *ifp = sc->ifp; | |||||
ifp->if_softc = sc; | |||||
strlcpy(ifp->if_xname, name, IFNAMSIZ); | |||||
ifp->if_dname = epairname; | |||||
ifp->if_dunit = unit; | |||||
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | |||||
ifp->if_flags |= IFF_KNOWSEPOCH; | |||||
ifp->if_capabilities = IFCAP_VLAN_MTU; | |||||
ifp->if_capenable = IFCAP_VLAN_MTU; | |||||
ifp->if_transmit = epair_transmit; | |||||
ifp->if_qflush = epair_qflush; | |||||
ifp->if_start = epair_start; | |||||
ifp->if_ioctl = epair_ioctl; | |||||
ifp->if_init = epair_init; | |||||
if_setsendqlen(ifp, ifqmaxlen); | |||||
if_setsendqready(ifp); | |||||
ifp->if_baudrate = IF_Gbps(10); /* arbitrary maximum */ | |||||
} | |||||
static void | |||||
generate_mac(struct epair_softc *sc, uint8_t *eaddr) | |||||
{ | |||||
uint32_t key[3]; | |||||
uint32_t hash; | |||||
uint64_t hostid; | |||||
EPAIR_LOCK(); | |||||
#ifdef SMP | |||||
/* Get an approximate distribution. */ | |||||
hash = next_index % mp_ncpus; | |||||
#else | |||||
hash = 0; | |||||
#endif | |||||
EPAIR_UNLOCK(); | |||||
/* | |||||
* Calculate the etheraddr hashing the hostid and the | |||||
* interface index. The result would be hopefully unique. | |||||
* Note that the "a" component of an epair instance may get moved | |||||
* to a different VNET after creation. In that case its index | |||||
* will be freed and the index can get reused by new epair instance. | |||||
* Make sure we do not create same etheraddr again. | |||||
*/ | |||||
getcredhostid(curthread->td_ucred, (unsigned long *)&hostid); | |||||
if (hostid == 0) | |||||
arc4rand(&hostid, sizeof(hostid), 0); | |||||
struct ifnet *ifp = sc->ifp; | |||||
EPAIR_LOCK(); | |||||
if (ifp->if_index > next_index) | |||||
next_index = ifp->if_index; | |||||
else | |||||
next_index++; | |||||
key[0] = (uint32_t)next_index; | |||||
EPAIR_UNLOCK(); | |||||
key[1] = (uint32_t)(hostid & 0xffffffff); | |||||
key[2] = (uint32_t)((hostid >> 32) & 0xfffffffff); | |||||
hash = jenkins_hash32(key, 3, 0); | |||||
eaddr[0] = 0x02; | |||||
memcpy(&eaddr[1], &hash, 4); | |||||
eaddr[5] = 0x0a; | |||||
} | |||||
static void | |||||
free_sc(struct epair_softc *sc) | |||||
{ | |||||
if_free(sc->ifp); | |||||
ifmedia_removeall(&sc->media); | |||||
for (int i = 0; i < sc->num_queues; i++) { | |||||
struct epair_queue *q = &sc->queues[i]; | |||||
buf_ring_free(q->rxring[0], M_EPAIR); | |||||
buf_ring_free(q->rxring[1], M_EPAIR); | |||||
} | |||||
free(sc->queues, M_EPAIR); | |||||
free(sc, M_EPAIR); | |||||
} | |||||
static int | static int | ||||
epair_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) | epair_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) | ||||
{ | { | ||||
struct epair_softc *sca, *scb; | struct epair_softc *sca, *scb; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
char *dp; | char *dp; | ||||
int error, unit, wildcard; | int error, unit, wildcard; | ||||
uint64_t hostid; | |||||
uint32_t key[3]; | |||||
uint32_t hash; | |||||
uint8_t eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ | uint8_t eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ | ||||
/* Try to see if a special unit was requested. */ | /* Try to see if a special unit was requested. */ | ||||
error = ifc_name2unit(name, &unit); | error = ifc_name2unit(name, &unit); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
wildcard = (unit < 0); | wildcard = (unit < 0); | ||||
Show All 27 Lines | epair_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) | ||||
/* Check if 'a' and 'b' interfaces already exist. */ | /* Check if 'a' and 'b' interfaces already exist. */ | ||||
if (ifunit(name) != NULL) | if (ifunit(name) != NULL) | ||||
return (EEXIST); | return (EEXIST); | ||||
*dp = 'a'; | *dp = 'a'; | ||||
if (ifunit(name) != NULL) | if (ifunit(name) != NULL) | ||||
return (EEXIST); | return (EEXIST); | ||||
/* Allocate memory for both [ab] interfaces */ | /* Allocate memory for both [ab] interfaces */ | ||||
sca = malloc(sizeof(struct epair_softc), M_EPAIR, M_WAITOK | M_ZERO); | sca = alloc_sc(ifc); | ||||
sca->ifp = if_alloc(IFT_ETHER); | scb = alloc_sc(ifc); | ||||
sca->num_queues = epair_tasks.tasks; | if (sca == NULL || scb == NULL) { | ||||
if (sca->ifp == NULL) { | free_sc(sca); | ||||
zleiUnsubmitted Done Inline Actionsif (sca == NULL ^ scb == NULL), then free_sc(sca) or free_sc(scb) may trigger null pointer dereference. zlei: if (sca == NULL ^ scb == NULL), then free_sc(sca) or free_sc(scb) may trigger null pointer… | |||||
kpUnsubmitted Not Done Inline ActionsGood catch! A simple if (sc == NULL) return; in free_sc() should work, and matches the regular free() idiom. kp: Good catch!
A simple `if (sc == NULL) return;` in free_sc() should work, and matches the… | |||||
melifaroAuthorUnsubmitted Done Inline ActionsYep, that's a good one! melifaro: Yep, that's a good one!
It was my initial intention but I forgot to actually add it :-) | |||||
free(sca, M_EPAIR); | free_sc(scb); | ||||
ifc_free_unit(ifc, unit); | ifc_free_unit(ifc, unit); | ||||
return (ENOSPC); | return (ENOSPC); | ||||
} | } | ||||
sca->queues = mallocarray(sca->num_queues, sizeof(struct epair_queue), | |||||
M_EPAIR, M_WAITOK); | |||||
for (int i = 0; i < sca->num_queues; i++) { | |||||
struct epair_queue *q = &sca->queues[i]; | |||||
q->id = i; | |||||
q->rxring[0] = buf_ring_alloc(RXRSIZE, M_EPAIR, M_WAITOK, NULL); | |||||
q->rxring[1] = buf_ring_alloc(RXRSIZE, M_EPAIR, M_WAITOK, NULL); | |||||
q->ridx = 0; | |||||
q->state = 0; | |||||
q->sc = sca; | |||||
NET_TASK_INIT(&q->tx_task, 0, epair_tx_start_deferred, q); | |||||
} | |||||
scb = malloc(sizeof(struct epair_softc), M_EPAIR, M_WAITOK | M_ZERO); | |||||
scb->ifp = if_alloc(IFT_ETHER); | |||||
scb->num_queues = epair_tasks.tasks; | |||||
if (scb->ifp == NULL) { | |||||
free(scb, M_EPAIR); | |||||
if_free(sca->ifp); | |||||
free(sca, M_EPAIR); | |||||
ifc_free_unit(ifc, unit); | |||||
return (ENOSPC); | |||||
} | |||||
scb->queues = mallocarray(scb->num_queues, sizeof(struct epair_queue), | |||||
M_EPAIR, M_WAITOK); | |||||
for (int i = 0; i < scb->num_queues; i++) { | |||||
struct epair_queue *q = &scb->queues[i]; | |||||
q->id = i; | |||||
q->rxring[0] = buf_ring_alloc(RXRSIZE, M_EPAIR, M_WAITOK, NULL); | |||||
q->rxring[1] = buf_ring_alloc(RXRSIZE, M_EPAIR, M_WAITOK, NULL); | |||||
q->ridx = 0; | |||||
q->state = 0; | |||||
q->sc = scb; | |||||
NET_TASK_INIT(&q->tx_task, 0, epair_tx_start_deferred, q); | |||||
} | |||||
/* | /* | ||||
* Cross-reference the interfaces so we will be able to free both. | * Cross-reference the interfaces so we will be able to free both. | ||||
*/ | */ | ||||
sca->oifp = scb->ifp; | sca->oifp = scb->ifp; | ||||
scb->oifp = sca->ifp; | scb->oifp = sca->ifp; | ||||
EPAIR_LOCK(); | |||||
#ifdef SMP | |||||
/* Get an approximate distribution. */ | |||||
hash = next_index % mp_ncpus; | |||||
#else | |||||
hash = 0; | |||||
#endif | |||||
EPAIR_UNLOCK(); | |||||
/* Initialise pseudo media types. */ | |||||
ifmedia_init(&sca->media, 0, epair_media_change, epair_media_status); | |||||
ifmedia_add(&sca->media, IFM_ETHER | IFM_10G_T, 0, NULL); | |||||
ifmedia_set(&sca->media, IFM_ETHER | IFM_10G_T); | |||||
ifmedia_init(&scb->media, 0, epair_media_change, epair_media_status); | |||||
ifmedia_add(&scb->media, IFM_ETHER | IFM_10G_T, 0, NULL); | |||||
ifmedia_set(&scb->media, IFM_ETHER | IFM_10G_T); | |||||
/* Finish initialization of interface <n>a. */ | /* Finish initialization of interface <n>a. */ | ||||
ifp = sca->ifp; | ifp = sca->ifp; | ||||
ifp->if_softc = sca; | setup_ifp(sca, name, unit); | ||||
strlcpy(ifp->if_xname, name, IFNAMSIZ); | |||||
ifp->if_dname = epairname; | |||||
ifp->if_dunit = unit; | |||||
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | |||||
ifp->if_flags |= IFF_KNOWSEPOCH; | |||||
ifp->if_capabilities = IFCAP_VLAN_MTU; | |||||
ifp->if_capenable = IFCAP_VLAN_MTU; | |||||
ifp->if_start = epair_start; | |||||
ifp->if_ioctl = epair_ioctl; | |||||
ifp->if_init = epair_init; | |||||
if_setsendqlen(ifp, ifqmaxlen); | |||||
if_setsendqready(ifp); | |||||
/* | generate_mac(sca, eaddr); | ||||
* Calculate the etheraddr hashing the hostid and the | |||||
* interface index. The result would be hopefully unique. | |||||
* Note that the "a" component of an epair instance may get moved | |||||
* to a different VNET after creation. In that case its index | |||||
* will be freed and the index can get reused by new epair instance. | |||||
* Make sure we do not create same etheraddr again. | |||||
*/ | |||||
getcredhostid(curthread->td_ucred, (unsigned long *)&hostid); | |||||
if (hostid == 0) | |||||
arc4rand(&hostid, sizeof(hostid), 0); | |||||
EPAIR_LOCK(); | |||||
if (ifp->if_index > next_index) | |||||
next_index = ifp->if_index; | |||||
else | |||||
next_index++; | |||||
key[0] = (uint32_t)next_index; | |||||
EPAIR_UNLOCK(); | |||||
key[1] = (uint32_t)(hostid & 0xffffffff); | |||||
key[2] = (uint32_t)((hostid >> 32) & 0xfffffffff); | |||||
hash = jenkins_hash32(key, 3, 0); | |||||
eaddr[0] = 0x02; | |||||
memcpy(&eaddr[1], &hash, 4); | |||||
eaddr[5] = 0x0a; | |||||
ether_ifattach(ifp, eaddr); | ether_ifattach(ifp, eaddr); | ||||
ifp->if_baudrate = IF_Gbps(10); /* arbitrary maximum */ | |||||
ifp->if_transmit = epair_transmit; | |||||
/* Swap the name and finish initialization of interface <n>b. */ | /* Swap the name and finish initialization of interface <n>b. */ | ||||
*dp = 'b'; | *dp = 'b'; | ||||
setup_ifp(scb, name, unit); | |||||
ifp = scb->ifp; | ifp = scb->ifp; | ||||
ifp->if_softc = scb; | |||||
strlcpy(ifp->if_xname, name, IFNAMSIZ); | |||||
ifp->if_dname = epairname; | |||||
ifp->if_dunit = unit; | |||||
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | |||||
ifp->if_flags |= IFF_KNOWSEPOCH; | |||||
ifp->if_capabilities = IFCAP_VLAN_MTU; | |||||
ifp->if_capenable = IFCAP_VLAN_MTU; | |||||
ifp->if_start = epair_start; | |||||
ifp->if_ioctl = epair_ioctl; | |||||
ifp->if_init = epair_init; | |||||
if_setsendqlen(ifp, ifqmaxlen); | |||||
if_setsendqready(ifp); | |||||
/* We need to play some tricks here for the second interface. */ | /* We need to play some tricks here for the second interface. */ | ||||
strlcpy(name, epairname, len); | strlcpy(name, epairname, len); | ||||
/* Correctly set the name for the cloner list. */ | /* Correctly set the name for the cloner list. */ | ||||
strlcpy(name, scb->ifp->if_xname, len); | strlcpy(name, scb->ifp->if_xname, len); | ||||
epair_clone_add(ifc, scb); | epair_clone_add(ifc, scb); | ||||
ifp->if_baudrate = IF_Gbps(10); /* arbitrary maximum */ | |||||
ifp->if_transmit = epair_transmit; | |||||
/* | /* | ||||
* Restore name to <n>a as the ifp for this will go into the | * Restore name to <n>a as the ifp for this will go into the | ||||
* cloner list for the initial call. | * cloner list for the initial call. | ||||
*/ | */ | ||||
strlcpy(name, sca->ifp->if_xname, len); | strlcpy(name, sca->ifp->if_xname, len); | ||||
/* Tell the world, that we are ready to rock. */ | /* Tell the world, that we are ready to rock. */ | ||||
sca->ifp->if_drv_flags |= IFF_DRV_RUNNING; | sca->ifp->if_drv_flags |= IFF_DRV_RUNNING; | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | epair_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) | ||||
/* Third free any queued packets and all the resources. */ | /* Third free any queued packets and all the resources. */ | ||||
CURVNET_SET_QUIET(oifp->if_vnet); | CURVNET_SET_QUIET(oifp->if_vnet); | ||||
epair_drain_rings(scb); | epair_drain_rings(scb); | ||||
oifp->if_softc = NULL; | oifp->if_softc = NULL; | ||||
error = if_clone_destroyif(ifc, oifp); | error = if_clone_destroyif(ifc, oifp); | ||||
if (error) | if (error) | ||||
panic("%s: if_clone_destroyif() for our 2nd iface failed: %d", | panic("%s: if_clone_destroyif() for our 2nd iface failed: %d", | ||||
__func__, error); | __func__, error); | ||||
if_free(oifp); | free_sc(scb); | ||||
ifmedia_removeall(&scb->media); | |||||
for (int i = 0; i < scb->num_queues; i++) { | |||||
struct epair_queue *q = &scb->queues[i]; | |||||
buf_ring_free(q->rxring[0], M_EPAIR); | |||||
buf_ring_free(q->rxring[1], M_EPAIR); | |||||
} | |||||
free(scb->queues, M_EPAIR); | |||||
free(scb, M_EPAIR); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
epair_drain_rings(sca); | epair_drain_rings(sca); | ||||
if_free(ifp); | free_sc(sca); | ||||
ifmedia_removeall(&sca->media); | |||||
for (int i = 0; i < sca->num_queues; i++) { | |||||
struct epair_queue *q = &sca->queues[i]; | |||||
buf_ring_free(q->rxring[0], M_EPAIR); | |||||
buf_ring_free(q->rxring[1], M_EPAIR); | |||||
} | |||||
free(sca->queues, M_EPAIR); | |||||
free(sca, M_EPAIR); | |||||
/* Last free the cloner unit. */ | /* Last free the cloner unit. */ | ||||
ifc_free_unit(ifc, unit); | ifc_free_unit(ifc, unit); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 107 Lines • Show Last 20 Lines |
I think epair_alloc_sc() will prevent potential naming pollution.
Although it is modified with static but still a dedicated prefix epair_ looks consistent with current naming.