Skip to content

Commit

Permalink
epair: deduplicate interface allocation code #1
Browse files Browse the repository at this point in the history
Simplify epair_clone_create() and epair_clone_destroy() by
 factoring out epair softc allocation / desctruction and
 interface setup/teardown into separate functions.

Reviewed By: kp, zlei.huang_gmail.com
Differential Revision: https://reviews.freebsd.org/D36614
MFC after:	2 weeks
  • Loading branch information
AlexanderChernikov committed Sep 22, 2022
1 parent 9453ec6 commit 12aeeb9
Showing 1 changed file with 137 additions and 137 deletions.
274 changes: 137 additions & 137 deletions sys/net/if_epair.c
Expand Up @@ -385,6 +385,11 @@ epair_transmit(struct ifnet *ifp, struct mbuf *m)
return (error);
}

static void
epair_qflush(struct ifnet *ifp __unused)
{
}

static int
epair_media_change(struct ifnet *ifp __unused)
{
Expand Down Expand Up @@ -486,16 +491,133 @@ epair_clone_add(struct if_clone *ifc, struct epair_softc *scb)
if_clone_addif(ifc, ifp);
}

static struct epair_softc *
epair_alloc_sc(struct if_clone *ifc)
{
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
epair_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
epair_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
epair_free_sc(struct epair_softc *sc)
{
if (sc == NULL)
return;

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
epair_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
{
struct epair_softc *sca, *scb;
struct ifnet *ifp;
char *dp;
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 */

/* Try to see if a special unit was requested. */
Expand Down Expand Up @@ -539,146 +661,40 @@ epair_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
return (EEXIST);

/* Allocate memory for both [ab] interfaces */
sca = malloc(sizeof(struct epair_softc), M_EPAIR, M_WAITOK | M_ZERO);
sca->ifp = if_alloc(IFT_ETHER);
sca->num_queues = epair_tasks.tasks;
if (sca->ifp == NULL) {
free(sca, M_EPAIR);
sca = epair_alloc_sc(ifc);
scb = epair_alloc_sc(ifc);
if (sca == NULL || scb == NULL) {
epair_free_sc(sca);
epair_free_sc(scb);
ifc_free_unit(ifc, unit);
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.
*/
sca->oifp = scb->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. */
ifp = sca->ifp;
ifp->if_softc = sca;
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);
epair_setup_ifp(sca, name, unit);
epair_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);
ifp->if_baudrate = IF_Gbps(10); /* arbitrary maximum */
ifp->if_transmit = epair_transmit;

/* Swap the name and finish initialization of interface <n>b. */
*dp = 'b';

epair_setup_ifp(scb, name, unit);

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. */
strlcpy(name, epairname, len);

/* Correctly set the name for the cloner list. */
strlcpy(name, scb->ifp->if_xname, len);
epair_clone_add(ifc, scb);

ifp->if_baudrate = IF_Gbps(10); /* arbitrary maximum */
ifp->if_transmit = epair_transmit;
epair_clone_add(ifc, scb);

/*
* Restore name to <n>a as the ifp for this will go into the
Expand Down Expand Up @@ -751,27 +767,11 @@ epair_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
if (error)
panic("%s: if_clone_destroyif() for our 2nd iface failed: %d",
__func__, error);
if_free(oifp);
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);
epair_free_sc(scb);
CURVNET_RESTORE();

epair_drain_rings(sca);
if_free(ifp);
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);
epair_free_sc(sca);

/* Last free the cloner unit. */
ifc_free_unit(ifc, unit);
Expand Down

0 comments on commit 12aeeb9

Please sign in to comment.