Add proper detach support for Xen3 xennet.
This commit is contained in:
parent
02e9db01de
commit
e35565d1d4
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: if_xennet_xenbus.c,v 1.5 2006/04/02 13:56:12 bouyer Exp $ */
|
||||
/* $NetBSD: if_xennet_xenbus.c,v 1.6 2006/04/02 15:32:41 bouyer Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Manuel Bouyer.
|
||||
@ -61,7 +61,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: if_xennet_xenbus.c,v 1.5 2006/04/02 13:56:12 bouyer Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: if_xennet_xenbus.c,v 1.6 2006/04/02 15:32:41 bouyer Exp $");
|
||||
|
||||
#include "opt_xen.h"
|
||||
#include "opt_nfs_boot.h"
|
||||
@ -122,7 +122,8 @@ int xennet_debug = 0xff;
|
||||
#define DPRINTFN(n,x)
|
||||
#endif
|
||||
|
||||
#define GRANT_INVALID_REF -1
|
||||
#define GRANT_INVALID_REF -1 /* entry is free */
|
||||
#define GRANT_STACK_REF -2 /* entry owned by the network stack */
|
||||
|
||||
#define NET_TX_RING_SIZE __RING_SIZE((netif_tx_sring_t *)0, PAGE_SIZE)
|
||||
#define NET_RX_RING_SIZE __RING_SIZE((netif_rx_sring_t *)0, PAGE_SIZE)
|
||||
@ -180,9 +181,12 @@ static paddr_t xennet_pages[NET_RX_RING_SIZE];
|
||||
|
||||
static int xennet_xenbus_match(struct device *, struct cfdata *, void *);
|
||||
static void xennet_xenbus_attach(struct device *, struct device *, void *);
|
||||
static int xennet_xenbus_detach(struct device *, int);
|
||||
static void xennet_backend_changed(struct device *, XenbusState);
|
||||
|
||||
static int xennet_xenbus_resume(void *);
|
||||
static void xennet_alloc_rx_buffer(struct xennet_xenbus_softc *);
|
||||
static void xennet_free_rx_buffer(struct xennet_xenbus_softc *);
|
||||
static void xennet_tx_complete(struct xennet_xenbus_softc *);
|
||||
static void xennet_rx_mbuf_free(struct mbuf *, caddr_t, size_t, void *);
|
||||
static int xennet_handler(void *);
|
||||
@ -199,7 +203,7 @@ static int xennet_ioctl(struct ifnet *, u_long, caddr_t);
|
||||
static void xennet_watchdog(struct ifnet *);
|
||||
|
||||
CFATTACH_DECL(xennet_xenbus, sizeof(struct xennet_xenbus_softc),
|
||||
xennet_xenbus_match, xennet_xenbus_attach, NULL, NULL);
|
||||
xennet_xenbus_match, xennet_xenbus_attach, xennet_xenbus_detach, NULL);
|
||||
|
||||
static int
|
||||
xennet_xenbus_match(struct device *parent, struct cfdata *match, void *aux)
|
||||
@ -258,6 +262,8 @@ xennet_xenbus_attach(struct device *parent, struct device *self, void *aux)
|
||||
}
|
||||
#endif /* XENNET_DEBUG */
|
||||
sc->sc_xbusd = xa->xa_xbusd;
|
||||
sc->sc_xbusd->xbusd_otherend_changed = xennet_backend_changed;
|
||||
|
||||
/* initialize free RX and RX request lists */
|
||||
SLIST_INIT(&sc->sc_txreq_head);
|
||||
for (i = 0; i < NET_TX_RING_SIZE; i++) {
|
||||
@ -278,7 +284,7 @@ xennet_xenbus_attach(struct device *parent, struct device *self, void *aux)
|
||||
if (!pmap_extract(pmap_kernel(), rxreq->rxreq_va,
|
||||
&rxreq->rxreq_pa))
|
||||
panic("xennet: no pa for mapped va ?");
|
||||
|
||||
rxreq->rxreq_gntref = GRANT_INVALID_REF;
|
||||
SLIST_INSERT_HEAD(&sc->sc_rxreq_head, rxreq, rxreq_next);
|
||||
}
|
||||
splx(s);
|
||||
@ -332,6 +338,54 @@ xennet_xenbus_attach(struct device *parent, struct device *self, void *aux)
|
||||
xennet_xenbus_resume(sc);
|
||||
}
|
||||
|
||||
static int
|
||||
xennet_xenbus_detach(struct device *self, int flags)
|
||||
{
|
||||
struct xennet_xenbus_softc *sc = (void *)self;
|
||||
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
||||
int s0, s1;
|
||||
RING_IDX i;
|
||||
|
||||
DPRINTF(("%s: xennet_xenbus_detach\n", sc->sc_dev.dv_xname));
|
||||
s0 = splnet();
|
||||
xennet_stop(ifp, 1);
|
||||
/* wait for pending TX to complete, and collect pending RX packets */
|
||||
xennet_handler(sc);
|
||||
while (sc->sc_tx_ring.sring->rsp_prod != sc->sc_tx_ring.rsp_cons) {
|
||||
tsleep(xennet_xenbus_detach, PRIBIO, "xnet_detach", hz/2);
|
||||
xennet_handler(sc);
|
||||
}
|
||||
xennet_free_rx_buffer(sc);
|
||||
|
||||
s1 = splvm();
|
||||
for (i = 0; i < NET_RX_RING_SIZE; i++) {
|
||||
struct xennet_rxreq *rxreq = &sc->sc_rxreqs[i];
|
||||
uvm_km_free(kernel_map, rxreq->rxreq_va, PAGE_SIZE,
|
||||
UVM_KMF_WIRED);
|
||||
}
|
||||
splx(s1);
|
||||
|
||||
ether_ifdetach(ifp);
|
||||
if_detach(ifp);
|
||||
while (xengnt_status(sc->sc_tx_ring_gntref)) {
|
||||
tsleep(xennet_xenbus_detach, PRIBIO, "xnet_txref", hz/2);
|
||||
}
|
||||
xengnt_revoke_access(sc->sc_tx_ring_gntref);
|
||||
uvm_km_free(kernel_map, (vaddr_t)sc->sc_tx_ring.sring, PAGE_SIZE,
|
||||
UVM_KMF_WIRED);
|
||||
while (xengnt_status(sc->sc_rx_ring_gntref)) {
|
||||
tsleep(xennet_xenbus_detach, PRIBIO, "xnet_rxref", hz/2);
|
||||
}
|
||||
xengnt_revoke_access(sc->sc_rx_ring_gntref);
|
||||
uvm_km_free(kernel_map, (vaddr_t)sc->sc_rx_ring.sring, PAGE_SIZE,
|
||||
UVM_KMF_WIRED);
|
||||
softintr_disestablish(sc->sc_softintr);
|
||||
event_remove_handler(sc->sc_evtchn, &xennet_handler, sc);
|
||||
splx(s0);
|
||||
DPRINTF(("%s: xennet_xenbus_detach done\n", sc->sc_dev.dv_xname));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xennet_xenbus_resume(void *p)
|
||||
{
|
||||
@ -411,6 +465,7 @@ again:
|
||||
xenbus_dev_fatal(sc->sc_xbusd, error, "completing transaction");
|
||||
return -1;
|
||||
}
|
||||
xennet_alloc_rx_buffer(sc);
|
||||
sc->sc_backend_status = BEST_CONNECTED;
|
||||
return 0;
|
||||
|
||||
@ -420,6 +475,27 @@ abort_transaction:
|
||||
return error;
|
||||
}
|
||||
|
||||
static void xennet_backend_changed(struct device *dev, XenbusState new_state)
|
||||
{
|
||||
struct xennet_xenbus_softc *sc = (void *)dev;
|
||||
DPRINTF(("%s: new backend state %d\n", sc->sc_dev.dv_xname, new_state));
|
||||
|
||||
switch (new_state) {
|
||||
case XenbusStateUnknown:
|
||||
case XenbusStateInitialising:
|
||||
case XenbusStateInitWait:
|
||||
case XenbusStateInitialised:
|
||||
case XenbusStateClosing:
|
||||
sc->sc_backend_status = BEST_CLOSED;
|
||||
xenbus_switch_state(sc->sc_xbusd, NULL, XenbusStateClosed);
|
||||
break;
|
||||
case XenbusStateConnected:
|
||||
break;
|
||||
default:
|
||||
panic("bad backend state %d", new_state);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xennet_alloc_rx_buffer(struct xennet_xenbus_softc *sc)
|
||||
{
|
||||
@ -490,6 +566,81 @@ xennet_alloc_rx_buffer(struct xennet_xenbus_softc *sc)
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
xennet_free_rx_buffer(struct xennet_xenbus_softc *sc)
|
||||
{
|
||||
paddr_t ma, pa;
|
||||
vaddr_t va;
|
||||
RING_IDX i;
|
||||
mmu_update_t mmu[1];
|
||||
multicall_entry_t mcl[2];
|
||||
|
||||
int s = splbio();
|
||||
|
||||
DPRINTF(("%s: xennet_free_rx_buffer\n", sc->sc_dev.dv_xname));
|
||||
/* get back memory from RX ring */
|
||||
for (i = 0; i < NET_RX_RING_SIZE; i++) {
|
||||
struct xennet_rxreq *rxreq = &sc->sc_rxreqs[i];
|
||||
|
||||
/*
|
||||
* if the buffer is in transit in the network stack, wait for
|
||||
* the network stack to free it.
|
||||
*/
|
||||
while ((volatile grant_ref_t)rxreq->rxreq_gntref ==
|
||||
GRANT_STACK_REF)
|
||||
tsleep(xennet_xenbus_detach, PRIBIO, "xnet_free", hz/2);
|
||||
|
||||
if (rxreq->rxreq_gntref != GRANT_INVALID_REF) {
|
||||
/*
|
||||
* this req is still granted. Get back the page or
|
||||
* allocate a new one, and remap it.
|
||||
*/
|
||||
SLIST_INSERT_HEAD(&sc->sc_rxreq_head, rxreq,
|
||||
rxreq_next);
|
||||
sc->sc_free_rxreql++;
|
||||
ma = xengnt_revoke_transfer(rxreq->rxreq_gntref);
|
||||
rxreq->rxreq_gntref = GRANT_INVALID_REF;
|
||||
if (ma == 0) {
|
||||
struct xen_memory_reservation xenres;
|
||||
/*
|
||||
* transfer not complete, we lost the page.
|
||||
* Get one from hypervisor
|
||||
*/
|
||||
xenres.extent_start = &ma;
|
||||
xenres.nr_extents = 1;
|
||||
xenres.extent_order = 0;
|
||||
xenres.address_bits = 31;
|
||||
xenres.domid = DOMID_SELF;
|
||||
if (HYPERVISOR_memory_op(
|
||||
XENMEM_increase_reservation, &xenres) < 0) {
|
||||
panic("xennet_free_rx_buffer: "
|
||||
"can't get memory back");
|
||||
}
|
||||
KASSERT(ma != 0);
|
||||
}
|
||||
pa = rxreq->rxreq_pa;
|
||||
va = rxreq->rxreq_va;
|
||||
/* remap the page */
|
||||
mmu[0].ptr = (ma << PAGE_SHIFT) | MMU_MACHPHYS_UPDATE;
|
||||
mmu[0].val = ((pa - XPMAP_OFFSET) >> PAGE_SHIFT);
|
||||
MULTI_update_va_mapping(&mcl[0], va,
|
||||
(ma << PAGE_SHIFT) | PG_V | PG_KW,
|
||||
UVMF_TLB_FLUSH|UVMF_ALL);
|
||||
xpmap_phys_to_machine_mapping[
|
||||
(pa - XPMAP_OFFSET) >> PAGE_SHIFT] = ma;
|
||||
mcl[1].op = __HYPERVISOR_mmu_update;
|
||||
mcl[1].args[0] = (unsigned long)mmu;
|
||||
mcl[1].args[1] = 1;
|
||||
mcl[1].args[2] = 0;
|
||||
mcl[1].args[3] = DOMID_SELF;
|
||||
HYPERVISOR_multicall(mcl, 2);
|
||||
}
|
||||
|
||||
}
|
||||
splx(s);
|
||||
DPRINTF(("%s: xennet_free_rx_buffer done\n", sc->sc_dev.dv_xname));
|
||||
}
|
||||
|
||||
static void
|
||||
xennet_rx_mbuf_free(struct mbuf *m, caddr_t buf, size_t size, void *arg)
|
||||
{
|
||||
@ -499,8 +650,11 @@ xennet_rx_mbuf_free(struct mbuf *m, caddr_t buf, size_t size, void *arg)
|
||||
SLIST_INSERT_HEAD(&sc->sc_rxreq_head, req, rxreq_next);
|
||||
sc->sc_free_rxreql++;
|
||||
|
||||
if (sc->sc_free_rxreql >= NET_RX_RING_SIZE / 2)
|
||||
req->rxreq_gntref = GRANT_INVALID_REF;
|
||||
if (sc->sc_free_rxreql >= NET_RX_RING_SIZE / 2 &&
|
||||
__predict_true(sc->sc_backend_status == BEST_CONNECTED)) {
|
||||
xennet_alloc_rx_buffer(sc);
|
||||
}
|
||||
|
||||
if (m)
|
||||
pool_cache_put(&mbpool_cache, m);
|
||||
@ -508,7 +662,7 @@ xennet_rx_mbuf_free(struct mbuf *m, caddr_t buf, size_t size, void *arg)
|
||||
|
||||
|
||||
static void
|
||||
xennet_tx_complete(struct xennet_xenbus_softc * sc)
|
||||
xennet_tx_complete(struct xennet_xenbus_softc *sc)
|
||||
{
|
||||
struct xennet_txreq *req;
|
||||
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
||||
@ -596,14 +750,7 @@ again:
|
||||
RING_PUSH_REQUESTS(&sc->sc_rx_ring);
|
||||
continue;
|
||||
}
|
||||
|
||||
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
||||
if (__predict_false(m == NULL)) {
|
||||
printf("xennet: rx no mbuf\n");
|
||||
ifp->if_ierrors++;
|
||||
sc->sc_rx_ring.rsp_cons = i;
|
||||
return 1;
|
||||
}
|
||||
req->rxreq_gntref = GRANT_INVALID_REF;
|
||||
|
||||
pa = req->rxreq_pa;
|
||||
va = req->rxreq_va;
|
||||
@ -632,16 +779,24 @@ again:
|
||||
/* packet not for us */
|
||||
xennet_rx_mbuf_free(NULL, (void *)va, PAGE_SIZE,
|
||||
req);
|
||||
m_freem(m);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
||||
if (__predict_false(m == NULL)) {
|
||||
printf("xennet: rx no mbuf\n");
|
||||
ifp->if_ierrors++;
|
||||
xennet_rx_mbuf_free(NULL, (void *)va, PAGE_SIZE, req);
|
||||
continue;
|
||||
}
|
||||
|
||||
m->m_pkthdr.rcvif = ifp;
|
||||
if (__predict_true(sc->sc_rx_ring.req_prod_pvt !=
|
||||
sc->sc_rx_ring.sring->rsp_prod)) {
|
||||
m->m_len = m->m_pkthdr.len = rx->status;
|
||||
MEXTADD(m, pktp, rx->status,
|
||||
M_DEVBUF, xennet_rx_mbuf_free, req);
|
||||
req->rxreq_gntref = GRANT_STACK_REF;
|
||||
} else {
|
||||
/*
|
||||
* This was our last receive buffer, allocate
|
||||
@ -921,22 +1076,16 @@ xennet_init(struct ifnet *ifp)
|
||||
|
||||
DPRINTFN(XEDB_FOLLOW, ("%s: xennet_init()\n", sc->sc_dev.dv_xname));
|
||||
|
||||
if (ifp->if_flags & IFF_UP) {
|
||||
if ((ifp->if_flags & IFF_RUNNING) == 0) {
|
||||
xennet_alloc_rx_buffer(sc);
|
||||
sc->sc_rx_ring.sring->rsp_event =
|
||||
sc->sc_rx_ring.rsp_cons + 1;
|
||||
hypervisor_enable_event(sc->sc_evtchn);
|
||||
hypervisor_notify_via_evtchn(sc->sc_evtchn);
|
||||
xennet_reset(sc);
|
||||
}
|
||||
ifp->if_flags |= IFF_RUNNING;
|
||||
ifp->if_flags &= ~IFF_OACTIVE;
|
||||
ifp->if_timer = 0;
|
||||
} else {
|
||||
ifp->if_flags &= ~IFF_RUNNING;
|
||||
if ((ifp->if_flags & IFF_RUNNING) == 0) {
|
||||
sc->sc_rx_ring.sring->rsp_event =
|
||||
sc->sc_rx_ring.rsp_cons + 1;
|
||||
hypervisor_enable_event(sc->sc_evtchn);
|
||||
hypervisor_notify_via_evtchn(sc->sc_evtchn);
|
||||
xennet_reset(sc);
|
||||
}
|
||||
ifp->if_flags |= IFF_RUNNING;
|
||||
ifp->if_flags &= ~IFF_OACTIVE;
|
||||
ifp->if_timer = 0;
|
||||
splx(s);
|
||||
return 0;
|
||||
}
|
||||
@ -944,7 +1093,13 @@ xennet_init(struct ifnet *ifp)
|
||||
void
|
||||
xennet_stop(struct ifnet *ifp, int disable)
|
||||
{
|
||||
struct xennet_xenbus_softc *sc = ifp->if_softc;
|
||||
int s = splnet();
|
||||
|
||||
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
|
||||
hypervisor_mask_event(sc->sc_evtchn);
|
||||
xennet_reset(sc);
|
||||
splx(s);
|
||||
}
|
||||
|
||||
void
|
||||
|
Loading…
Reference in New Issue
Block a user