diff options
Diffstat (limited to 'sys/dev/netmap/netmap_monitor.c')
-rw-r--r-- | sys/dev/netmap/netmap_monitor.c | 405 |
1 files changed, 249 insertions, 156 deletions
diff --git a/sys/dev/netmap/netmap_monitor.c b/sys/dev/netmap/netmap_monitor.c index bf6e23f5546e..8b788920ff80 100644 --- a/sys/dev/netmap/netmap_monitor.c +++ b/sys/dev/netmap/netmap_monitor.c @@ -128,6 +128,13 @@ ******************************************************************** */ +static int netmap_zmon_reg(struct netmap_adapter *, int); +static int +nm_is_zmon(struct netmap_adapter *na) +{ + return na->nm_register == netmap_zmon_reg; +} + /* nm_sync callback for the monitor's own tx rings. * This makes no sense and always returns error */ @@ -148,7 +155,7 @@ static int netmap_monitor_rxsync(struct netmap_kring *kring, int flags) { ND("%s %x", kring->name, flags); - kring->nr_hwcur = kring->rcur; + kring->nr_hwcur = kring->rhead; mb(); return 0; } @@ -185,19 +192,16 @@ nm_txrx2flag(enum txrx t) static int nm_monitor_alloc(struct netmap_kring *kring, u_int n) { - size_t len; + size_t old_len, len; struct netmap_kring **nm; if (n <= kring->max_monitors) /* we already have more entries that requested */ return 0; + old_len = sizeof(struct netmap_kring *)*kring->max_monitors; len = sizeof(struct netmap_kring *) * n; -#ifndef _WIN32 - nm = realloc(kring->monitors, len, M_DEVBUF, M_NOWAIT | M_ZERO); -#else - nm = realloc(kring->monitors, len, sizeof(struct netmap_kring *)*kring->max_monitors); -#endif + nm = nm_os_realloc(kring->monitors, len, old_len); if (nm == NULL) return ENOMEM; @@ -216,13 +220,22 @@ nm_monitor_dealloc(struct netmap_kring *kring) D("freeing not empty monitor array for %s (%d dangling monitors)!", kring->name, kring->n_monitors); } - free(kring->monitors, M_DEVBUF); + nm_os_free(kring->monitors); kring->monitors = NULL; kring->max_monitors = 0; kring->n_monitors = 0; } } +/* returns 1 iff kring has no monitors */ +static inline int +nm_monitor_none(struct netmap_kring *kring) +{ + return kring->n_monitors == 0 && + kring->zmon_list[NR_TX].next == NULL && + kring->zmon_list[NR_RX].next == NULL; +} + /* * monitors work by replacing the nm_sync() and possibly the * nm_notify() callbacks in the monitored rings. @@ -233,71 +246,122 @@ static int netmap_monitor_parent_txsync(struct netmap_kring *, int); static int netmap_monitor_parent_rxsync(struct netmap_kring *, int); static int netmap_monitor_parent_notify(struct netmap_kring *, int); - /* add the monitor mkring to the list of monitors of kring. * If this is the first monitor, intercept the callbacks */ static int -netmap_monitor_add(struct netmap_kring *mkring, struct netmap_kring *kring, int zcopy) +netmap_monitor_add(struct netmap_kring *mkring, struct netmap_kring *kring, int zmon) { int error = NM_IRQ_COMPLETED; + enum txrx t = kring->tx; + struct netmap_zmon_list *z = &kring->zmon_list[t]; + struct netmap_zmon_list *mz = &mkring->zmon_list[t]; + + /* a zero-copy monitor which is not the first in the list + * must monitor the previous monitor + */ + if (zmon && z->prev != NULL) + kring = z->prev; /* sinchronize with concurrently running nm_sync()s */ nm_kr_stop(kring, NM_KR_LOCKED); - /* make sure the monitor array exists and is big enough */ - error = nm_monitor_alloc(kring, kring->n_monitors + 1); - if (error) - goto out; - kring->monitors[kring->n_monitors] = mkring; - mkring->mon_pos = kring->n_monitors; - kring->n_monitors++; - if (kring->n_monitors == 1) { + + if (nm_monitor_none(kring)) { /* this is the first monitor, intercept callbacks */ - ND("%s: intercept callbacks on %s", mkring->name, kring->name); + ND("intercept callbacks on %s", kring->name); kring->mon_sync = kring->nm_sync; - /* zcopy monitors do not override nm_notify(), but - * we save the original one regardless, so that - * netmap_monitor_del() does not need to know the - * monitor type - */ kring->mon_notify = kring->nm_notify; if (kring->tx == NR_TX) { - kring->nm_sync = (zcopy ? netmap_zmon_parent_txsync : - netmap_monitor_parent_txsync); + kring->nm_sync = netmap_monitor_parent_txsync; } else { - kring->nm_sync = (zcopy ? netmap_zmon_parent_rxsync : - netmap_monitor_parent_rxsync); - if (!zcopy) { - /* also intercept notify */ - kring->nm_notify = netmap_monitor_parent_notify; - kring->mon_tail = kring->nr_hwtail; - } + kring->nm_sync = netmap_monitor_parent_rxsync; + kring->nm_notify = netmap_monitor_parent_notify; + kring->mon_tail = kring->nr_hwtail; } } + if (zmon) { + /* append the zmon to the list */ + struct netmap_monitor_adapter *mna = + (struct netmap_monitor_adapter *)mkring->na; + struct netmap_adapter *pna; + + if (z->prev != NULL) + z->prev->zmon_list[t].next = mkring; + mz->prev = z->prev; + z->prev = mkring; + if (z->next == NULL) + z->next = mkring; + + /* grap a reference to the previous netmap adapter + * in the chain (this may be the monitored port + * or another zero-copy monitor) + */ + pna = kring->na; + netmap_adapter_get(pna); + netmap_adapter_put(mna->priv.np_na); + mna->priv.np_na = pna; + } else { + /* make sure the monitor array exists and is big enough */ + error = nm_monitor_alloc(kring, kring->n_monitors + 1); + if (error) + goto out; + kring->monitors[kring->n_monitors] = mkring; + mkring->mon_pos[kring->tx] = kring->n_monitors; + kring->n_monitors++; + } + out: nm_kr_start(kring); return error; } - /* remove the monitor mkring from the list of monitors of kring. * If this is the last monitor, restore the original callbacks */ static void netmap_monitor_del(struct netmap_kring *mkring, struct netmap_kring *kring) { + struct netmap_zmon_list *mz = &mkring->zmon_list[kring->tx]; + int zmon = nm_is_zmon(mkring->na); + + + if (zmon && mz->prev != NULL) + kring = mz->prev; + /* sinchronize with concurrently running nm_sync()s */ nm_kr_stop(kring, NM_KR_LOCKED); - kring->n_monitors--; - if (mkring->mon_pos != kring->n_monitors) { - kring->monitors[mkring->mon_pos] = kring->monitors[kring->n_monitors]; - kring->monitors[mkring->mon_pos]->mon_pos = mkring->mon_pos; + + if (zmon) { + /* remove the monitor from the list */ + if (mz->prev != NULL) + mz->prev->zmon_list[kring->tx].next = mz->next; + else + kring->zmon_list[kring->tx].next = mz->next; + if (mz->next != NULL) { + mz->next->zmon_list[kring->tx].prev = mz->prev; + } else { + kring->zmon_list[kring->tx].prev = mz->prev; + } + } else { + /* this is a copy monitor */ + uint32_t mon_pos = mkring->mon_pos[kring->tx]; + kring->n_monitors--; + if (mon_pos != kring->n_monitors) { + kring->monitors[mon_pos] = + kring->monitors[kring->n_monitors]; + kring->monitors[mon_pos]->mon_pos[kring->tx] = mon_pos; + } + kring->monitors[kring->n_monitors] = NULL; + if (kring->n_monitors == 0) { + nm_monitor_dealloc(kring); + } } - kring->monitors[kring->n_monitors] = NULL; - if (kring->n_monitors == 0) { - /* this was the last monitor, restore callbacks and delete monitor array */ - ND("%s: restoring sync on %s: %p", mkring->name, kring->name, kring->mon_sync); + + if (nm_monitor_none(kring)) { + /* this was the last monitor, restore the callbacks */ + ND("%s: restoring sync on %s: %p", mkring->name, kring->name, + kring->mon_sync); kring->nm_sync = kring->mon_sync; kring->mon_sync = NULL; if (kring->tx == NR_RX) { @@ -306,8 +370,8 @@ netmap_monitor_del(struct netmap_kring *mkring, struct netmap_kring *kring) kring->nm_notify = kring->mon_notify; kring->mon_notify = NULL; } - nm_monitor_dealloc(kring); } + nm_kr_start(kring); } @@ -329,6 +393,7 @@ netmap_monitor_stop(struct netmap_adapter *na) for (i = 0; i < nma_get_nrings(na, t) + 1; i++) { struct netmap_kring *kring = &NMR(na, t)[i]; + struct netmap_kring *zkring; u_int j; for (j = 0; j < kring->n_monitors; j++) { @@ -337,8 +402,33 @@ netmap_monitor_stop(struct netmap_adapter *na) struct netmap_monitor_adapter *mna = (struct netmap_monitor_adapter *)mkring->na; /* forget about this adapter */ - netmap_adapter_put(mna->priv.np_na); - mna->priv.np_na = NULL; + if (mna->priv.np_na != NULL) { + netmap_adapter_put(mna->priv.np_na); + mna->priv.np_na = NULL; + } + } + + zkring = kring->zmon_list[kring->tx].next; + if (zkring != NULL) { + struct netmap_monitor_adapter *next = + (struct netmap_monitor_adapter *)zkring->na; + struct netmap_monitor_adapter *this = + (struct netmap_monitor_adapter *)na; + struct netmap_adapter *pna = this->priv.np_na; + /* let the next monitor forget about us */ + if (next->priv.np_na != NULL) { + netmap_adapter_put(next->priv.np_na); + } + if (pna != NULL && nm_is_zmon(na)) { + /* we are a monitor ourselves and we may + * need to pass down the reference to + * the previous adapter in the chain + */ + netmap_adapter_get(pna); + next->priv.np_na = pna; + continue; + } + next->priv.np_na = NULL; } } } @@ -357,7 +447,7 @@ netmap_monitor_reg_common(struct netmap_adapter *na, int onoff, int zmon) struct netmap_adapter *pna = priv->np_na; struct netmap_kring *kring, *mkring; int i; - enum txrx t; + enum txrx t, s; ND("%p: onoff %d", na, onoff); if (onoff) { @@ -367,13 +457,19 @@ netmap_monitor_reg_common(struct netmap_adapter *na, int onoff, int zmon) return ENXIO; } for_rx_tx(t) { - if (mna->flags & nm_txrx2flag(t)) { - for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) { - kring = &NMR(pna, t)[i]; - mkring = &na->rx_rings[i]; - if (nm_kring_pending_on(mkring)) { + for (i = 0; i < nma_get_nrings(na, t) + 1; i++) { + mkring = &NMR(na, t)[i]; + if (!nm_kring_pending_on(mkring)) + continue; + mkring->nr_mode = NKR_NETMAP_ON; + if (t == NR_TX) + continue; + for_rx_tx(s) { + if (i > nma_get_nrings(pna, s)) + continue; + if (mna->flags & nm_txrx2flag(s)) { + kring = &NMR(pna, s)[i]; netmap_monitor_add(mkring, kring, zmon); - mkring->nr_mode = NKR_NETMAP_ON; } } } @@ -383,19 +479,25 @@ netmap_monitor_reg_common(struct netmap_adapter *na, int onoff, int zmon) if (na->active_fds == 0) na->na_flags &= ~NAF_NETMAP_ON; for_rx_tx(t) { - if (mna->flags & nm_txrx2flag(t)) { - for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) { - mkring = &na->rx_rings[i]; - if (nm_kring_pending_off(mkring)) { - mkring->nr_mode = NKR_NETMAP_OFF; - /* we cannot access the parent krings if the parent - * has left netmap mode. This is signaled by a NULL - * pna pointer - */ - if (pna) { - kring = &NMR(pna, t)[i]; - netmap_monitor_del(mkring, kring); - } + for (i = 0; i < nma_get_nrings(na, t) + 1; i++) { + mkring = &NMR(na, t)[i]; + if (!nm_kring_pending_off(mkring)) + continue; + mkring->nr_mode = NKR_NETMAP_OFF; + if (t == NR_TX) + continue; + /* we cannot access the parent krings if the parent + * has left netmap mode. This is signaled by a NULL + * pna pointer + */ + if (pna == NULL) + continue; + for_rx_tx(s) { + if (i > nma_get_nrings(pna, s)) + continue; + if (mna->flags & nm_txrx2flag(s)) { + kring = &NMR(pna, s)[i]; + netmap_monitor_del(mkring, kring); } } } @@ -417,7 +519,7 @@ netmap_monitor_reg_common(struct netmap_adapter *na, int onoff, int zmon) static int netmap_zmon_parent_sync(struct netmap_kring *kring, int flags, enum txrx tx) { - struct netmap_kring *mkring = kring->monitors[0]; + struct netmap_kring *mkring = kring->zmon_list[tx].next; struct netmap_ring *ring = kring->ring, *mring; int error = 0; int rel_slots, free_slots, busy, sent = 0; @@ -434,11 +536,11 @@ netmap_zmon_parent_sync(struct netmap_kring *kring, int flags, enum txrx tx) /* get the relased slots (rel_slots) */ if (tx == NR_TX) { - beg = kring->nr_hwtail; + beg = kring->nr_hwtail + 1; error = kring->mon_sync(kring, flags); if (error) return error; - end = kring->nr_hwtail; + end = kring->nr_hwtail + 1; } else { /* NR_RX */ beg = kring->nr_hwcur; end = kring->rhead; @@ -473,10 +575,10 @@ netmap_zmon_parent_sync(struct netmap_kring *kring, int flags, enum txrx tx) /* swap min(free_slots, rel_slots) slots */ if (free_slots < rel_slots) { beg += (rel_slots - free_slots); - if (beg >= kring->nkr_num_slots) - beg -= kring->nkr_num_slots; rel_slots = free_slots; } + if (unlikely(beg >= kring->nkr_num_slots)) + beg -= kring->nkr_num_slots; sent = rel_slots; for ( ; rel_slots; rel_slots--) { @@ -521,7 +623,6 @@ out_rxsync: static int netmap_zmon_parent_txsync(struct netmap_kring *kring, int flags) { - ND("%s %x", kring->name, flags); return netmap_zmon_parent_sync(kring, flags, NR_TX); } @@ -529,11 +630,9 @@ netmap_zmon_parent_txsync(struct netmap_kring *kring, int flags) static int netmap_zmon_parent_rxsync(struct netmap_kring *kring, int flags) { - ND("%s %x", kring->name, flags); return netmap_zmon_parent_sync(kring, flags, NR_RX); } - static int netmap_zmon_reg(struct netmap_adapter *na, int onoff) { @@ -638,12 +737,17 @@ netmap_monitor_parent_txsync(struct netmap_kring *kring, int flags) int new_slots; /* get the new slots */ - first_new = kring->nr_hwcur; - new_slots = kring->rhead - first_new; - if (new_slots < 0) - new_slots += kring->nkr_num_slots; - if (new_slots) - netmap_monitor_parent_sync(kring, first_new, new_slots); + if (kring->n_monitors > 0) { + first_new = kring->nr_hwcur; + new_slots = kring->rhead - first_new; + if (new_slots < 0) + new_slots += kring->nkr_num_slots; + if (new_slots) + netmap_monitor_parent_sync(kring, first_new, new_slots); + } + if (kring->zmon_list[NR_TX].next != NULL) { + return netmap_zmon_parent_txsync(kring, flags); + } return kring->mon_sync(kring, flags); } @@ -655,16 +759,22 @@ netmap_monitor_parent_rxsync(struct netmap_kring *kring, int flags) int new_slots, error; /* get the new slots */ - error = kring->mon_sync(kring, flags); + if (kring->zmon_list[NR_RX].next != NULL) { + error = netmap_zmon_parent_rxsync(kring, flags); + } else { + error = kring->mon_sync(kring, flags); + } if (error) return error; - first_new = kring->mon_tail; - new_slots = kring->nr_hwtail - first_new; - if (new_slots < 0) - new_slots += kring->nkr_num_slots; - if (new_slots) - netmap_monitor_parent_sync(kring, first_new, new_slots); - kring->mon_tail = kring->nr_hwtail; + if (kring->n_monitors > 0) { + first_new = kring->mon_tail; + new_slots = kring->nr_hwtail - first_new; + if (new_slots < 0) + new_slots += kring->nkr_num_slots; + if (new_slots) + netmap_monitor_parent_sync(kring, first_new, new_slots); + kring->mon_tail = kring->nr_hwtail; + } return 0; } @@ -684,12 +794,14 @@ netmap_monitor_parent_notify(struct netmap_kring *kring, int flags) } if (kring->n_monitors > 0) { netmap_monitor_parent_rxsync(kring, NAF_FORCE_READ); - notify = kring->mon_notify; - } else { + } + if (nm_monitor_none(kring)) { /* we are no longer monitoring this ring, so both * mon_sync and mon_notify are NULL */ notify = kring->nm_notify; + } else { + notify = kring->mon_notify; } nm_kr_put(kring); return notify(kring, flags); @@ -716,24 +828,21 @@ netmap_monitor_dtor(struct netmap_adapter *na) /* check if nmr is a request for a monitor adapter that we can satisfy */ int -netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na, int create) +netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na, + struct netmap_mem_d *nmd, int create) { struct nmreq pnmr; struct netmap_adapter *pna; /* parent adapter */ struct netmap_monitor_adapter *mna; struct ifnet *ifp = NULL; - int i, error; - enum txrx t; + int error; int zcopy = (nmr->nr_flags & NR_ZCOPY_MON); char monsuff[10] = ""; + if (zcopy) { + nmr->nr_flags |= (NR_MONITOR_TX | NR_MONITOR_RX); + } if ((nmr->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX)) == 0) { - if (nmr->nr_flags & NR_ZCOPY_MON) { - /* the flag makes no sense unless you are - * creating a monitor - */ - return EINVAL; - } ND("not a monitor"); return 0; } @@ -741,12 +850,6 @@ netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na, int create) ND("flags %x", nmr->nr_flags); - mna = malloc(sizeof(*mna), M_DEVBUF, M_NOWAIT | M_ZERO); - if (mna == NULL) { - D("memory error"); - return ENOMEM; - } - /* first, try to find the adapter that we want to monitor * We use the same nmr, after we have turned off the monitor flags. * In this way we can potentially monitor everything netmap understands, @@ -754,10 +857,9 @@ netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na, int create) */ memcpy(&pnmr, nmr, sizeof(pnmr)); pnmr.nr_flags &= ~(NR_MONITOR_TX | NR_MONITOR_RX | NR_ZCOPY_MON); - error = netmap_get_na(&pnmr, &pna, &ifp, create); + error = netmap_get_na(&pnmr, &pna, &ifp, nmd, create); if (error) { D("parent lookup failed: %d", error); - free(mna, M_DEVBUF); return error; } ND("found parent: %s", pna->name); @@ -772,12 +874,19 @@ netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na, int create) goto put_out; } - /* grab all the rings we need in the parent */ + mna = nm_os_malloc(sizeof(*mna)); + if (mna == NULL) { + D("memory error"); + error = ENOMEM; + goto put_out; + } mna->priv.np_na = pna; + + /* grab all the rings we need in the parent */ error = netmap_interp_ringid(&mna->priv, nmr->nr_ringid, nmr->nr_flags); if (error) { D("ringid error"); - goto put_out; + goto free_out; } if (mna->priv.np_qlast[NR_TX] - mna->priv.np_qfirst[NR_TX] == 1) { snprintf(monsuff, 10, "-%d", mna->priv.np_qfirst[NR_TX]); @@ -788,57 +897,14 @@ netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na, int create) (nmr->nr_flags & NR_MONITOR_RX) ? "r" : "", (nmr->nr_flags & NR_MONITOR_TX) ? "t" : ""); - if (zcopy) { - /* zero copy monitors need exclusive access to the monitored rings */ - for_rx_tx(t) { - if (! (nmr->nr_flags & nm_txrx2flag(t))) - continue; - for (i = mna->priv.np_qfirst[t]; i < mna->priv.np_qlast[t]; i++) { - struct netmap_kring *kring = &NMR(pna, t)[i]; - if (kring->n_monitors > 0) { - error = EBUSY; - D("ring %s already monitored by %s", kring->name, - kring->monitors[0]->name); - goto put_out; - } - } - } - mna->up.nm_register = netmap_zmon_reg; - mna->up.nm_dtor = netmap_zmon_dtor; - /* to have zero copy, we need to use the same memory allocator - * as the monitored port - */ - mna->up.nm_mem = pna->nm_mem; - mna->up.na_lut = pna->na_lut; - } else { - /* normal monitors are incompatible with zero copy ones */ - for_rx_tx(t) { - if (! (nmr->nr_flags & nm_txrx2flag(t))) - continue; - for (i = mna->priv.np_qfirst[t]; i < mna->priv.np_qlast[t]; i++) { - struct netmap_kring *kring = &NMR(pna, t)[i]; - if (kring->n_monitors > 0 && - kring->monitors[0]->na->nm_register == netmap_zmon_reg) - { - error = EBUSY; - D("ring busy"); - goto put_out; - } - } - } - mna->up.nm_rxsync = netmap_monitor_rxsync; - mna->up.nm_register = netmap_monitor_reg; - mna->up.nm_dtor = netmap_monitor_dtor; - } - /* the monitor supports the host rings iff the parent does */ - mna->up.na_flags = (pna->na_flags & NAF_HOST_RINGS); + mna->up.na_flags |= (pna->na_flags & NAF_HOST_RINGS); /* a do-nothing txsync: monitors cannot be used to inject packets */ mna->up.nm_txsync = netmap_monitor_txsync; mna->up.nm_rxsync = netmap_monitor_rxsync; mna->up.nm_krings_create = netmap_monitor_krings_create; mna->up.nm_krings_delete = netmap_monitor_krings_delete; - mna->up.num_tx_rings = 1; // XXX we don't need it, but field can't be zero + mna->up.num_tx_rings = 1; // XXX what should we do here with chained zmons? /* we set the number of our rx_rings to be max(num_rx_rings, num_rx_rings) * in the parent */ @@ -855,14 +921,38 @@ netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na, int create) mna->up.num_rx_desc = nmr->nr_rx_slots; nm_bound_var(&mna->up.num_rx_desc, pna->num_rx_desc, 1, NM_MONITOR_MAXSLOTS, NULL); + if (zcopy) { + mna->up.nm_register = netmap_zmon_reg; + mna->up.nm_dtor = netmap_zmon_dtor; + /* to have zero copy, we need to use the same memory allocator + * as the monitored port + */ + mna->up.nm_mem = netmap_mem_get(pna->nm_mem); + /* and the allocator cannot be changed */ + mna->up.na_flags |= NAF_MEM_OWNER; + } else { + mna->up.nm_register = netmap_monitor_reg; + mna->up.nm_dtor = netmap_monitor_dtor; + mna->up.nm_mem = netmap_mem_private_new( + mna->up.num_tx_rings, + mna->up.num_tx_desc, + mna->up.num_rx_rings, + mna->up.num_rx_desc, + 0, /* extra bufs */ + 0, /* pipes */ + &error); + if (mna->up.nm_mem == NULL) + goto put_out; + } + error = netmap_attach_common(&mna->up); if (error) { D("attach_common error"); - goto put_out; + goto mem_put_out; } /* remember the traffic directions we have to monitor */ - mna->flags = (nmr->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX)); + mna->flags = (nmr->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX | NR_ZCOPY_MON)); *na = &mna->up; netmap_adapter_get(*na); @@ -876,9 +966,12 @@ netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na, int create) return 0; +mem_put_out: + netmap_mem_put(mna->up.nm_mem); +free_out: + nm_os_free(mna); put_out: netmap_unget_na(pna, ifp); - free(mna, M_DEVBUF); return error; } |