tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: workqueues ....



> Date: Thu, 26 Jul 2018 22:57:57 -0700
> From: Phil Nelson <phil%NetBSD.org@localhost>
> 
>     I'm trying to work with workqueues and am having a locking problem

Is this a conceptual problem, or do you have a symptom that you're
actually hitting with specific code?  If the latter, can you describe
the symptom and quote the code?

>     Lets say I have a function f() as follows:
> 
> int  f() {
>            mutex_enter(&some_mutex);
>            ...... code .....
>            mutex_exit(&some_mutex);
> }
> 
> and now lets say that I start another function running via a workqueue, g()
> g() {
>       ..... some code ....
>       if (f()) {
>           .... do something else ...
>       } else {
>           error
>       }
> }
> 
> So, my question is:  if g() is running f() and holds the mutex and then the
> main code calls f() ... will this be detected as already holding the lock?
> 
> If it will be detected as already holding the lock,  how can I do locking
> between the code that does the enqueue and the code in the work item?

The workqueue function runs asynchronously in its own thread.  You are
allowed to hold a lock when scheduling work with workqueue_enqueue
that the workqueue function itself will acquire too.  Indeed, many
users will do exactly that.

Note that in your example, the caller of f must not hold some_mutex,
workqueues or not.  If g _already_ holds the mutex when it calls f,
that's a bug.

Here's how you can use a workqueue to schedule work if it's not
already scheduled:

struct foo_softc {
	...
	struct workqueue *sc_wq;
	kmutex_t sc_work_lock;
	struct work sc_work;
	bool sc_work_scheduled;
	...
};

static void
do_work(struct work *wk, void *arg)
{
	struct foo_softc *sc = container_of(wk, struct foo_softc, sc_work);

	mutex_enter(&sc->sc_work_lock);
	sc->sc_work_scheduled = false;
	mutex_exit(&sc->sc_work_lock);

	/* do stuff */
}


static void
foo_intr(...)
{
	...
	mutex_enter(&sc->sc_work_lock);
	if (!sc->sc_work_scheduled) {
		workqueue_enqueue(sc->sc_wq, &sc->sc_work, NULL);
		sc->sc_work_scheduled = true;
	}
	mutex_exit(&sc->sc_work_lock);
	...
}


Home | Main Index | Thread Index | Old Index