xref: /minix3/minix/lib/libblockdriver/driver_mt.c (revision 7c48de6cc4c6d56f2277d378dba01dbac8a8c3b9)
1433d6423SLionel Sambuc /* This file contains the multithreaded driver interface.
2433d6423SLionel Sambuc  *
3433d6423SLionel Sambuc  * Changes:
4433d6423SLionel Sambuc  *   Aug 27, 2011   created (A. Welzel)
5433d6423SLionel Sambuc  *
6433d6423SLionel Sambuc  * The entry points into this file are:
7433d6423SLionel Sambuc  *   blockdriver_mt_task:	the main message loop of the driver
8433d6423SLionel Sambuc  *   blockdriver_mt_terminate:	break out of the main message loop
9433d6423SLionel Sambuc  *   blockdriver_mt_sleep:	put the current thread to sleep
10433d6423SLionel Sambuc  *   blockdriver_mt_wakeup:	wake up a sleeping thread
11433d6423SLionel Sambuc  *   blockdriver_mt_set_workers:set the number of worker threads
12433d6423SLionel Sambuc  */
13433d6423SLionel Sambuc 
14433d6423SLionel Sambuc #include <minix/blockdriver_mt.h>
15433d6423SLionel Sambuc #include <minix/mthread.h>
16433d6423SLionel Sambuc #include <assert.h>
17433d6423SLionel Sambuc 
18433d6423SLionel Sambuc #include "const.h"
19433d6423SLionel Sambuc #include "driver.h"
200d6c408fSDavid van Moolenbroek #include "driver_mt.h"
21433d6423SLionel Sambuc #include "mq.h"
22433d6423SLionel Sambuc 
23433d6423SLionel Sambuc /* A thread ID is composed of a device ID and a per-device worker thread ID.
24433d6423SLionel Sambuc  * All thread IDs must be in the range 0..(MAX_THREADS-1) inclusive.
25433d6423SLionel Sambuc  */
26433d6423SLionel Sambuc #define MAKE_TID(did, wid)	((did) * MAX_WORKERS + (wid))
27433d6423SLionel Sambuc #define TID_DEVICE(tid)		((tid) / MAX_WORKERS)
28433d6423SLionel Sambuc #define TID_WORKER(tid)		((tid) % MAX_WORKERS)
29433d6423SLionel Sambuc 
3065f76edbSDavid van Moolenbroek typedef unsigned int worker_id_t;
31433d6423SLionel Sambuc 
32433d6423SLionel Sambuc typedef enum {
33433d6423SLionel Sambuc   STATE_DEAD,
34433d6423SLionel Sambuc   STATE_RUNNING,
35433d6423SLionel Sambuc   STATE_BUSY,
36433d6423SLionel Sambuc   STATE_EXITED
37433d6423SLionel Sambuc } worker_state;
38433d6423SLionel Sambuc 
39433d6423SLionel Sambuc /* Structure with information about a worker thread. */
40433d6423SLionel Sambuc typedef struct {
41433d6423SLionel Sambuc   device_id_t device_id;
42433d6423SLionel Sambuc   worker_id_t worker_id;
43433d6423SLionel Sambuc   worker_state state;
44433d6423SLionel Sambuc   mthread_thread_t mthread;
45433d6423SLionel Sambuc   mthread_event_t sleep_event;
46433d6423SLionel Sambuc } worker_t;
47433d6423SLionel Sambuc 
48433d6423SLionel Sambuc /* Structure with information about a device. */
49433d6423SLionel Sambuc typedef struct {
50433d6423SLionel Sambuc   device_id_t id;
51433d6423SLionel Sambuc   unsigned int workers;
52433d6423SLionel Sambuc   worker_t worker[MAX_WORKERS];
53433d6423SLionel Sambuc   mthread_event_t queue_event;
54433d6423SLionel Sambuc   mthread_rwlock_t barrier;
55433d6423SLionel Sambuc } device_t;
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc static struct blockdriver *bdtab;
58433d6423SLionel Sambuc static int running = FALSE;
59433d6423SLionel Sambuc 
60433d6423SLionel Sambuc static mthread_key_t worker_key;
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc static device_t device[MAX_DEVICES];
63433d6423SLionel Sambuc 
64433d6423SLionel Sambuc static worker_t *exited[MAX_THREADS];
65433d6423SLionel Sambuc static int num_exited = 0;
66433d6423SLionel Sambuc 
67433d6423SLionel Sambuc /*===========================================================================*
68433d6423SLionel Sambuc  *				enqueue					     *
69433d6423SLionel Sambuc  *===========================================================================*/
enqueue(device_t * dp,const message * m_src,int ipc_status)70433d6423SLionel Sambuc static void enqueue(device_t *dp, const message *m_src, int ipc_status)
71433d6423SLionel Sambuc {
72433d6423SLionel Sambuc /* Enqueue a message into the device's queue, and signal the event.
73433d6423SLionel Sambuc  * Must be called from the master thread.
74433d6423SLionel Sambuc  */
75433d6423SLionel Sambuc 
76433d6423SLionel Sambuc   if (!mq_enqueue(dp->id, m_src, ipc_status))
77433d6423SLionel Sambuc 	panic("blockdriver_mt: enqueue failed (message queue full)");
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc   mthread_event_fire(&dp->queue_event);
80433d6423SLionel Sambuc }
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc /*===========================================================================*
83433d6423SLionel Sambuc  *				try_dequeue				     *
84433d6423SLionel Sambuc  *===========================================================================*/
try_dequeue(device_t * dp,message * m_dst,int * ipc_status)85433d6423SLionel Sambuc static int try_dequeue(device_t *dp, message *m_dst, int *ipc_status)
86433d6423SLionel Sambuc {
87433d6423SLionel Sambuc /* See if a message can be dequeued from the current worker thread's device
88433d6423SLionel Sambuc  * queue. If so, dequeue the message and return TRUE. If not, return FALSE.
89433d6423SLionel Sambuc  * Must be called from a worker thread. Does not block.
90433d6423SLionel Sambuc  */
91433d6423SLionel Sambuc 
92433d6423SLionel Sambuc   return mq_dequeue(dp->id, m_dst, ipc_status);
93433d6423SLionel Sambuc }
94433d6423SLionel Sambuc 
95433d6423SLionel Sambuc /*===========================================================================*
96433d6423SLionel Sambuc  *				dequeue					     *
97433d6423SLionel Sambuc  *===========================================================================*/
dequeue(device_t * dp,worker_t * wp,message * m_dst,int * ipc_status)98433d6423SLionel Sambuc static int dequeue(device_t *dp, worker_t *wp, message *m_dst,
99433d6423SLionel Sambuc   int *ipc_status)
100433d6423SLionel Sambuc {
101433d6423SLionel Sambuc /* Dequeue a message from the current worker thread's device queue. Block the
102433d6423SLionel Sambuc  * current thread if necessary. Must be called from a worker thread. Either
103433d6423SLionel Sambuc  * succeeds with a message (TRUE) or indicates that the thread should be
104433d6423SLionel Sambuc  * terminated (FALSE).
105433d6423SLionel Sambuc  */
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc   do {
108433d6423SLionel Sambuc 	mthread_event_wait(&dp->queue_event);
109433d6423SLionel Sambuc 
110433d6423SLionel Sambuc 	/* If we were woken up as a result of terminate or set_workers, break
111433d6423SLionel Sambuc 	 * out of the loop and terminate the thread.
112433d6423SLionel Sambuc 	 */
113433d6423SLionel Sambuc 	if (!running || wp->worker_id >= dp->workers)
114433d6423SLionel Sambuc 		return FALSE;
115433d6423SLionel Sambuc   } while (!try_dequeue(dp, m_dst, ipc_status));
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc   return TRUE;
118433d6423SLionel Sambuc }
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc /*===========================================================================*
121433d6423SLionel Sambuc  *				is_transfer_req				     *
122433d6423SLionel Sambuc  *===========================================================================*/
is_transfer_req(int type)123433d6423SLionel Sambuc static int is_transfer_req(int type)
124433d6423SLionel Sambuc {
125433d6423SLionel Sambuc /* Return whether the given block device request is a transfer request.
126433d6423SLionel Sambuc  */
127433d6423SLionel Sambuc 
128433d6423SLionel Sambuc   switch (type) {
129433d6423SLionel Sambuc   case BDEV_READ:
130433d6423SLionel Sambuc   case BDEV_WRITE:
131433d6423SLionel Sambuc   case BDEV_GATHER:
132433d6423SLionel Sambuc   case BDEV_SCATTER:
133433d6423SLionel Sambuc 	return TRUE;
134433d6423SLionel Sambuc 
135433d6423SLionel Sambuc   default:
136433d6423SLionel Sambuc 	return FALSE;
137433d6423SLionel Sambuc   }
138433d6423SLionel Sambuc }
139433d6423SLionel Sambuc 
140433d6423SLionel Sambuc /*===========================================================================*
141433d6423SLionel Sambuc  *				worker_thread				     *
142433d6423SLionel Sambuc  *===========================================================================*/
worker_thread(void * param)143433d6423SLionel Sambuc static void *worker_thread(void *param)
144433d6423SLionel Sambuc {
145433d6423SLionel Sambuc /* The worker thread loop. Set up the thread-specific reference to itself and
146433d6423SLionel Sambuc  * start looping. The loop consists of blocking dequeing and handling messages.
147433d6423SLionel Sambuc  * After handling a message, the thread might have been stopped, so we check
148433d6423SLionel Sambuc  * for this condition and exit if so.
149433d6423SLionel Sambuc  */
150433d6423SLionel Sambuc   worker_t *wp;
151433d6423SLionel Sambuc   device_t *dp;
152433d6423SLionel Sambuc   thread_id_t tid;
153433d6423SLionel Sambuc   message m;
15465f76edbSDavid van Moolenbroek   int ipc_status;
155433d6423SLionel Sambuc 
156433d6423SLionel Sambuc   wp = (worker_t *) param;
157433d6423SLionel Sambuc   assert(wp != NULL);
158433d6423SLionel Sambuc   dp = &device[wp->device_id];
159433d6423SLionel Sambuc   tid = MAKE_TID(wp->device_id, wp->worker_id);
160433d6423SLionel Sambuc 
161433d6423SLionel Sambuc   if (mthread_setspecific(worker_key, wp))
162433d6423SLionel Sambuc 	panic("blockdriver_mt: could not save local thread pointer");
163433d6423SLionel Sambuc 
164433d6423SLionel Sambuc   while (running && wp->worker_id < dp->workers) {
165433d6423SLionel Sambuc 
166433d6423SLionel Sambuc 	/* See if a new message is available right away. */
167433d6423SLionel Sambuc 	if (!try_dequeue(dp, &m, &ipc_status)) {
168433d6423SLionel Sambuc 
169433d6423SLionel Sambuc 		/* If not, block waiting for a new message or a thread
170433d6423SLionel Sambuc 		 * termination event.
171433d6423SLionel Sambuc 		 */
172433d6423SLionel Sambuc 		if (!dequeue(dp, wp, &m, &ipc_status))
173433d6423SLionel Sambuc 			break;
174433d6423SLionel Sambuc 	}
175433d6423SLionel Sambuc 
176433d6423SLionel Sambuc 	/* Even if the thread was stopped before, a new message resumes it. */
177433d6423SLionel Sambuc 	wp->state = STATE_BUSY;
178433d6423SLionel Sambuc 
179433d6423SLionel Sambuc 	/* If the request is a transfer request, we acquire the read barrier
180433d6423SLionel Sambuc 	 * lock. Otherwise, we acquire the write lock.
181433d6423SLionel Sambuc 	 */
182433d6423SLionel Sambuc 	if (is_transfer_req(m.m_type))
183433d6423SLionel Sambuc 		mthread_rwlock_rdlock(&dp->barrier);
184433d6423SLionel Sambuc 	else
185433d6423SLionel Sambuc 		mthread_rwlock_wrlock(&dp->barrier);
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc 	/* Handle the request and send a reply. */
188433d6423SLionel Sambuc 	blockdriver_process_on_thread(bdtab, &m, ipc_status, tid);
189433d6423SLionel Sambuc 
190433d6423SLionel Sambuc 	/* Switch the thread back to running state, and unlock the barrier. */
191433d6423SLionel Sambuc 	wp->state = STATE_RUNNING;
192433d6423SLionel Sambuc 	mthread_rwlock_unlock(&dp->barrier);
193433d6423SLionel Sambuc   }
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc   /* Clean up and terminate this thread. */
196433d6423SLionel Sambuc   if (mthread_setspecific(worker_key, NULL))
197433d6423SLionel Sambuc 	panic("blockdriver_mt: could not delete local thread pointer");
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc   wp->state = STATE_EXITED;
200433d6423SLionel Sambuc 
201433d6423SLionel Sambuc   exited[num_exited++] = wp;
202433d6423SLionel Sambuc 
203433d6423SLionel Sambuc   return NULL;
204433d6423SLionel Sambuc }
205433d6423SLionel Sambuc 
206433d6423SLionel Sambuc /*===========================================================================*
207433d6423SLionel Sambuc  *				master_create_worker			     *
208433d6423SLionel Sambuc  *===========================================================================*/
master_create_worker(worker_t * wp,worker_id_t worker_id,device_id_t device_id)209433d6423SLionel Sambuc static void master_create_worker(worker_t *wp, worker_id_t worker_id,
210433d6423SLionel Sambuc   device_id_t device_id)
211433d6423SLionel Sambuc {
212433d6423SLionel Sambuc /* Start a new worker thread.
213433d6423SLionel Sambuc  */
214433d6423SLionel Sambuc   mthread_attr_t attr;
215433d6423SLionel Sambuc   int r;
216433d6423SLionel Sambuc 
217433d6423SLionel Sambuc   wp->device_id = device_id;
218433d6423SLionel Sambuc   wp->worker_id = worker_id;
219433d6423SLionel Sambuc   wp->state = STATE_RUNNING;
220433d6423SLionel Sambuc 
221433d6423SLionel Sambuc   /* Initialize synchronization primitives. */
222433d6423SLionel Sambuc   mthread_event_init(&wp->sleep_event);
223433d6423SLionel Sambuc 
224433d6423SLionel Sambuc   r = mthread_attr_init(&attr);
225433d6423SLionel Sambuc   if (r != 0)
226433d6423SLionel Sambuc 	panic("blockdriver_mt: could not initialize attributes (%d)", r);
227433d6423SLionel Sambuc 
228433d6423SLionel Sambuc   r = mthread_attr_setstacksize(&attr, STACK_SIZE);
229433d6423SLionel Sambuc   if (r != 0)
230433d6423SLionel Sambuc 	panic("blockdriver_mt: could not set stack size (%d)", r);
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc   r = mthread_create(&wp->mthread, &attr, worker_thread, (void *) wp);
233433d6423SLionel Sambuc   if (r != 0)
234433d6423SLionel Sambuc 	panic("blockdriver_mt: could not start thread %d (%d)", worker_id, r);
235433d6423SLionel Sambuc 
236433d6423SLionel Sambuc   mthread_attr_destroy(&attr);
237433d6423SLionel Sambuc }
238433d6423SLionel Sambuc 
239433d6423SLionel Sambuc /*===========================================================================*
240433d6423SLionel Sambuc  *				master_destroy_worker			     *
241433d6423SLionel Sambuc  *===========================================================================*/
master_destroy_worker(worker_t * wp)242433d6423SLionel Sambuc static void master_destroy_worker(worker_t *wp)
243433d6423SLionel Sambuc {
244433d6423SLionel Sambuc /* Clean up resources used by an exited worker thread.
245433d6423SLionel Sambuc  */
246433d6423SLionel Sambuc 
247433d6423SLionel Sambuc   assert(wp != NULL);
248433d6423SLionel Sambuc   assert(wp->state == STATE_EXITED);
249433d6423SLionel Sambuc 
250433d6423SLionel Sambuc   /* Join the thread. */
251433d6423SLionel Sambuc   if (mthread_join(wp->mthread, NULL))
252433d6423SLionel Sambuc 	panic("blockdriver_mt: could not join thread %d", wp->worker_id);
253433d6423SLionel Sambuc 
254433d6423SLionel Sambuc   /* Destroy resources. */
255433d6423SLionel Sambuc   mthread_event_destroy(&wp->sleep_event);
256433d6423SLionel Sambuc 
257433d6423SLionel Sambuc   wp->state = STATE_DEAD;
258433d6423SLionel Sambuc }
259433d6423SLionel Sambuc 
260433d6423SLionel Sambuc /*===========================================================================*
261433d6423SLionel Sambuc  *				master_handle_exits			     *
262433d6423SLionel Sambuc  *===========================================================================*/
master_handle_exits(void)263433d6423SLionel Sambuc static void master_handle_exits(void)
264433d6423SLionel Sambuc {
265433d6423SLionel Sambuc /* Destroy the remains of all exited threads.
266433d6423SLionel Sambuc  */
267433d6423SLionel Sambuc   int i;
268433d6423SLionel Sambuc 
269433d6423SLionel Sambuc   for (i = 0; i < num_exited; i++)
270433d6423SLionel Sambuc 	master_destroy_worker(exited[i]);
271433d6423SLionel Sambuc 
272433d6423SLionel Sambuc   num_exited = 0;
273433d6423SLionel Sambuc }
274433d6423SLionel Sambuc 
275433d6423SLionel Sambuc /*===========================================================================*
2760d6c408fSDavid van Moolenbroek  *				master_yield				     *
2770d6c408fSDavid van Moolenbroek  *===========================================================================*/
master_yield(void)2780d6c408fSDavid van Moolenbroek static void master_yield(void)
2790d6c408fSDavid van Moolenbroek {
2800d6c408fSDavid van Moolenbroek /* Let worker threads run, and clean up any exited threads.
2810d6c408fSDavid van Moolenbroek  */
2820d6c408fSDavid van Moolenbroek 
2830d6c408fSDavid van Moolenbroek   mthread_yield_all();
2840d6c408fSDavid van Moolenbroek 
2850d6c408fSDavid van Moolenbroek   if (num_exited > 0)
2860d6c408fSDavid van Moolenbroek 	master_handle_exits();
2870d6c408fSDavid van Moolenbroek }
2880d6c408fSDavid van Moolenbroek 
2890d6c408fSDavid van Moolenbroek /*===========================================================================*
290433d6423SLionel Sambuc  *				master_handle_message			     *
291433d6423SLionel Sambuc  *===========================================================================*/
master_handle_message(message * m_ptr,int ipc_status)292433d6423SLionel Sambuc static void master_handle_message(message *m_ptr, int ipc_status)
293433d6423SLionel Sambuc {
294433d6423SLionel Sambuc /* For real request messages, query the device ID, start a thread if none is
295433d6423SLionel Sambuc  * free and the maximum number of threads for that device has not yet been
296433d6423SLionel Sambuc  * reached, and enqueue the message in the devices's message queue. All other
297433d6423SLionel Sambuc  * messages are handled immediately from the main thread.
298433d6423SLionel Sambuc  */
299433d6423SLionel Sambuc   device_id_t id;
300433d6423SLionel Sambuc   worker_t *wp;
301433d6423SLionel Sambuc   device_t *dp;
30265f76edbSDavid van Moolenbroek   unsigned int wid;
30365f76edbSDavid van Moolenbroek   int r;
304433d6423SLionel Sambuc 
305433d6423SLionel Sambuc   /* If this is not a block driver request, we cannot get the minor device
306433d6423SLionel Sambuc    * associated with it, and thus we can not tell which thread should process
307433d6423SLionel Sambuc    * it either. In that case, the master thread has to handle it instead.
308433d6423SLionel Sambuc    */
309433d6423SLionel Sambuc   if (is_ipc_notify(ipc_status) || !IS_BDEV_RQ(m_ptr->m_type)) {
310433d6423SLionel Sambuc 	/* Process as 'other' message. */
311433d6423SLionel Sambuc 	blockdriver_process_on_thread(bdtab, m_ptr, ipc_status, MAIN_THREAD);
312433d6423SLionel Sambuc 
313433d6423SLionel Sambuc 	return;
314433d6423SLionel Sambuc   }
315433d6423SLionel Sambuc 
316433d6423SLionel Sambuc   /* Query the device ID. Upon failure, send the error code to the caller. */
317433d6423SLionel Sambuc   r = (*bdtab->bdr_device)(m_ptr->m_lbdev_lblockdriver_msg.minor, &id);
318433d6423SLionel Sambuc   if (r != OK) {
319433d6423SLionel Sambuc 	blockdriver_reply(m_ptr, ipc_status, r);
320433d6423SLionel Sambuc 
321433d6423SLionel Sambuc 	return;
322433d6423SLionel Sambuc   }
323433d6423SLionel Sambuc 
324433d6423SLionel Sambuc   /* Look up the device control block. */
325433d6423SLionel Sambuc   assert(id >= 0 && id < MAX_DEVICES);
326433d6423SLionel Sambuc   dp = &device[id];
327433d6423SLionel Sambuc 
328433d6423SLionel Sambuc   /* Find the first non-busy worker thread. */
329433d6423SLionel Sambuc   for (wid = 0; wid < dp->workers; wid++)
330433d6423SLionel Sambuc 	if (dp->worker[wid].state != STATE_BUSY)
331433d6423SLionel Sambuc 		break;
332433d6423SLionel Sambuc 
333433d6423SLionel Sambuc   /* If the worker thread is dead, start a thread now, unless we have already
334433d6423SLionel Sambuc    * reached the maximum number of threads.
335433d6423SLionel Sambuc    */
336433d6423SLionel Sambuc   if (wid < dp->workers) {
337433d6423SLionel Sambuc 	wp = &dp->worker[wid];
338433d6423SLionel Sambuc 
339433d6423SLionel Sambuc 	assert(wp->state != STATE_EXITED);
340433d6423SLionel Sambuc 
341433d6423SLionel Sambuc 	/* If the non-busy thread has not yet been created, create one now. */
342433d6423SLionel Sambuc 	if (wp->state == STATE_DEAD)
343433d6423SLionel Sambuc 		master_create_worker(wp, wid, dp->id);
344433d6423SLionel Sambuc   }
345433d6423SLionel Sambuc 
346433d6423SLionel Sambuc   /* Enqueue the message at the device queue. */
347433d6423SLionel Sambuc   enqueue(dp, m_ptr, ipc_status);
348433d6423SLionel Sambuc }
349433d6423SLionel Sambuc 
350433d6423SLionel Sambuc /*===========================================================================*
351433d6423SLionel Sambuc  *				master_init				     *
352433d6423SLionel Sambuc  *===========================================================================*/
master_init(struct blockdriver * bdp)353433d6423SLionel Sambuc static void master_init(struct blockdriver *bdp)
354433d6423SLionel Sambuc {
355433d6423SLionel Sambuc /* Initialize the state of the master thread.
356433d6423SLionel Sambuc  */
357433d6423SLionel Sambuc   int i, j;
358433d6423SLionel Sambuc 
359433d6423SLionel Sambuc   assert(bdp != NULL);
360433d6423SLionel Sambuc   assert(bdp->bdr_device != NULL);
361433d6423SLionel Sambuc 
362433d6423SLionel Sambuc   bdtab = bdp;
363433d6423SLionel Sambuc 
364433d6423SLionel Sambuc   /* Initialize device-specific data structures. */
365433d6423SLionel Sambuc   for (i = 0; i < MAX_DEVICES; i++) {
366433d6423SLionel Sambuc 	device[i].id = i;
367433d6423SLionel Sambuc 	device[i].workers = 1;
368433d6423SLionel Sambuc 	mthread_event_init(&device[i].queue_event);
369433d6423SLionel Sambuc 	mthread_rwlock_init(&device[i].barrier);
370433d6423SLionel Sambuc 
371433d6423SLionel Sambuc 	for (j = 0; j < MAX_WORKERS; j++)
372433d6423SLionel Sambuc 		device[i].worker[j].state = STATE_DEAD;
373433d6423SLionel Sambuc   }
374433d6423SLionel Sambuc 
375433d6423SLionel Sambuc   /* Initialize a per-thread key, where each worker thread stores its own
376433d6423SLionel Sambuc    * reference to the worker structure.
377433d6423SLionel Sambuc    */
378433d6423SLionel Sambuc   if (mthread_key_create(&worker_key, NULL))
379433d6423SLionel Sambuc 	panic("blockdriver_mt: error initializing worker key");
380433d6423SLionel Sambuc }
381433d6423SLionel Sambuc 
382433d6423SLionel Sambuc /*===========================================================================*
383433d6423SLionel Sambuc  *				blockdriver_mt_get_tid			     *
384433d6423SLionel Sambuc  *===========================================================================*/
blockdriver_mt_get_tid(void)385433d6423SLionel Sambuc thread_id_t blockdriver_mt_get_tid(void)
386433d6423SLionel Sambuc {
387433d6423SLionel Sambuc /* Return back the ID of this thread.
388433d6423SLionel Sambuc  */
389433d6423SLionel Sambuc   worker_t *wp;
390433d6423SLionel Sambuc 
391433d6423SLionel Sambuc   wp = (worker_t *) mthread_getspecific(worker_key);
392433d6423SLionel Sambuc 
393433d6423SLionel Sambuc   if (wp == NULL)
394433d6423SLionel Sambuc 	panic("blockdriver_mt: master thread cannot query thread ID\n");
395433d6423SLionel Sambuc 
396433d6423SLionel Sambuc   return MAKE_TID(wp->device_id, wp->worker_id);
397433d6423SLionel Sambuc }
398433d6423SLionel Sambuc 
399433d6423SLionel Sambuc /*===========================================================================*
400433d6423SLionel Sambuc  *				blockdriver_mt_receive			     *
401433d6423SLionel Sambuc  *===========================================================================*/
blockdriver_mt_receive(message * m_ptr,int * ipc_status)402433d6423SLionel Sambuc static void blockdriver_mt_receive(message *m_ptr, int *ipc_status)
403433d6423SLionel Sambuc {
404433d6423SLionel Sambuc /* Receive a message.
405433d6423SLionel Sambuc  */
406433d6423SLionel Sambuc   int r;
407433d6423SLionel Sambuc 
408433d6423SLionel Sambuc   r = sef_receive_status(ANY, m_ptr, ipc_status);
409433d6423SLionel Sambuc 
410433d6423SLionel Sambuc   if (r != OK)
411433d6423SLionel Sambuc 	panic("blockdriver_mt: sef_receive_status() returned %d", r);
412433d6423SLionel Sambuc }
413433d6423SLionel Sambuc 
414433d6423SLionel Sambuc /*===========================================================================*
415433d6423SLionel Sambuc  *				blockdriver_mt_task			     *
416433d6423SLionel Sambuc  *===========================================================================*/
blockdriver_mt_task(struct blockdriver * driver_tab)417433d6423SLionel Sambuc void blockdriver_mt_task(struct blockdriver *driver_tab)
418433d6423SLionel Sambuc {
419433d6423SLionel Sambuc /* The multithreaded driver task.
420433d6423SLionel Sambuc  */
421433d6423SLionel Sambuc   int ipc_status, i;
422433d6423SLionel Sambuc   message mess;
423433d6423SLionel Sambuc 
424433d6423SLionel Sambuc   /* Initialize first if necessary. */
425433d6423SLionel Sambuc   if (!running) {
426433d6423SLionel Sambuc 	master_init(driver_tab);
427433d6423SLionel Sambuc 
428433d6423SLionel Sambuc 	running = TRUE;
429433d6423SLionel Sambuc   }
430433d6423SLionel Sambuc 
431433d6423SLionel Sambuc   /* The main message loop. */
432433d6423SLionel Sambuc   while (running) {
433433d6423SLionel Sambuc 	/* Receive a message. */
434433d6423SLionel Sambuc 	blockdriver_mt_receive(&mess, &ipc_status);
435433d6423SLionel Sambuc 
436433d6423SLionel Sambuc 	/* Dispatch the message. */
437433d6423SLionel Sambuc 	master_handle_message(&mess, ipc_status);
438433d6423SLionel Sambuc 
4390d6c408fSDavid van Moolenbroek 	/* Let worker threads run. */
4400d6c408fSDavid van Moolenbroek 	master_yield();
441433d6423SLionel Sambuc   }
442433d6423SLionel Sambuc 
443433d6423SLionel Sambuc   /* Free up resources. */
444433d6423SLionel Sambuc   for (i = 0; i < MAX_DEVICES; i++)
445433d6423SLionel Sambuc 	mthread_event_destroy(&device[i].queue_event);
446433d6423SLionel Sambuc }
447433d6423SLionel Sambuc 
448433d6423SLionel Sambuc /*===========================================================================*
449433d6423SLionel Sambuc  *				blockdriver_mt_terminate		     *
450433d6423SLionel Sambuc  *===========================================================================*/
blockdriver_mt_terminate(void)451433d6423SLionel Sambuc void blockdriver_mt_terminate(void)
452433d6423SLionel Sambuc {
453433d6423SLionel Sambuc /* Instruct libblockdriver to shut down.
454433d6423SLionel Sambuc  */
455433d6423SLionel Sambuc 
456433d6423SLionel Sambuc   running = FALSE;
457433d6423SLionel Sambuc }
458433d6423SLionel Sambuc 
459433d6423SLionel Sambuc /*===========================================================================*
460433d6423SLionel Sambuc  *				blockdriver_mt_sleep			     *
461433d6423SLionel Sambuc  *===========================================================================*/
blockdriver_mt_sleep(void)462433d6423SLionel Sambuc void blockdriver_mt_sleep(void)
463433d6423SLionel Sambuc {
464433d6423SLionel Sambuc /* Let the current thread sleep until it gets woken up by the master thread.
465433d6423SLionel Sambuc  */
466433d6423SLionel Sambuc   worker_t *wp;
467433d6423SLionel Sambuc 
468433d6423SLionel Sambuc   wp = (worker_t *) mthread_getspecific(worker_key);
469433d6423SLionel Sambuc 
470433d6423SLionel Sambuc   if (wp == NULL)
471433d6423SLionel Sambuc 	panic("blockdriver_mt: master thread cannot sleep");
472433d6423SLionel Sambuc 
473433d6423SLionel Sambuc   mthread_event_wait(&wp->sleep_event);
474433d6423SLionel Sambuc }
475433d6423SLionel Sambuc 
476433d6423SLionel Sambuc /*===========================================================================*
477433d6423SLionel Sambuc  *				blockdriver_mt_wakeup			     *
478433d6423SLionel Sambuc  *===========================================================================*/
blockdriver_mt_wakeup(thread_id_t id)479433d6423SLionel Sambuc void blockdriver_mt_wakeup(thread_id_t id)
480433d6423SLionel Sambuc {
481433d6423SLionel Sambuc /* Wake up a sleeping worker thread from the master thread.
482433d6423SLionel Sambuc  */
483433d6423SLionel Sambuc   worker_t *wp;
484433d6423SLionel Sambuc   device_id_t device_id;
485433d6423SLionel Sambuc   worker_id_t worker_id;
486433d6423SLionel Sambuc 
487433d6423SLionel Sambuc   device_id = TID_DEVICE(id);
488433d6423SLionel Sambuc   worker_id = TID_WORKER(id);
489433d6423SLionel Sambuc 
490433d6423SLionel Sambuc   assert(device_id >= 0 && device_id < MAX_DEVICES);
491*7c48de6cSDavid van Moolenbroek   assert(worker_id < MAX_WORKERS);
492433d6423SLionel Sambuc 
493433d6423SLionel Sambuc   wp = &device[device_id].worker[worker_id];
494433d6423SLionel Sambuc 
495433d6423SLionel Sambuc   assert(wp->state == STATE_RUNNING || wp->state == STATE_BUSY);
496433d6423SLionel Sambuc 
497433d6423SLionel Sambuc   mthread_event_fire(&wp->sleep_event);
498433d6423SLionel Sambuc }
499433d6423SLionel Sambuc 
500433d6423SLionel Sambuc /*===========================================================================*
501433d6423SLionel Sambuc  *				blockdriver_mt_set_workers		     *
502433d6423SLionel Sambuc  *===========================================================================*/
blockdriver_mt_set_workers(device_id_t id,unsigned int workers)50365f76edbSDavid van Moolenbroek void blockdriver_mt_set_workers(device_id_t id, unsigned int workers)
504433d6423SLionel Sambuc {
505433d6423SLionel Sambuc /* Set the number of worker threads for the given device.
506433d6423SLionel Sambuc  */
507433d6423SLionel Sambuc   device_t *dp;
508433d6423SLionel Sambuc 
509433d6423SLionel Sambuc   assert(id >= 0 && id < MAX_DEVICES);
510433d6423SLionel Sambuc 
511433d6423SLionel Sambuc   if (workers > MAX_WORKERS)
512433d6423SLionel Sambuc 	workers = MAX_WORKERS;
513433d6423SLionel Sambuc 
514433d6423SLionel Sambuc   dp = &device[id];
515433d6423SLionel Sambuc 
516433d6423SLionel Sambuc   /* If we are cleaning up, wake up all threads waiting on a queue event. */
517433d6423SLionel Sambuc   if (workers == 1 && dp->workers > workers)
518433d6423SLionel Sambuc 	mthread_event_fire_all(&dp->queue_event);
519433d6423SLionel Sambuc 
520433d6423SLionel Sambuc   dp->workers = workers;
521433d6423SLionel Sambuc }
5220d6c408fSDavid van Moolenbroek 
5230d6c408fSDavid van Moolenbroek /*===========================================================================*
5240d6c408fSDavid van Moolenbroek  *				blockdriver_mt_is_idle			     *
5250d6c408fSDavid van Moolenbroek  *===========================================================================*/
blockdriver_mt_is_idle(void)5260d6c408fSDavid van Moolenbroek int blockdriver_mt_is_idle(void)
5270d6c408fSDavid van Moolenbroek {
5280d6c408fSDavid van Moolenbroek /* Return whether the block driver is idle. This means that it has no enqueued
5290d6c408fSDavid van Moolenbroek  * requests and no busy worker threads. Used for live update functionality.
5300d6c408fSDavid van Moolenbroek  */
5310d6c408fSDavid van Moolenbroek   unsigned int did, wid;
5320d6c408fSDavid van Moolenbroek 
5330d6c408fSDavid van Moolenbroek   for (did = 0; did < MAX_DEVICES; did++) {
5340d6c408fSDavid van Moolenbroek 	if (!mq_isempty(did))
5350d6c408fSDavid van Moolenbroek 		return FALSE;
5360d6c408fSDavid van Moolenbroek 
5370d6c408fSDavid van Moolenbroek 	for (wid = 0; wid < device[did].workers; wid++)
5380d6c408fSDavid van Moolenbroek 		if (device[did].worker[wid].state == STATE_BUSY)
5390d6c408fSDavid van Moolenbroek 			return FALSE;
5400d6c408fSDavid van Moolenbroek   }
5410d6c408fSDavid van Moolenbroek 
5420d6c408fSDavid van Moolenbroek   return TRUE;
5430d6c408fSDavid van Moolenbroek }
5440d6c408fSDavid van Moolenbroek 
5450d6c408fSDavid van Moolenbroek /*===========================================================================*
5460d6c408fSDavid van Moolenbroek  *				blockdriver_mt_suspend			     *
5470d6c408fSDavid van Moolenbroek  *===========================================================================*/
blockdriver_mt_suspend(void)5480d6c408fSDavid van Moolenbroek void blockdriver_mt_suspend(void)
5490d6c408fSDavid van Moolenbroek {
5500d6c408fSDavid van Moolenbroek /* Suspend the driver operation in order to facilitate a live update.
5510d6c408fSDavid van Moolenbroek  * Suspension involves shutting down all worker threads, because transferring
5520d6c408fSDavid van Moolenbroek  * thread stacks is currently not supported by the state transfer framework.
5530d6c408fSDavid van Moolenbroek  */
5540d6c408fSDavid van Moolenbroek   unsigned int did;
5550d6c408fSDavid van Moolenbroek 
5560d6c408fSDavid van Moolenbroek   assert(running);
5570d6c408fSDavid van Moolenbroek   assert(blockdriver_mt_is_idle());
5580d6c408fSDavid van Moolenbroek 
5590d6c408fSDavid van Moolenbroek   /* We terminate the worker threads by simulating a driver shutdown. */
5600d6c408fSDavid van Moolenbroek   running = FALSE;
5610d6c408fSDavid van Moolenbroek 
5620d6c408fSDavid van Moolenbroek   for (did = 0; did < MAX_DEVICES; did++)
5630d6c408fSDavid van Moolenbroek 	mthread_event_fire_all(&device[did].queue_event);
5640d6c408fSDavid van Moolenbroek 
5650d6c408fSDavid van Moolenbroek   master_yield();
5660d6c408fSDavid van Moolenbroek }
5670d6c408fSDavid van Moolenbroek 
5680d6c408fSDavid van Moolenbroek /*===========================================================================*
5690d6c408fSDavid van Moolenbroek  *				blockdriver_mt_resume			     *
5700d6c408fSDavid van Moolenbroek  *===========================================================================*/
blockdriver_mt_resume(void)5710d6c408fSDavid van Moolenbroek void blockdriver_mt_resume(void)
5720d6c408fSDavid van Moolenbroek {
5730d6c408fSDavid van Moolenbroek /* Resume regular operation after a (successful or failed) live update. We do
5740d6c408fSDavid van Moolenbroek  * not recreate worker threads; instead, they are recreated lazily as new
5750d6c408fSDavid van Moolenbroek  * requests come in.
5760d6c408fSDavid van Moolenbroek  */
5770d6c408fSDavid van Moolenbroek 
5780d6c408fSDavid van Moolenbroek   assert(!running);
5790d6c408fSDavid van Moolenbroek 
5800d6c408fSDavid van Moolenbroek   running = TRUE;
5810d6c408fSDavid van Moolenbroek }
582