From 134164f8d3353179d7c6956d27938a81f19586ef Mon Sep 17 00:00:00 2001 From: Bill Paul Date: Sat, 14 Feb 2004 20:57:32 +0000 Subject: [PATCH] Fix a problem with the way we schedule work on the NDIS worker threads. The Am1771 driver will sometimes do the following: - Some thread-> NdisScheduleWorkItem(some work) - Worker thread -> do some work, KeWaitForSingleObject(some event) - Some other thread -> NdisScheduleWorkItem(some other work) When the second call to NdisScheduleWorkItem() occurs, the NDIS worker thread (in our case ndis taskqueue) is suspended in KeWaitForSingleObject() and waiting for an event to be signaled. This is different from when the worker thread is idle and waiting on NdisScheduleWorkItem() to send it more jobs. However, the ndis_sched() function in kern_ndis.c always calls kthread_resume() when queueing a new job. Normally this would be ok, but here this causes KeWaitForSingleObject() to return prematurely, which is not what we want. To fix this, the NDIS threads created by kern_ndis.c maintain a state variable to indicate whether they are running (scanning the job list and executing jobs) or sleeping (blocked on kthread_suspend() in ndis_runq()), and ndis_sched() will only call kthread_resume() if the thread is in the sleeping state. Note that we can't just check to see if the thread is on the run queue: in both cases, the thread is sleeping, but it's sleeping for different reasons. This stops the Am1771 driver from emitting various "NDIS ERROR" messages and fixes some cases where it crashes. --- sys/compat/ndis/kern_ndis.c | 21 +++++++++++++++++++-- sys/compat/ndis/ndis_var.h | 3 +++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/sys/compat/ndis/kern_ndis.c b/sys/compat/ndis/kern_ndis.c index 028ca20f8df3..7d1fb9852e5d 100644 --- a/sys/compat/ndis/kern_ndis.c +++ b/sys/compat/ndis/kern_ndis.c @@ -99,6 +99,7 @@ struct ndis_req { struct ndisproc { struct ndisqhead *np_q; struct proc *np_p; + int np_state; }; static int ndis_create_kthreads(void); @@ -214,6 +215,7 @@ ndis_runq(arg) /* Look for any jobs on the work queue. */ mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); + p->np_state = NDIS_PSTATE_RUNNING; while(STAILQ_FIRST(p->np_q) != NULL) { r = STAILQ_FIRST(p->np_q); STAILQ_REMOVE_HEAD(p->np_q, link); @@ -232,6 +234,7 @@ ndis_runq(arg) if (r->nr_exit == TRUE) die = r; } + p->np_state = NDIS_PSTATE_SLEEPING; mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); /* Bail if we were told to shut down. */ @@ -267,12 +270,14 @@ ndis_create_kthreads() if (error == 0) { ndis_tproc.np_q = &ndis_ttodo; + ndis_tproc.np_state = NDIS_PSTATE_SLEEPING; error = kthread_create(ndis_runq, &ndis_tproc, &ndis_tproc.np_p, RFHIGHPID, 0, "ndis taskqueue"); } if (error == 0) { ndis_iproc.np_q = &ndis_itodo; + ndis_iproc.np_state = NDIS_PSTATE_SLEEPING; error = kthread_create(ndis_runq, &ndis_iproc, &ndis_iproc.np_p, RFHIGHPID, 0, "ndis swi"); } @@ -408,6 +413,7 @@ ndis_sched(func, arg, t) struct ndis_req *r; struct ndisqhead *q; struct proc *p; + int s; if (t == NDIS_TASKQUEUE) { q = &ndis_ttodo; @@ -438,10 +444,21 @@ ndis_sched(func, arg, t) r->nr_arg = arg; r->nr_exit = FALSE; STAILQ_INSERT_TAIL(q, r, link); + if (t == NDIS_TASKQUEUE) + s = ndis_tproc.np_state; + else + s = ndis_iproc.np_state; mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); - /* Post the job. */ - kthread_resume(p); + /* + * Post the job, but only if the thread is actually blocked + * on its own suspend call. If a driver queues up a job with + * NdisScheduleWorkItem() which happens to do a KeWaitForObject(), + * it may suspend there, and in that case we don't want to wake + * it up until KeWaitForObject() gets woken up on its own. + */ + if (s == NDIS_PSTATE_SLEEPING) + kthread_resume(p); return(0); } diff --git a/sys/compat/ndis/ndis_var.h b/sys/compat/ndis/ndis_var.h index 58f174fceb72..4068816b8a4a 100644 --- a/sys/compat/ndis/ndis_var.h +++ b/sys/compat/ndis/ndis_var.h @@ -1475,6 +1475,9 @@ extern image_patch_table ndis_functbl[]; #define NDIS_TASKQUEUE 1 #define NDIS_SWI 2 +#define NDIS_PSTATE_RUNNING 1 +#define NDIS_PSTATE_SLEEPING 2 + __BEGIN_DECLS extern int ndis_libinit(void); extern int ndis_libfini(void);