diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/compat/ndis/kern_ndis.c | 336 | ||||
-rw-r--r-- | sys/compat/ndis/ndis_var.h | 4 | ||||
-rw-r--r-- | sys/compat/ndis/subr_ndis.c | 23 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndis.c | 64 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndisvar.h | 3 |
5 files changed, 374 insertions, 56 deletions
diff --git a/sys/compat/ndis/kern_ndis.c b/sys/compat/ndis/kern_ndis.c index d94d25c..acb3dca 100644 --- a/sys/compat/ndis/kern_ndis.c +++ b/sys/compat/ndis/kern_ndis.c @@ -34,20 +34,22 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/systm.h> +#include <sys/unistd.h> #include <sys/types.h> #include <sys/errno.h> #include <sys/callout.h> #include <sys/socket.h> #include <sys/queue.h> #include <sys/sysctl.h> -#include <sys/systm.h> +#include <sys/proc.h> #include <sys/malloc.h> #include <sys/lock.h> #include <sys/mutex.h> #include <sys/conf.h> -#include <sys/taskqueue.h> #include <sys/kernel.h> +#include <sys/kthread.h> #include <machine/bus.h> #include <machine/resource.h> #include <sys/bus.h> @@ -85,7 +87,30 @@ __stdcall static void ndis_getdone_func(ndis_handle, ndis_status); __stdcall static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t); __stdcall static void ndis_sendrsrcavail_func(ndis_handle); +static int ndis_create_kthreads(void); +static void ndis_destroy_kthreads(void); +static void ndis_stop_thread(int); +static int ndis_enlarge_thrqueue(int); +static int ndis_shrink_thrqueue(int); +static void ndis_runq(void *); +static void ndis_intq(void *); + +extern struct mtx_pool *ndis_mtxpool; static uma_zone_t ndis_packet_zone, ndis_buffer_zone; +struct mtx *ndis_thr_mtx; +static STAILQ_HEAD(ndisqhead, ndis_req) ndis_ttodo; +struct ndisqhead ndis_itodo; +struct ndisqhead ndis_free; +static int ndis_jobs = 32; +static struct proc *ndis_tproc; +static struct proc *ndis_iproc; + +struct ndis_req { + void (*ndis_func)(void *); + void *ndis_arg; + int ndis_exit; + STAILQ_ENTRY(ndis_req) link; +}; /* * This allows us to export our symbols to other modules. @@ -111,9 +136,15 @@ ndis_modevent(module_t mod, int cmd, void *arg) ndis_buffer_zone = uma_zcreate("NDIS buffer", sizeof(ndis_buffer), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + + ndis_create_kthreads(); + break; case MOD_UNLOAD: case MOD_SHUTDOWN: + /* stop kthreads */ + ndis_destroy_kthreads(); + /* Shut down subsystems */ ndis_libfini(); ntoskrnl_libfini(); @@ -132,6 +163,303 @@ ndis_modevent(module_t mod, int cmd, void *arg) DEV_MODULE(ndisapi, ndis_modevent, NULL); MODULE_VERSION(ndisapi, 1); +/* + * We create two kthreads for the NDIS subsystem. One of them is a task + * queue for performing various odd jobs. The other is an swi thread + * reserved exclusively for running interrupt handlers. The reason we + * have our own task queue is that there are some cases where we may + * need to sleep for a significant amount of time, and if we were to + * use one of the taskqueue threads, we might delay the processing + * of other pending tasks which might need to run right away. We have + * a separate swi thread because we don't want our interrupt handling + * to be delayed either. + * + * By default there are 32 jobs available to start, and another 8 + * are added to the free list each time a new device is created. + */ + +static void +ndis_runq(dummy) + void *dummy; +{ + struct ndis_req *r = NULL, *die = NULL; + + while (1) { + kthread_suspend(ndis_tproc, 0); + + /* Look for any jobs on the work queue. */ + + mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + while(STAILQ_FIRST(&ndis_ttodo) != NULL) { + r = STAILQ_FIRST(&ndis_ttodo); + STAILQ_REMOVE_HEAD(&ndis_ttodo, link); + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + + /* Do the work. */ + + if (r->ndis_func != NULL) + (*r->ndis_func)(r->ndis_arg); + + mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + STAILQ_INSERT_HEAD(&ndis_free, r, link); + + /* Check for a shutdown request */ + + if (r->ndis_exit == TRUE) + die = r; + } + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + + /* Bail if we were told to shut down. */ + + if (die != NULL) + break; + } + + wakeup(die); + mtx_lock(&Giant); + kthread_exit(0); +} + +static void +ndis_intq(dummy) + void *dummy; +{ + struct ndis_req *r = NULL, *die = NULL; + + while (1) { + kthread_suspend(ndis_iproc, 0); + + /* Look for any jobs on the work queue. */ + + mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + while(STAILQ_FIRST(&ndis_itodo) != NULL) { + r = STAILQ_FIRST(&ndis_itodo); + STAILQ_REMOVE_HEAD(&ndis_itodo, link); + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + + /* Do the work. */ + + if (r->ndis_func != NULL) + (*r->ndis_func)(r->ndis_arg); + + mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + STAILQ_INSERT_HEAD(&ndis_free, r, link); + + /* Check for a shutdown request */ + + if (r->ndis_exit == TRUE) + die = r; + } + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + + /* Bail if this is an exit request. */ + + if (die != NULL) + break; + } + + wakeup(die); + mtx_lock(&Giant); + kthread_exit(0); +} + +static int +ndis_create_kthreads() +{ + struct ndis_req *r; + int i, error = 0; + + ndis_thr_mtx = mtx_pool_alloc(ndis_mtxpool); + STAILQ_INIT(&ndis_ttodo); + STAILQ_INIT(&ndis_itodo); + STAILQ_INIT(&ndis_free); + + for (i = 0; i < ndis_jobs; i++) { + r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK); + if (r == NULL) { + error = ENOMEM; + break; + } + STAILQ_INSERT_HEAD(&ndis_free, r, link); + } + + if (error == 0) + error = kthread_create(ndis_runq, NULL, + &ndis_tproc, RFHIGHPID, 0, "ndis taskqueue"); + + if (error == 0) + error = kthread_create(ndis_intq, NULL, + &ndis_iproc, RFHIGHPID, 0, "ndis swi"); + + if (error) { + while ((r = STAILQ_FIRST(&ndis_free)) != NULL) { + STAILQ_REMOVE_HEAD(&ndis_free, link); + free(r, M_DEVBUF); + } + return(error); + } + + return(0); +} + +static void +ndis_destroy_kthreads() +{ + struct ndis_req *r; + + /* Stop the threads. */ + + ndis_stop_thread(NDIS_TASKQUEUE); + ndis_stop_thread(NDIS_SWI); + + /* Destroy request structures. */ + + while ((r = STAILQ_FIRST(&ndis_free)) != NULL) { + STAILQ_REMOVE_HEAD(&ndis_free, link); + free(r, M_DEVBUF); + } + + return; +} + +static void +ndis_stop_thread(t) + int t; +{ + struct ndis_req *r; + struct timeval tv; + struct ndisqhead *q; + struct proc *p; + + if (t == NDIS_TASKQUEUE) { + q = &ndis_ttodo; + p = ndis_tproc; + } else { + q = &ndis_itodo; + p = ndis_iproc; + } + + /* Create and post a special 'exit' job. */ + + mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + r = STAILQ_FIRST(&ndis_free); + STAILQ_REMOVE_HEAD(&ndis_free, link); + r->ndis_func = NULL; + r->ndis_arg = NULL; + r->ndis_exit = TRUE; + STAILQ_INSERT_TAIL(q, r, link); + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + + kthread_resume(p); + + /* wait for thread exit */ + + tv.tv_sec = 60; + tv.tv_usec = 0; + tsleep(r, PPAUSE|PCATCH, "ndisthrexit", tvtohz(&tv)); + + /* Now empty the job list. */ + + mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + while ((r = STAILQ_FIRST(q)) != NULL) { + STAILQ_REMOVE_HEAD(q, link); + STAILQ_INSERT_HEAD(&ndis_free, r, link); + } + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + + return; +} + +static int +ndis_enlarge_thrqueue(cnt) + int cnt; +{ + struct ndis_req *r; + int i; + + for (i = 0; i < cnt; i++) { + r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK); + if (r == NULL) + return(ENOMEM); + mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + STAILQ_INSERT_HEAD(&ndis_free, r, link); + ndis_jobs++; + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + } + + return(0); +} + +static int +ndis_shrink_thrqueue(cnt) + int cnt; +{ + struct ndis_req *r; + int i; + + for (i = 0; i < cnt; i++) { + mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + r = STAILQ_FIRST(&ndis_free); + if (r == NULL) { + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + return(ENOMEM); + } + STAILQ_REMOVE_HEAD(&ndis_free, link); + ndis_jobs--; + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + free(r, M_DEVBUF); + } + + return(0); +} + +int +ndis_sched(func, arg, t) + void (*func)(void *); + void *arg; + int t; +{ + struct ndis_req *r; + struct ndisqhead *q; + struct proc *p; + + if (t == NDIS_TASKQUEUE) { + q = &ndis_ttodo; + p = ndis_tproc; + } else { + q = &ndis_itodo; + p = ndis_iproc; + } + + mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + /* + * Check to see if an instance of this job is already + * pending. If so, don't bother queuing it again. + */ + STAILQ_FOREACH(r, q, link) { + if (r->ndis_func == func && r->ndis_arg == arg) { + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + return(0); + } + } + r = STAILQ_FIRST(&ndis_free); + if (r == NULL) { + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + return(EAGAIN); + } + STAILQ_REMOVE_HEAD(&ndis_free, link); + r->ndis_func = func; + r->ndis_arg = arg; + r->ndis_exit = FALSE; + STAILQ_INSERT_TAIL(q, r, link); + mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); + + /* Post the job. */ + kthread_resume(p); + + return(0); +} __stdcall static void ndis_sendrsrcavail_func(adapter) @@ -1080,6 +1408,8 @@ ndis_unload_driver(arg) ndis_flush_sysctls(sc); + ndis_shrink_thrqueue(8); + return(0); } @@ -1186,5 +1516,7 @@ ndis_load_driver(img, arg) block->nmb_dev = sc->ndis_dev; block->nmb_img = img; + ndis_enlarge_thrqueue(8); + return(0); } diff --git a/sys/compat/ndis/ndis_var.h b/sys/compat/ndis/ndis_var.h index 27b8942..f4303ac 100644 --- a/sys/compat/ndis/ndis_var.h +++ b/sys/compat/ndis/ndis_var.h @@ -1321,6 +1321,9 @@ typedef ndis_status (*driver_entry)(void *, ndis_unicode_string *); extern image_patch_table ndis_functbl[]; +#define NDIS_TASKQUEUE 1 +#define NDIS_SWI 2 + __BEGIN_DECLS extern int ndis_libinit(void); extern int ndis_libfini(void); @@ -1353,6 +1356,7 @@ extern int ndis_destroy_dma(void *); extern int ndis_create_sysctls(void *); extern int ndis_add_sysctl(void *, char *, char *, char *, int); extern int ndis_flush_sysctls(void *); +extern int ndis_sched(void (*)(void *), void *, int); __END_DECLS #endif /* _NDIS_VAR_H_ */ diff --git a/sys/compat/ndis/subr_ndis.c b/sys/compat/ndis/subr_ndis.c index 9b210a0..da5cf89 100644 --- a/sys/compat/ndis/subr_ndis.c +++ b/sys/compat/ndis/subr_ndis.c @@ -64,7 +64,6 @@ __FBSDID("$FreeBSD$"); #include <sys/timespec.h> #include <sys/smp.h> #include <sys/queue.h> -#include <sys/taskqueue.h> #include <sys/proc.h> #include <sys/namei.h> #include <sys/fcntl.h> @@ -261,7 +260,7 @@ __stdcall static u_int8_t ndis_cpu_cnt(void); __stdcall static void ndis_ind_statusdone(ndis_handle); __stdcall static void ndis_ind_status(ndis_handle, ndis_status, void *, uint32_t); -static void ndis_workfunc(void *, int); +static void ndis_workfunc(void *); __stdcall static ndis_status ndis_sched_workitem(ndis_work_item *); __stdcall static void ndis_pkt_to_pkt(ndis_packet *, uint32_t, uint32_t, ndis_packet *, uint32_t, uint32_t *); @@ -601,18 +600,18 @@ ndis_decode_parm(block, parm, val) ndis_config_parm *parm; char *val; { - uint16_t *unicode; ndis_unicode_string *ustr; - - unicode = (uint16_t *)&block->nmb_dummybuf; + char *astr = NULL; switch(parm->ncp_type) { case ndis_parm_string: ustr = &parm->ncp_parmdata.ncp_stringdata; - ndis_unicode_to_ascii(ustr->nus_buf, ustr->nus_len, &val); + ndis_unicode_to_ascii(ustr->nus_buf, ustr->nus_len, &astr); + bcopy(astr, val, 254); + free(astr, M_DEVBUF); break; case ndis_parm_int: - sprintf(val, "%ul", parm->ncp_parmdata.ncp_intdata); + sprintf(val, "%d", parm->ncp_parmdata.ncp_intdata); break; case ndis_parm_hexint: sprintf(val, "%xu", parm->ncp_parmdata.ncp_intdata); @@ -644,6 +643,7 @@ ndis_write_cfg(status, cfg, key, parm) ndis_unicode_to_ascii(key->nus_buf, key->nus_len, &keystr); /* Decode the parameter into a string. */ + bzero(val, sizeof(val)); *status = ndis_decode_parm(block, parm, val); if (*status != NDIS_STATUS_SUCCESS) { free(keystr, M_DEVBUF); @@ -2484,9 +2484,8 @@ ndis_ind_status(adapter, status, sbuf, slen) } static void -ndis_workfunc(ctx, pending) +ndis_workfunc(ctx) void *ctx; - int pending; { ndis_work_item *work; __stdcall ndis_proc workfunc; @@ -2501,11 +2500,7 @@ __stdcall static ndis_status ndis_sched_workitem(work) ndis_work_item *work; { - struct task *t; - - t = (struct task *)&work->nwi_wraprsvd; - TASK_INIT(t, 0, ndis_workfunc, work); - taskqueue_enqueue(taskqueue_swi, t); + ndis_sched(ndis_workfunc, work, NDIS_TASKQUEUE); return(NDIS_STATUS_SUCCESS); } diff --git a/sys/dev/if_ndis/if_ndis.c b/sys/dev/if_ndis/if_ndis.c index e4650e0..814f4a8 100644 --- a/sys/dev/if_ndis/if_ndis.c +++ b/sys/dev/if_ndis/if_ndis.c @@ -44,7 +44,6 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <sys/queue.h> #include <sys/sysctl.h> -#include <sys/taskqueue.h> #include <net/if.h> #include <net/if_arp.h> @@ -106,11 +105,11 @@ static __stdcall void ndis_linksts (ndis_handle, static __stdcall void ndis_linksts_done (ndis_handle); static void ndis_intr (void *); -static void ndis_intrtask (void *, int); +static void ndis_intrtask (void *); static void ndis_tick (void *); -static void ndis_ticktask (void *, int); +static void ndis_ticktask (void *); static void ndis_start (struct ifnet *); -static void ndis_starttask (void *, int); +static void ndis_starttask (void *); static int ndis_ioctl (struct ifnet *, u_long, caddr_t); static int ndis_wi_ioctl_get (struct ifnet *, u_long, caddr_t); static int ndis_wi_ioctl_set (struct ifnet *, u_long, caddr_t); @@ -239,10 +238,6 @@ ndis_attach(dev) sc->ndis_mtx = mtx_pool_alloc(ndis_mtxpool); sc->ndis_intrmtx = mtx_pool_alloc(ndis_mtxpool); - TASK_INIT(&sc->ndis_intrtask, 0, ndis_intrtask, sc); - TASK_INIT(&sc->ndis_ticktask, 0, ndis_ticktask, sc); - TASK_INIT(&sc->ndis_starttask, 0, ndis_starttask, sc); - /* * Map control/status registers. @@ -825,20 +820,25 @@ ndis_txeof(adapter, packet, status) m = packet->np_m0; idx = packet->np_txidx; - ifp->if_opackets++; if (sc->ndis_sc) bus_dmamap_unload(sc->ndis_ttag, sc->ndis_tmaps[idx]); ndis_free_packet(packet); + m_freem(m); + + NDIS_LOCK(sc); sc->ndis_txarray[idx] = NULL; sc->ndis_txpending++; + if (status == NDIS_STATUS_SUCCESS) + ifp->if_opackets++; + else + ifp->if_oerrors++; ifp->if_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; + NDIS_UNLOCK(sc); - m_freem(m); - - taskqueue_enqueue(taskqueue_swi, &sc->ndis_starttask); + ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE); return; } @@ -875,13 +875,13 @@ ndis_linksts_done(adapter) switch (block->nmb_getstat) { case NDIS_STATUS_MEDIA_CONNECT: - taskqueue_enqueue(taskqueue_swi, &sc->ndis_ticktask); - taskqueue_enqueue(taskqueue_swi, &sc->ndis_starttask); + ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE); + ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE); break; case NDIS_STATUS_MEDIA_DISCONNECT: if (sc->ndis_80211) ndis_getstate_80211(sc); - taskqueue_enqueue(taskqueue_swi, &sc->ndis_ticktask); + ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE); break; default: break; @@ -891,9 +891,8 @@ ndis_linksts_done(adapter) } static void -ndis_intrtask(arg, pending) +ndis_intrtask(arg) void *arg; - int pending; { struct ndis_softc *sc; struct ifnet *ifp; @@ -901,16 +900,11 @@ ndis_intrtask(arg, pending) sc = arg; ifp = &sc->arpcom.ac_if; - NDIS_LOCK(sc); ndis_intrhand(sc); - NDIS_UNLOCK(sc); mtx_lock(sc->ndis_intrmtx); ndis_enable_intr(sc); mtx_unlock(sc->ndis_intrmtx); - if (ifp->if_snd.ifq_head != NULL) - ndis_start(ifp); - return; } @@ -940,7 +934,7 @@ ndis_intr(arg) mtx_unlock(sc->ndis_intrmtx); if ((is_our_intr || call_isr) && (ifp->if_flags & IFF_UP)) - taskqueue_enqueue(taskqueue_swi, &sc->ndis_intrtask); + ndis_sched(ndis_intrtask, ifp, NDIS_SWI); return; } @@ -951,16 +945,16 @@ ndis_tick(xsc) { struct ndis_softc *sc; sc = xsc; - taskqueue_enqueue(taskqueue_swi, &sc->ndis_ticktask); + + ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE); sc->ndis_stat_ch = timeout(ndis_tick, sc, hz * sc->ndis_block.nmb_checkforhangsecs); } static void -ndis_ticktask(xsc, pending) +ndis_ticktask(xsc) void *xsc; - int pending; { struct ndis_softc *sc; __stdcall ndis_checkforhang_handler hangfunc; @@ -1029,9 +1023,8 @@ ndis_map_sclist(arg, segs, nseg, mapsize, error) } static void -ndis_starttask(arg, pending) +ndis_starttask(arg) void *arg; - int pending; { struct ifnet *ifp; @@ -1875,12 +1868,10 @@ ndis_watchdog(ifp) NDIS_LOCK(sc); ifp->if_oerrors++; device_printf(sc->ndis_dev, "watchdog timeout\n"); + NDIS_UNLOCK(sc); ndis_reset(sc); - - taskqueue_enqueue(taskqueue_swi, &sc->ndis_starttask); - - NDIS_UNLOCK(sc); + ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE); return; } @@ -1895,17 +1886,16 @@ ndis_stop(sc) { struct ifnet *ifp; -/* NDIS_LOCK(sc);*/ ifp = &sc->arpcom.ac_if; - ifp->if_timer = 0; - - sc->ndis_link = 0; untimeout(ndis_tick, sc, sc->ndis_stat_ch); ndis_halt_nic(sc); + NDIS_LOCK(sc); + ifp->if_timer = 0; + sc->ndis_link = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - /*NDIS_UNLOCK(sc);*/ + NDIS_UNLOCK(sc); return; } diff --git a/sys/dev/if_ndis/if_ndisvar.h b/sys/dev/if_ndis/if_ndisvar.h index 45736f0..b202127 100644 --- a/sys/dev/if_ndis/if_ndisvar.h +++ b/sys/dev/if_ndis/if_ndisvar.h @@ -92,9 +92,6 @@ struct ndis_softc { int ndis_rescnt; struct mtx *ndis_mtx; struct mtx *ndis_intrmtx; - struct task ndis_intrtask; - struct task ndis_ticktask; - struct task ndis_starttask; device_t ndis_dev; int ndis_unit; ndis_miniport_block ndis_block; |