Same as for VFS.
Change-Id: I0f09d43f24c32361af5e5658923140c79244d3d1
/* 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();
}
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();
}
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 */
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>
#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.
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 *
*===========================================================================*/
/* 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. */
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;
+}
--- /dev/null
+#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 */
--- /dev/null
+/* 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);
+}
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 *
*===========================================================================*/
*/
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]);
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 */