libblockdriver: suspend threads for live update

Same as for VFS.

Change-Id: I0f09d43f24c32361af5e5658923140c79244d3d1
This commit is contained in:
David van Moolenbroek 2015-08-25 08:36:39 +02:00
parent 728b0e5b34
commit 0d6c408f48
9 changed files with 204 additions and 10 deletions

View file

@ -2378,6 +2378,9 @@ static void sef_local_startup(void)
/* Register signal callbacks. */
sef_setcb_signal_handler(sef_cb_signal_handler);
/* Enable support for live update. */
blockdriver_mt_support_lu();
/* Let SEF perform startup. */
sef_startup();
}

View file

@ -733,6 +733,9 @@ sef_local_startup(void)
sef_setcb_init_fresh(sef_cb_init_fresh);
sef_setcb_signal_handler(sef_cb_signal_handler);
/* Enable suppor for live update. */
blockdriver_mt_support_lu();
sef_startup();
}

View file

@ -13,4 +13,6 @@ void blockdriver_mt_terminate(void);
void blockdriver_mt_set_workers(device_id_t id, unsigned int workers);
thread_id_t blockdriver_mt_get_tid(void);
void blockdriver_mt_support_lu(void);
#endif /* _MINIX_BLOCKDRIVER_MT_H */

View file

@ -7,6 +7,6 @@ CPPFLAGS+= -D_MINIX_SYSTEM
LIB= blockdriver
SRCS= driver.c drvlib.c driver_st.c driver_mt.c mq.c trace.c
SRCS= driver.c drvlib.c driver_st.c driver_mt.c liveupdate.c mq.c trace.c
.include <bsd.lib.mk>

View file

@ -17,6 +17,7 @@
#include "const.h"
#include "driver.h"
#include "driver_mt.h"
#include "mq.h"
/* A thread ID is composed of a device ID and a per-device worker thread ID.
@ -271,6 +272,20 @@ static void master_handle_exits(void)
num_exited = 0;
}
/*===========================================================================*
* master_yield *
*===========================================================================*/
static void master_yield(void)
{
/* Let worker threads run, and clean up any exited threads.
*/
mthread_yield_all();
if (num_exited > 0)
master_handle_exits();
}
/*===========================================================================*
* master_handle_message *
*===========================================================================*/
@ -421,12 +436,8 @@ void blockdriver_mt_task(struct blockdriver *driver_tab)
/* Dispatch the message. */
master_handle_message(&mess, ipc_status);
/* Let other threads run. */
mthread_yield_all();
/* Clean up any exited threads. */
if (num_exited > 0)
master_handle_exits();
/* Let worker threads run. */
master_yield();
}
/* Free up resources. */
@ -508,3 +519,63 @@ void blockdriver_mt_set_workers(device_id_t id, unsigned int workers)
dp->workers = workers;
}
/*===========================================================================*
* blockdriver_mt_is_idle *
*===========================================================================*/
int blockdriver_mt_is_idle(void)
{
/* Return whether the block driver is idle. This means that it has no enqueued
* requests and no busy worker threads. Used for live update functionality.
*/
unsigned int did, wid;
for (did = 0; did < MAX_DEVICES; did++) {
if (!mq_isempty(did))
return FALSE;
for (wid = 0; wid < device[did].workers; wid++)
if (device[did].worker[wid].state == STATE_BUSY)
return FALSE;
}
return TRUE;
}
/*===========================================================================*
* blockdriver_mt_suspend *
*===========================================================================*/
void blockdriver_mt_suspend(void)
{
/* Suspend the driver operation in order to facilitate a live update.
* Suspension involves shutting down all worker threads, because transferring
* thread stacks is currently not supported by the state transfer framework.
*/
unsigned int did;
assert(running);
assert(blockdriver_mt_is_idle());
/* We terminate the worker threads by simulating a driver shutdown. */
running = FALSE;
for (did = 0; did < MAX_DEVICES; did++)
mthread_event_fire_all(&device[did].queue_event);
master_yield();
}
/*===========================================================================*
* blockdriver_mt_resume *
*===========================================================================*/
void blockdriver_mt_resume(void)
{
/* Resume regular operation after a (successful or failed) live update. We do
* not recreate worker threads; instead, they are recreated lazily as new
* requests come in.
*/
assert(!running);
running = TRUE;
}

