#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/msgport.h>
#include <sys/proc.h>
#include <sys/interrupt.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/socketvar.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/netisr2.h>
#include <machine/cpufunc.h>
#include <machine/smp.h>
#include <sys/thread2.h>
#include <sys/msgport2.h>
#include <net/netmsg2.h>
#include <vm/vm_extern.h>
static void netmsg_service_port_init(lwkt_port_t);
static void netmsg_service_loop(void *arg);
static void netisr_hashfn0(struct mbuf **mp, int hoff);
static void netisr_nohashck(struct mbuf *, const struct pktinfo *);
struct netmsg_port_registration {
TAILQ_ENTRY(netmsg_port_registration) npr_entry;
lwkt_port_t npr_port;
};
struct netisr_rollup {
TAILQ_ENTRY(netisr_rollup) ru_entry;
netisr_ru_t ru_func;
int ru_prio;
void *ru_key;
};
struct netmsg_rollup {
struct netmsg_base base;
netisr_ru_t func;
int prio;
void *key;
};
struct netmsg_barrier {
struct netmsg_base base;
volatile cpumask_t *br_cpumask;
volatile uint32_t br_done;
};
#define NETISR_BR_NOTDONE 0x1
#define NETISR_BR_WAITDONE 0x80000000
struct netisr_barrier {
struct netmsg_barrier *br_msgs[MAXCPU];
int br_isset;
};
struct netisr_data {
struct thread thread;
#ifdef INVARIANTS
void *netlastfunc;
#endif
TAILQ_HEAD(, netisr_rollup) netrulist;
};
static struct netisr_data *netisr_data[MAXCPU];
static struct netisr netisrs[NETISR_MAX];
static TAILQ_HEAD(,netmsg_port_registration) netreglist;
struct thread *netisr_threads[MAXCPU];
lwkt_port netisr_afree_rport;
lwkt_port netisr_afree_free_so_rport;
lwkt_port netisr_adone_rport;
lwkt_port netisr_apanic_rport;
lwkt_port netisr_sync_port;
static int (*netmsg_fwd_port_fn)(lwkt_port_t, lwkt_msg_t);
SYSCTL_NODE(_net, OID_AUTO, netisr, CTLFLAG_RW, 0, "netisr");
__read_frequently static int netisr_rollup_limit = 32;
SYSCTL_INT(_net_netisr, OID_AUTO, rollup_limit, CTLFLAG_RW,
&netisr_rollup_limit, 0, "Message to process before rollup");
__read_frequently int netisr_ncpus;
TUNABLE_INT("net.netisr.ncpus", &netisr_ncpus);
SYSCTL_INT(_net_netisr, OID_AUTO, ncpus, CTLFLAG_RD,
&netisr_ncpus, 0, "# of CPUs to handle network messages");
static void
netisr_autofree_reply(lwkt_port_t port, lwkt_msg_t msg)
{
kfree(msg, M_LWKTMSG);
}
static void
netisr_autofree_free_so_reply(lwkt_port_t port, lwkt_msg_t msg)
{
sofree(((netmsg_t)msg)->base.nm_so);
kfree(msg, M_LWKTMSG);
}
static int
netmsg_put_port(lwkt_port_t port, lwkt_msg_t lmsg)
{
netmsg_base_t nmsg = (void *)lmsg;
if ((lmsg->ms_flags & MSGF_SYNC) && port == &curthread->td_msgport) {
nmsg->nm_dispatch((netmsg_t)nmsg);
return(EASYNC);
} else {
return(netmsg_fwd_port_fn(port, lmsg));
}
}
static int
netmsg_sync_putport(lwkt_port_t port, lwkt_msg_t lmsg)
{
netmsg_base_t nmsg = (void *)lmsg;
KKASSERT((lmsg->ms_flags & MSGF_DONE) == 0);
lmsg->ms_target_port = port;
nmsg->nm_dispatch((netmsg_t)nmsg);
return(EASYNC);
}
static void
netisr_init(void)
{
int i;
if (netisr_ncpus <= 0 || netisr_ncpus > ncpus) {
netisr_ncpus = ncpus;
}
if (netisr_ncpus > NETISR_CPUMAX)
netisr_ncpus = NETISR_CPUMAX;
TAILQ_INIT(&netreglist);
for (i = 0; i < ncpus; ++i) {
struct netisr_data *nd;
nd = (void *)kmem_alloc3(kernel_map, sizeof(*nd),
VM_SUBSYS_GD, KM_CPU(i));
memset(nd, 0, sizeof(*nd));
TAILQ_INIT(&nd->netrulist);
netisr_data[i] = nd;
lwkt_create(netmsg_service_loop, NULL, &netisr_threads[i],
&nd->thread, TDF_NOSTART|TDF_FORCE_SPINPORT|TDF_FIXEDCPU,
i, "netisr %d", i);
netmsg_service_port_init(&netisr_threads[i]->td_msgport);
lwkt_schedule(netisr_threads[i]);
}
lwkt_initport_replyonly(&netisr_afree_rport, netisr_autofree_reply);
lwkt_initport_replyonly(&netisr_afree_free_so_rport,
netisr_autofree_free_so_reply);
lwkt_initport_replyonly_null(&netisr_adone_rport);
lwkt_initport_panic(&netisr_apanic_rport);
lwkt_initport_putonly(&netisr_sync_port, netmsg_sync_putport);
}
SYSINIT(netisr, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST, netisr_init, NULL);
static void
netmsg_service_port_init(lwkt_port_t port)
{
struct netmsg_port_registration *reg;
if (netmsg_fwd_port_fn == NULL)
netmsg_fwd_port_fn = port->mp_putport;
KKASSERT(netmsg_fwd_port_fn == port->mp_putport);
port->mp_putport = netmsg_put_port;
reg = kmalloc(sizeof(*reg), M_TEMP, M_WAITOK|M_ZERO);
reg->npr_port = port;
TAILQ_INSERT_TAIL(&netreglist, reg, npr_entry);
}
void
netmsg_service_sync(void)
{
struct netmsg_port_registration *reg;
struct netmsg_base smsg;
netmsg_init(&smsg, NULL, &curthread->td_msgport, 0, netmsg_sync_handler);
TAILQ_FOREACH(reg, &netreglist, npr_entry) {
lwkt_domsg(reg->npr_port, &smsg.lmsg, 0);
}
}
void
netmsg_sync_handler(netmsg_t msg)
{
lwkt_replymsg(&msg->lmsg, 0);
}
static void
netmsg_service_loop(void *arg)
{
netmsg_base_t msg;
thread_t td = curthread;
int limit;
struct netisr_data *nd = netisr_data[mycpuid];
td->td_type = TD_TYPE_NETISR;
while ((msg = lwkt_waitport(&td->td_msgport, 0))) {
struct netisr_rollup *ru;
limit = netisr_rollup_limit;
do {
KASSERT(msg->nm_dispatch != NULL,
("netmsg_service isr %d badmsg",
msg->lmsg.u.ms_result));
if ((msg->lmsg.ms_flags & MSGF_IGNSOPORT) == 0 &&
msg->nm_so &&
msg->nm_so->so_port != &td->td_msgport) {
#ifdef foo
kprintf("%s: Warning, port changed so=%p\n",
__func__, msg->nm_so);
#endif
lwkt_forwardmsg(msg->nm_so->so_port,
&msg->lmsg);
} else {
#ifdef INVARIANTS
nd->netlastfunc = msg->nm_dispatch;
#endif
msg->nm_dispatch((netmsg_t)msg);
}
if (--limit == 0)
break;
} while ((msg = lwkt_getport(&td->td_msgport)) != NULL);
TAILQ_FOREACH(ru, &nd->netrulist, ru_entry)
ru->ru_func();
}
}
int
netisr_queue(int num, struct mbuf *m)
{
struct netisr *ni;
struct netmsg_packet *pmsg;
lwkt_port_t port;
KASSERT((num > 0 && num <= NELEM(netisrs)),
("Bad isr %d", num));
ni = &netisrs[num];
if (ni->ni_handler == NULL) {
kprintf("%s: Unregistered isr %d\n", __func__, num);
m_freem(m);
return (EIO);
}
if ((m->m_flags & M_HASH) == 0) {
ni->ni_hashfn(&m, 0);
if (m == NULL)
return (EIO);
if ((m->m_flags & M_HASH) == 0) {
kprintf("%s(%d): packet hash failed\n",
__func__, num);
m_freem(m);
return (EIO);
}
}
port = netisr_hashport(m->m_pkthdr.hash);
pmsg = &m->m_hdr.mh_netmsg;
netmsg_init(&pmsg->base, NULL, &netisr_apanic_rport,
0, ni->ni_handler);
pmsg->nm_packet = m;
pmsg->base.lmsg.u.ms_result = num;
lwkt_sendmsg(port, &pmsg->base.lmsg);
return (0);
}
int
netisr_handle(int num, struct mbuf *m)
{
struct netisr *ni;
struct netmsg_packet *pmsg;
lwkt_port_t port;
KASSERT((m->m_flags & M_HASH), ("packet not characterized"));
port = netisr_hashport(m->m_pkthdr.hash);
KASSERT(&curthread->td_msgport == port, ("wrong msgport"));
KASSERT((num > 0 && num <= NELEM(netisrs)), ("bad isr %d", num));
ni = &netisrs[num];
if (ni->ni_handler == NULL) {
kprintf("%s: unregistered isr %d\n", __func__, num);
m_freem(m);
return EIO;
}
pmsg = &m->m_hdr.mh_netmsg;
netmsg_init(&pmsg->base, NULL, &netisr_apanic_rport,
0, ni->ni_handler);
pmsg->nm_packet = m;
pmsg->base.lmsg.u.ms_result = num;
ni->ni_handler((netmsg_t)&pmsg->base);
return 0;
}
void
netisr_characterize(int num, struct mbuf **mp, int hoff)
{
struct netisr *ni;
struct mbuf *m;
m = *mp;
KKASSERT(m != NULL);
if (num < 0 || num >= NETISR_MAX) {
if (num == NETISR_MAX) {
m_sethash(m, 0);
return;
}
panic("Bad isr %d", num);
}
ni = &netisrs[num];
if (ni->ni_handler == NULL) {
kprintf("%s: Unregistered isr %d\n", __func__, num);
m_freem(m);
*mp = NULL;
}
if ((m->m_flags & M_HASH) == 0) {
ni->ni_hashfn(mp, hoff);
m = *mp;
if (m && (m->m_flags & M_HASH) == 0) {
kprintf("%s(%d): packet hash failed\n",
__func__, num);
}
}
}
void
netisr_register(int num, netisr_fn_t handler, netisr_hashfn_t hashfn)
{
struct netisr *ni;
KASSERT((num > 0 && num <= NELEM(netisrs)),
("netisr_register: bad isr %d", num));
KKASSERT(handler != NULL);
if (hashfn == NULL)
hashfn = netisr_hashfn0;
ni = &netisrs[num];
ni->ni_handler = handler;
ni->ni_hashck = netisr_nohashck;
ni->ni_hashfn = hashfn;
netmsg_init(&ni->ni_netmsg, NULL, &netisr_adone_rport, 0, NULL);
}
void
netisr_register_hashcheck(int num, netisr_hashck_t hashck)
{
struct netisr *ni;
KASSERT((num > 0 && num <= NELEM(netisrs)),
("netisr_register: bad isr %d", num));
ni = &netisrs[num];
ni->ni_hashck = hashck;
}
static void
netisr_register_rollup_dispatch(netmsg_t nmsg)
{
struct netmsg_rollup *nm = (struct netmsg_rollup *)nmsg;
int cpuid = mycpuid;
struct netisr_data *nd = netisr_data[cpuid];
struct netisr_rollup *new_ru, *ru;
new_ru = kmalloc(sizeof(*new_ru), M_TEMP, M_WAITOK|M_ZERO);
new_ru->ru_func = nm->func;
new_ru->ru_prio = nm->prio;
TAILQ_FOREACH(ru, &nd->netrulist, ru_entry) {
if (ru->ru_prio < new_ru->ru_prio) {
TAILQ_INSERT_BEFORE(ru, new_ru, ru_entry);
goto done;
}
}
TAILQ_INSERT_TAIL(&nd->netrulist, new_ru, ru_entry);
done:
if (cpuid == 0)
nm->key = new_ru;
KKASSERT(nm->key != NULL);
new_ru->ru_key = nm->key;
netisr_forwardmsg_all(&nm->base, cpuid + 1);
}
struct netisr_rollup *
netisr_register_rollup(netisr_ru_t func, int prio)
{
struct netmsg_rollup nm;
netmsg_init(&nm.base, NULL, &curthread->td_msgport, MSGF_PRIORITY,
netisr_register_rollup_dispatch);
nm.func = func;
nm.prio = prio;
nm.key = NULL;
netisr_domsg_global(&nm.base);
KKASSERT(nm.key != NULL);
return (nm.key);
}
static void
netisr_unregister_rollup_dispatch(netmsg_t nmsg)
{
struct netmsg_rollup *nm = (struct netmsg_rollup *)nmsg;
int cpuid = mycpuid;
struct netisr_data *nd = netisr_data[cpuid];
struct netisr_rollup *ru;
TAILQ_FOREACH(ru, &nd->netrulist, ru_entry) {
if (ru->ru_key == nm->key)
break;
}
if (ru == NULL)
panic("netisr: no rullup for %p", nm->key);
TAILQ_REMOVE(&nd->netrulist, ru, ru_entry);
kfree(ru, M_TEMP);
netisr_forwardmsg_all(&nm->base, cpuid + 1);
}
void
netisr_unregister_rollup(struct netisr_rollup *key)
{
struct netmsg_rollup nm;
netmsg_init(&nm.base, NULL, &curthread->td_msgport, MSGF_PRIORITY,
netisr_unregister_rollup_dispatch);
nm.key = key;
netisr_domsg_global(&nm.base);
}
lwkt_port_t
cpu0_ctlport(int cmd __unused, struct sockaddr *sa __unused,
void *extra __unused, int *cpuid)
{
*cpuid = 0;
return netisr_cpuport(*cpuid);
}
static void
netisr_hashfn0(struct mbuf **mp, int hoff __unused)
{
m_sethash(*mp, 0);
}
static void
schednetisr_remote(void *data)
{
int num = (int)(intptr_t)data;
struct netisr *ni = &netisrs[num];
lwkt_port_t port = &netisr_threads[0]->td_msgport;
netmsg_base_t pmsg;
pmsg = &netisrs[num].ni_netmsg;
if (pmsg->lmsg.ms_flags & MSGF_DONE) {
netmsg_init(pmsg, NULL, &netisr_adone_rport, 0, ni->ni_handler);
pmsg->lmsg.u.ms_result = num;
lwkt_sendmsg(port, &pmsg->lmsg);
}
}
void
schednetisr(int num)
{
KASSERT((num > 0 && num <= NELEM(netisrs)),
("schednetisr: bad isr %d", num));
KKASSERT(netisrs[num].ni_handler != NULL);
if (mycpu->gd_cpuid != 0) {
lwkt_send_ipiq(globaldata_find(0),
schednetisr_remote, (void *)(intptr_t)num);
} else {
crit_enter();
schednetisr_remote((void *)(intptr_t)num);
crit_exit();
}
}
static void
netisr_barrier_dispatch(netmsg_t nmsg)
{
struct netmsg_barrier *msg = (struct netmsg_barrier *)nmsg;
ATOMIC_CPUMASK_NANDBIT(*msg->br_cpumask, mycpu->gd_cpuid);
if (CPUMASK_TESTZERO(*msg->br_cpumask))
wakeup(msg->br_cpumask);
for (;;) {
uint32_t done = msg->br_done;
cpu_ccfence();
if ((done & NETISR_BR_NOTDONE) == 0)
break;
tsleep_interlock(&msg->br_done, 0);
if (atomic_cmpset_int(&msg->br_done,
done, done | NETISR_BR_WAITDONE))
tsleep(&msg->br_done, PINTERLOCKED, "nbrdsp", 0);
}
lwkt_replymsg(&nmsg->lmsg, 0);
}
struct netisr_barrier *
netisr_barrier_create(void)
{
struct netisr_barrier *br;
br = kmalloc(sizeof(*br), M_LWKTMSG, M_WAITOK | M_ZERO);
return br;
}
void
netisr_barrier_set(struct netisr_barrier *br)
{
volatile cpumask_t other_cpumask;
int i, cur_cpuid;
ASSERT_NETISR0;
KKASSERT(!br->br_isset);
other_cpumask = mycpu->gd_other_cpus;
CPUMASK_ANDMASK(other_cpumask, smp_active_mask);
cur_cpuid = mycpuid;
for (i = 0; i < ncpus; ++i) {
struct netmsg_barrier *msg;
if (i == cur_cpuid)
continue;
msg = kmalloc(sizeof(struct netmsg_barrier),
M_LWKTMSG, M_WAITOK);
netmsg_init(&msg->base, NULL, &netisr_afree_rport, 0,
netisr_barrier_dispatch);
msg->br_cpumask = &other_cpumask;
msg->br_done = NETISR_BR_NOTDONE;
KKASSERT(br->br_msgs[i] == NULL);
br->br_msgs[i] = msg;
}
for (i = 0; i < ncpus; ++i) {
if (i == cur_cpuid)
continue;
lwkt_sendmsg(netisr_cpuport(i), &br->br_msgs[i]->base.lmsg);
}
while (CPUMASK_TESTNZERO(other_cpumask)) {
tsleep_interlock(&other_cpumask, 0);
if (CPUMASK_TESTNZERO(other_cpumask))
tsleep(&other_cpumask, PINTERLOCKED, "nbrset", 0);
}
br->br_isset = 1;
}
void
netisr_barrier_rem(struct netisr_barrier *br)
{
int i, cur_cpuid;
ASSERT_NETISR0;
KKASSERT(br->br_isset);
cur_cpuid = mycpuid;
for (i = 0; i < ncpus; ++i) {
struct netmsg_barrier *msg = br->br_msgs[i];
uint32_t done;
msg = br->br_msgs[i];
br->br_msgs[i] = NULL;
if (i == cur_cpuid)
continue;
done = atomic_swap_int(&msg->br_done, 0);
if (done & NETISR_BR_WAITDONE)
wakeup(&msg->br_done);
}
br->br_isset = 0;
}
static void
netisr_nohashck(struct mbuf *m, const struct pktinfo *pi __unused)
{
m->m_flags &= ~M_HASH;
}
void
netisr_hashcheck(int num, struct mbuf *m, const struct pktinfo *pi)
{
struct netisr *ni;
if (num < 0 || num >= NETISR_MAX)
panic("Bad isr %d", num);
ni = &netisrs[num];
if (ni->ni_handler == NULL)
panic("Unregistered isr %d", num);
ni->ni_hashck(m, pi);
}