View file

@ -0,0 +1,9 @@
#ifndef _BLOCKDRIVER_DRIVER_MT_H
#define _BLOCKDRIVER_DRIVER_MT_H
/* These functions are used for live update. */
int blockdriver_mt_is_idle(void);
void blockdriver_mt_suspend(void);
void blockdriver_mt_resume(void);
#endif /* !_BLOCKDRIVER_DRIVER_MT_H */

View file

@ -0,0 +1,94 @@
/* This file implements SEF hooks for live update of multithreaded block
* drivers.
*/
#include <minix/drivers.h>
#include <minix/blockdriver_mt.h>
#include "driver_mt.h"
/*===========================================================================*
* sef_cb_lu_prepare *
*===========================================================================*/
static int sef_cb_lu_prepare(int state)
{
/* This function is called to decide whether we can enter the given live
* update state, and to prepare for such an update. If we are requested to
* update to a request-free or protocol-free state, make sure there is no work
* pending or being processed, and shut down all worker threads.
*/
switch (state) {
case SEF_LU_STATE_REQUEST_FREE:
case SEF_LU_STATE_PROTOCOL_FREE:
if (!blockdriver_mt_is_idle()) {
printf("libblockdriver(%d): not idle, blocking update\n",
sef_self());
break;
}
blockdriver_mt_suspend();
return OK;
}
return ENOTREADY;
}
/*===========================================================================*
* sef_cb_lu_state_changed *
*===========================================================================*/
static void sef_cb_lu_state_changed(int old_state, int state)
{
/* This function is called in the old driver instance when the state changes.
* We use it to resume normal operation after a failed live update.
*/
if (state != SEF_LU_STATE_NULL)
return;
switch (old_state) {
case SEF_LU_STATE_REQUEST_FREE:
case SEF_LU_STATE_PROTOCOL_FREE:
blockdriver_mt_resume();
}
}
/*===========================================================================*
* sef_cb_init_lu *
*===========================================================================*/
static int sef_cb_init_lu(int type, sef_init_info_t *info)
{
/* This function is called in the new driver instance during a live update.
*/
int r;
/* Perform regular state transfer. */
if ((r = SEF_CB_INIT_LU_DEFAULT(type, info)) != OK)
return r;
/* Recreate worker threads, if necessary. */
switch (info->prepare_state) {
case SEF_LU_STATE_REQUEST_FREE:
case SEF_LU_STATE_PROTOCOL_FREE:
blockdriver_mt_resume();
}
return OK;
}
/*===========================================================================*
* blockdriver_mt_support_lu *
*===========================================================================*/
void blockdriver_mt_support_lu(void)
{
/* Enable suppor for live update of this driver. To be called before
* sef_startup().
*/
/* Register live update callbacks. */
sef_setcb_init_lu(sef_cb_init_lu);
sef_setcb_lu_prepare(sef_cb_lu_prepare);
sef_setcb_lu_state_changed(sef_cb_lu_state_changed);
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
}

View file

@ -70,6 +70,19 @@ int mq_enqueue(device_id_t device_id, const message *mess,
return TRUE;
}
/*===========================================================================*
* mq_isempty *
*===========================================================================*/
int mq_isempty(device_id_t device_id)
{
/* Return whether the message queue for the given device is empty.
*/
assert(device_id >= 0 && device_id < MAX_DEVICES);
return STAILQ_EMPTY(&queue[device_id]);
}
/*===========================================================================*
* mq_dequeue *
*===========================================================================*/
@ -80,9 +93,7 @@ int mq_dequeue(device_id_t device_id, message *mess, int *ipc_status)
*/
struct mq_cell *cell;
assert(device_id >= 0 && device_id < MAX_DEVICES);
if (STAILQ_EMPTY(&queue[device_id]))
if (mq_isempty(device_id))
return FALSE;
cell = STAILQ_FIRST(&queue[device_id]);

View file

@ -5,5 +5,6 @@ void mq_init(void);
int mq_enqueue(device_id_t device_id, const message *mess, int
ipc_status);
int mq_dequeue(device_id_t device_id, message *mess, int *ipc_status);
int mq_isempty(device_id_t device_id);
#endif /* _BLOCKDRIVER_MQ_H */