Split block/character protocols and libdriver
This patch separates the character and block driver communication protocols. The old character protocol remains the same, but a new block protocol is introduced. The libdriver library is replaced by two new libraries: libchardriver and libblockdriver. Their exposed API, and drivers that use them, have been updated accordingly. Together, libbdev and libblockdriver now completely abstract away the message format used by the block protocol. As the memory driver is both a character and a block device driver, it now implements its own message loop. The most important semantic change made to the block protocol is that it is no longer possible to return both partial results and an error for a single transfer. This simplifies the interaction between the caller and the driver, as the I/O vector no longer needs to be copied back. Also, drivers are now no longer supposed to decide based on the layout of the I/O vector when a transfer should be cut short. Put simply, transfers are now supposed to either succeed completely, or result in an error. After this patch, the state of the various pieces is as follows: - block protocol: stable - libbdev API: stable for synchronous communication - libblockdriver API: needs slight revision (the drvlib/partition API in particular; the threading API will also change shortly) - character protocol: needs cleanup - libchardriver API: needs cleanup accordingly - driver restarts: largely unsupported until endpoint changes are reintroduced As a side effect, this patch eliminates several bugs, hacks, and gcc -Wall and -W warnings all over the place. It probably introduces a few new ones, too. Update warning: this patch changes the protocol between MFS and disk drivers, so in order to use old/new images, the MFS from the ramdisk must be used to mount all file systems.
This commit is contained in:
12
lib/libblockdriver/Makefile
Normal file
12
lib/libblockdriver/Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
# Makefile for libblockdriver
|
||||
.include <bsd.own.mk>
|
||||
|
||||
LIB= blockdriver
|
||||
|
||||
SRCS= driver.c drvlib.c driver_st.c driver_mt.c mq.c event.c
|
||||
|
||||
.if ${USE_STATECTL} != "no"
|
||||
CPPFLAGS+= -DUSE_STATECTL
|
||||
.endif
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
382
lib/libblockdriver/driver.c
Normal file
382
lib/libblockdriver/driver.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/* This file contains the device independent block driver interface.
|
||||
*
|
||||
* Block drivers support the following requests. Message format m10 is used.
|
||||
* Field names are prefixed with BDEV_. Separate field names are used for the
|
||||
* "access" and "request" fields.
|
||||
*
|
||||
* m_type MINOR COUNT GRANT FLAGS ID POS_LO POS_HI
|
||||
* +--------------+--------+----------+-------+-------+------+------+------+
|
||||
* | BDEV_OPEN | minor | access | | | id | | |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_CLOSE | minor | | | | id | | |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_READ | minor | bytes | grant | flags | id | position |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_WRITE | minor | bytes | grant | flags | id | position |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_GATHER | minor | elements | grant | flags | id | position |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_SCATTER | minor | elements | grant | flags | id | position |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_IOCTL | minor | request | grant | flags | id | | |
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* The following reply message is used for all requests.
|
||||
*
|
||||
* m_type STATUS ID
|
||||
* +--------------+--------+----------+-------+-------+------+------+------+
|
||||
* | BDEV_REPLY | status | | | | id | | |
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* Changes:
|
||||
* Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek)
|
||||
* Aug 27, 2011 move common functions into driver.c (A. Welzel)
|
||||
* Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder)
|
||||
* Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
|
||||
* Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
|
||||
* Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot)
|
||||
*/
|
||||
|
||||
#include <minix/drivers.h>
|
||||
#include <minix/blockdriver.h>
|
||||
#include <minix/ds.h>
|
||||
#include <sys/ioc_disk.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "mq.h"
|
||||
|
||||
/* Management data for opened devices. */
|
||||
PRIVATE int open_devs[MAX_NR_OPEN_DEVICES];
|
||||
PRIVATE int next_open_devs_slot = 0;
|
||||
|
||||
/*===========================================================================*
|
||||
* clear_open_devs *
|
||||
*===========================================================================*/
|
||||
PRIVATE void clear_open_devs(void)
|
||||
{
|
||||
/* Reset the set of previously opened minor devices. */
|
||||
next_open_devs_slot = 0;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* is_open_dev *
|
||||
*===========================================================================*/
|
||||
PRIVATE int is_open_dev(int device)
|
||||
{
|
||||
/* Check whether the given minor device has previously been opened. */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < next_open_devs_slot; i++)
|
||||
if (open_devs[i] == device)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* set_open_dev *
|
||||
*===========================================================================*/
|
||||
PRIVATE void set_open_dev(int device)
|
||||
{
|
||||
/* Mark the given minor device as having been opened. */
|
||||
|
||||
if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES)
|
||||
panic("out of slots for open devices");
|
||||
|
||||
open_devs[next_open_devs_slot] = device;
|
||||
next_open_devs_slot++;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_announce *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_announce(void)
|
||||
{
|
||||
/* Announce we are up after a fresh start or a restart. */
|
||||
int r;
|
||||
char key[DS_MAX_KEYLEN];
|
||||
char label[DS_MAX_KEYLEN];
|
||||
char *driver_prefix = "drv.blk.";
|
||||
|
||||
/* Callers are allowed to use sendrec to communicate with drivers.
|
||||
* For this reason, there may blocked callers when a driver restarts.
|
||||
* Ask the kernel to unblock them (if any).
|
||||
*/
|
||||
#if USE_STATECTL
|
||||
if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS)) != OK)
|
||||
panic("blockdriver_init: sys_statectl failed: %d", r);
|
||||
#endif
|
||||
|
||||
/* Publish a driver up event. */
|
||||
if ((r = ds_retrieve_label_name(label, getprocnr())) != OK)
|
||||
panic("blockdriver_init: unable to get own label: %d", r);
|
||||
|
||||
snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label);
|
||||
if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
|
||||
panic("blockdriver_init: unable to publish driver up event: %d", r);
|
||||
|
||||
/* Expect an open for any device before serving regular driver requests. */
|
||||
clear_open_devs();
|
||||
|
||||
/* Initialize or reset the message queue. */
|
||||
mq_init();
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_reply *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_reply(message *m_ptr, int ipc_status, int reply)
|
||||
{
|
||||
/* Reply to a block request sent to the driver. */
|
||||
endpoint_t caller_e;
|
||||
long id;
|
||||
int r;
|
||||
|
||||
if (reply == EDONTREPLY)
|
||||
return;
|
||||
|
||||
caller_e = m_ptr->m_source;
|
||||
id = m_ptr->BDEV_ID;
|
||||
|
||||
memset(m_ptr, 0, sizeof(*m_ptr));
|
||||
|
||||
m_ptr->m_type = BDEV_REPLY;
|
||||
m_ptr->BDEV_STATUS = reply;
|
||||
m_ptr->BDEV_ID = id;
|
||||
|
||||
/* If we would block sending the message, send it asynchronously. */
|
||||
if (IPC_STATUS_CALL(ipc_status) == SENDREC)
|
||||
r = sendnb(caller_e, m_ptr);
|
||||
else
|
||||
r = asynsend(caller_e, m_ptr);
|
||||
|
||||
if (r != OK)
|
||||
printf("blockdriver_reply: unable to send reply to %d: %d\n",
|
||||
caller_e, r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_open *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_open(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Open a minor device. */
|
||||
|
||||
return (*bdp->bdr_open)(mp->BDEV_MINOR, mp->BDEV_ACCESS);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_close *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_close(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Close a minor device. */
|
||||
|
||||
return (*bdp->bdr_close)(mp->BDEV_MINOR);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_rdwt *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_rdwt(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Carry out a single read or write request. */
|
||||
iovec_t iovec1;
|
||||
u64_t position;
|
||||
int do_write;
|
||||
ssize_t r;
|
||||
|
||||
/* Disk address? Address and length of the user buffer? */
|
||||
if (mp->BDEV_COUNT < 0) return EINVAL;
|
||||
|
||||
/* Create a one element scatter/gather vector for the buffer. */
|
||||
iovec1.iov_addr = mp->BDEV_GRANT;
|
||||
iovec1.iov_size = mp->BDEV_COUNT;
|
||||
|
||||
/* Transfer bytes from/to the device. */
|
||||
do_write = (mp->m_type == BDEV_WRITE);
|
||||
position = make64(mp->BDEV_POS_LO, mp->BDEV_POS_HI);
|
||||
|
||||
r = (*bdp->bdr_transfer)(mp->BDEV_MINOR, do_write, position, mp->m_source,
|
||||
&iovec1, 1, mp->BDEV_FLAGS);
|
||||
|
||||
/* Return the number of bytes transferred or an error code. */
|
||||
return r;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_vrdwt *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_vrdwt(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Carry out an device read or write to/from a vector of buffers. */
|
||||
iovec_t iovec[NR_IOREQS];
|
||||
unsigned nr_req;
|
||||
u64_t position;
|
||||
int i, do_write;
|
||||
ssize_t r, size;
|
||||
|
||||
/* Copy the vector from the caller to kernel space. */
|
||||
nr_req = mp->BDEV_COUNT; /* Length of I/O vector */
|
||||
if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
|
||||
|
||||
if (OK != sys_safecopyfrom(mp->m_source, (vir_bytes) mp->BDEV_GRANT,
|
||||
0, (vir_bytes) iovec, nr_req * sizeof(iovec[0]), D)) {
|
||||
printf("blockdriver: bad I/O vector by: %d\n", mp->m_source);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Check for overflow condition. */
|
||||
for (i = size = 0; i < nr_req; i++) {
|
||||
if ((ssize_t) (size + iovec[i].iov_size) < size) return EINVAL;
|
||||
size += iovec[i].iov_size;
|
||||
}
|
||||
|
||||
/* Transfer bytes from/to the device. */
|
||||
do_write = (mp->m_type == BDEV_SCATTER);
|
||||
position = make64(mp->BDEV_POS_LO, mp->BDEV_POS_HI);
|
||||
|
||||
r = (*bdp->bdr_transfer)(mp->BDEV_MINOR, do_write, position, mp->m_source,
|
||||
iovec, nr_req, mp->BDEV_FLAGS);
|
||||
|
||||
/* Return the number of bytes transferred or an error code. */
|
||||
return r;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_ioctl *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_ioctl(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Carry out an I/O control request. For now, we handle setting/getting
|
||||
* partitions here, and let the driver handle any other requests.
|
||||
*/
|
||||
struct device *dv;
|
||||
struct partition entry;
|
||||
unsigned int request;
|
||||
cp_grant_id_t grant;
|
||||
dev_t minor;
|
||||
int r;
|
||||
|
||||
minor = mp->BDEV_MINOR;
|
||||
request = mp->BDEV_REQUEST;
|
||||
grant = mp->BDEV_GRANT;
|
||||
|
||||
switch (request) {
|
||||
case DIOCSETP:
|
||||
/* Copy just this one partition table entry. */
|
||||
r = sys_safecopyfrom(mp->m_source, grant, 0, (vir_bytes) &entry,
|
||||
sizeof(entry), D);
|
||||
if (r != OK)
|
||||
return r;
|
||||
|
||||
if ((dv = (*bdp->bdr_part)(minor)) == NULL)
|
||||
return ENXIO;
|
||||
dv->dv_base = entry.base;
|
||||
dv->dv_size = entry.size;
|
||||
|
||||
break;
|
||||
|
||||
case DIOCGETP:
|
||||
/* Return a partition table entry and the geometry of the drive. */
|
||||
if ((dv = (*bdp->bdr_part)(minor)) == NULL)
|
||||
return ENXIO;
|
||||
entry.base = dv->dv_base;
|
||||
entry.size = dv->dv_size;
|
||||
if (bdp->bdr_geometry) {
|
||||
(*bdp->bdr_geometry)(minor, &entry);
|
||||
} else {
|
||||
/* The driver doesn't care -- make up fake geometry. */
|
||||
entry.cylinders = div64u(entry.size, SECTOR_SIZE);
|
||||
entry.heads = 64;
|
||||
entry.sectors = 32;
|
||||
}
|
||||
|
||||
r = sys_safecopyto(mp->m_source, grant, 0, (vir_bytes) &entry,
|
||||
sizeof(entry), D);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (bdp->bdr_ioctl)
|
||||
r = (*bdp->bdr_ioctl)(minor, request, mp->m_source, grant);
|
||||
else
|
||||
r = EINVAL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_handle_notify *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_handle_notify(struct blockdriver *bdp, message *m_ptr)
|
||||
{
|
||||
/* Take care of the notifications (interrupt and clock messages) by calling
|
||||
* the appropiate callback functions. Never send a reply.
|
||||
*/
|
||||
|
||||
/* Call the appropriate function for this notification. */
|
||||
switch (_ENDPOINT_P(m_ptr->m_source)) {
|
||||
case HARDWARE:
|
||||
if (bdp->bdr_intr)
|
||||
(*bdp->bdr_intr)(m_ptr->NOTIFY_ARG);
|
||||
break;
|
||||
|
||||
case CLOCK:
|
||||
if (bdp->bdr_alarm)
|
||||
(*bdp->bdr_alarm)(m_ptr->NOTIFY_TIMESTAMP);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (bdp->bdr_other)
|
||||
(void) (*bdp->bdr_other)(m_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_handle_request *
|
||||
*===========================================================================*/
|
||||
PUBLIC int blockdriver_handle_request(struct blockdriver *bdp, message *m_ptr)
|
||||
{
|
||||
/* Call the appropiate driver function, based on the type of request. Return
|
||||
* the result code that is to be sent back to the caller, or EDONTREPLY if no
|
||||
* reply is to be sent.
|
||||
*/
|
||||
int r;
|
||||
|
||||
/* We might get spurious requests if the driver has been restarted. Deny any
|
||||
* requests on devices that have not previously been opened, signaling the
|
||||
* caller that something went wrong.
|
||||
*/
|
||||
if (IS_BDEV_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->BDEV_MINOR)) {
|
||||
/* Reply ERESTART to spurious requests for unopened devices. */
|
||||
if (m_ptr->m_type != BDEV_OPEN)
|
||||
return ERESTART;
|
||||
|
||||
/* Mark the device as opened otherwise. */
|
||||
set_open_dev(m_ptr->BDEV_MINOR);
|
||||
}
|
||||
|
||||
/* Call the appropriate function(s) for this request. */
|
||||
switch (m_ptr->m_type) {
|
||||
case BDEV_OPEN: r = do_open(bdp, m_ptr); break;
|
||||
case BDEV_CLOSE: r = do_close(bdp, m_ptr); break;
|
||||
case BDEV_READ:
|
||||
case BDEV_WRITE: r = do_rdwt(bdp, m_ptr); break;
|
||||
case BDEV_GATHER:
|
||||
case BDEV_SCATTER: r = do_vrdwt(bdp, m_ptr); break;
|
||||
case BDEV_IOCTL: r = do_ioctl(bdp, m_ptr); break;
|
||||
default:
|
||||
if (bdp->bdr_other)
|
||||
r = bdp->bdr_other(m_ptr);
|
||||
else
|
||||
r = EINVAL;
|
||||
}
|
||||
|
||||
/* Let the driver perform any cleanup. */
|
||||
if (bdp->bdr_cleanup != NULL)
|
||||
(*bdp->bdr_cleanup)();
|
||||
|
||||
return r;
|
||||
}
|
||||
11
lib/libblockdriver/driver.h
Normal file
11
lib/libblockdriver/driver.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef _BLOCKDRIVER_DRIVER_H
|
||||
#define _BLOCKDRIVER_DRIVER_H
|
||||
|
||||
_PROTOTYPE( void blockdriver_handle_notify, (struct blockdriver *bdp,
|
||||
message *m_ptr) );
|
||||
_PROTOTYPE( int blockdriver_handle_request, (struct blockdriver *bdp,
|
||||
message *m_ptr) );
|
||||
_PROTOTYPE( void blockdriver_reply, (message *m_ptr, int ipc_status,
|
||||
int reply) );
|
||||
|
||||
#endif /* _BLOCKDRIVER_DRIVER_H */
|
||||
398
lib/libblockdriver/driver_mt.c
Normal file
398
lib/libblockdriver/driver_mt.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/* This file contains the multithreaded driver interface.
|
||||
*
|
||||
* Changes:
|
||||
* Aug 27, 2011 created (A. Welzel)
|
||||
*
|
||||
* The entry points into this file are:
|
||||
* blockdriver_mt_task: the main message loop of the driver
|
||||
* blockdriver_mt_terminate: break out of the main message loop
|
||||
* blockdriver_mt_sleep: put the current thread to sleep
|
||||
* blockdriver_mt_wakeup: wake up a sleeping thread
|
||||
* blockdriver_mt_stop: put up the current thread for termination
|
||||
*/
|
||||
|
||||
#include <minix/blockdriver_mt.h>
|
||||
#include <minix/mthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "mq.h"
|
||||
#include "event.h"
|
||||
|
||||
typedef enum {
|
||||
STATE_DEAD,
|
||||
STATE_RUNNING,
|
||||
STATE_STOPPING,
|
||||
STATE_EXITED
|
||||
} worker_state;
|
||||
|
||||
/* Structure to handle running worker threads. */
|
||||
typedef struct {
|
||||
thread_id_t id;
|
||||
worker_state state;
|
||||
mthread_thread_t mthread;
|
||||
event_t queue_event;
|
||||
event_t sleep_event;
|
||||
} worker_t;
|
||||
|
||||
PRIVATE struct blockdriver *bdtab;
|
||||
PRIVATE int running = FALSE;
|
||||
|
||||
PRIVATE mthread_key_t worker_key;
|
||||
|
||||
PRIVATE worker_t worker[DRIVER_MT_MAX_WORKERS];
|
||||
|
||||
PRIVATE worker_t *exited[DRIVER_MT_MAX_WORKERS];
|
||||
PRIVATE int num_exited = 0;
|
||||
|
||||
/*===========================================================================*
|
||||
* enqueue *
|
||||
*===========================================================================*/
|
||||
PRIVATE void enqueue(worker_t *wp, const message *m_src, int ipc_status)
|
||||
{
|
||||
/* Enqueue a message into a worker thread's queue, and signal the thread.
|
||||
* Must be called from the master thread.
|
||||
*/
|
||||
|
||||
assert(wp->state == STATE_RUNNING || wp->state == STATE_STOPPING);
|
||||
|
||||
if (!mq_enqueue(wp->id, m_src, ipc_status))
|
||||
panic("blockdriver_mt: enqueue failed (message queue full)");
|
||||
|
||||
event_fire(&wp->queue_event);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* try_dequeue *
|
||||
*===========================================================================*/
|
||||
PRIVATE int try_dequeue(worker_t *wp, message *m_dst, int *ipc_status)
|
||||
{
|
||||
/* See if a message can be dequeued from the current worker thread's queue. If
|
||||
* so, dequeue the message and return TRUE. If not, return FALSE. Must be
|
||||
* called from a worker thread. Does not block.
|
||||
*/
|
||||
|
||||
return mq_dequeue(wp->id, m_dst, ipc_status);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* dequeue *
|
||||
*===========================================================================*/
|
||||
PRIVATE void dequeue(worker_t *wp, message *m_dst, int *ipc_status)
|
||||
{
|
||||
/* Dequeue a message from the current worker thread's queue. Block the current
|
||||
* thread if necessary. Must be called from a worker thread. Always successful.
|
||||
*/
|
||||
|
||||
while (!try_dequeue(wp, m_dst, ipc_status))
|
||||
event_wait(&wp->queue_event);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* worker_thread *
|
||||
*===========================================================================*/
|
||||
PRIVATE void *worker_thread(void *param)
|
||||
{
|
||||
/* The worker thread loop. Set up the thread-specific reference to itself and
|
||||
* start looping. The loop consists of blocking dequeing and handling messages.
|
||||
* After handling a message, the thread might have been stopped, so we check
|
||||
* for this condition and exit if so.
|
||||
*/
|
||||
worker_t *wp;
|
||||
message m;
|
||||
int ipc_status, r;
|
||||
|
||||
wp = (worker_t *) param;
|
||||
assert(wp != NULL);
|
||||
|
||||
if (mthread_setspecific(worker_key, wp))
|
||||
panic("blockdriver_mt: could not save local thread pointer");
|
||||
|
||||
while (running) {
|
||||
/* See if a new message is available right away. */
|
||||
if (!try_dequeue(wp, &m, &ipc_status)) {
|
||||
/* If not, and this thread should be stopped, stop now. */
|
||||
if (wp->state == STATE_STOPPING)
|
||||
break;
|
||||
|
||||
/* Otherwise, block waiting for a new message. */
|
||||
dequeue(wp, &m, &ipc_status);
|
||||
|
||||
if (!running)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Even if the thread was stopped before, a new message resumes it. */
|
||||
wp->state = STATE_RUNNING;
|
||||
|
||||
/* Handle the request, and send a reply. */
|
||||
r = blockdriver_handle_request(bdtab, &m);
|
||||
|
||||
blockdriver_reply(&m, ipc_status, r);
|
||||
}
|
||||
|
||||
/* Clean up and terminate this thread. */
|
||||
if (mthread_setspecific(worker_key, NULL))
|
||||
panic("blockdriver_mt: could not delete local thread pointer");
|
||||
|
||||
wp->state = STATE_EXITED;
|
||||
|
||||
exited[num_exited++] = wp;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* master_create_worker *
|
||||
*===========================================================================*/
|
||||
PRIVATE void master_create_worker(worker_t *wp, thread_id_t id)
|
||||
{
|
||||
/* Start a new worker thread.
|
||||
*/
|
||||
int r;
|
||||
|
||||
wp->id = id;
|
||||
wp->state = STATE_RUNNING;
|
||||
|
||||
/* Initialize synchronization primitives. */
|
||||
event_init(&wp->queue_event);
|
||||
event_init(&wp->sleep_event);
|
||||
|
||||
r = mthread_create(&wp->mthread, NULL /*attr*/, worker_thread, (void *) wp);
|
||||
|
||||
if (r != 0)
|
||||
panic("blockdriver_mt: could not start thread %d (%d)", id, r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* master_destroy_worker *
|
||||
*===========================================================================*/
|
||||
PRIVATE void master_destroy_worker(worker_t *wp)
|
||||
{
|
||||
/* Clean up resources used by an exited worker thread.
|
||||
*/
|
||||
message m;
|
||||
int ipc_status;
|
||||
|
||||
assert(wp != NULL);
|
||||
assert(wp->state == STATE_EXITED);
|
||||
assert(!mq_dequeue(wp->id, &m, &ipc_status));
|
||||
|
||||
/* Join the thread. */
|
||||
if (mthread_join(wp->mthread, NULL))
|
||||
panic("blockdriver_mt: could not join thread %d", wp->id);
|
||||
|
||||
/* Destroy resources. */
|
||||
event_destroy(&wp->sleep_event);
|
||||
event_destroy(&wp->queue_event);
|
||||
|
||||
wp->state = STATE_DEAD;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* master_handle_exits *
|
||||
*===========================================================================*/
|
||||
PRIVATE void master_handle_exits(void)
|
||||
{
|
||||
/* Destroy the remains of all exited threads.
|
||||
*/
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_exited; i++)
|
||||
master_destroy_worker(exited[i]);
|
||||
|
||||
num_exited = 0;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* master_handle_request *
|
||||
*===========================================================================*/
|
||||
PRIVATE void master_handle_request(message *m_ptr, int ipc_status)
|
||||
{
|
||||
/* For real request messages, query the thread ID, start a thread if one with
|
||||
* that ID is not already running, and enqueue the message in the thread's
|
||||
* message queue.
|
||||
*/
|
||||
thread_id_t thread_id;
|
||||
worker_t *wp;
|
||||
int r;
|
||||
|
||||
/* If this is not a block driver request, we cannot get the minor device
|
||||
* associated with it, and thus we can not tell which thread should process
|
||||
* it either. In that case, the master thread has to handle it instead.
|
||||
*/
|
||||
if (!IS_BDEV_RQ(m_ptr->m_type)) {
|
||||
/* Process as 'other' message. */
|
||||
r = blockdriver_handle_request(bdtab, m_ptr);
|
||||
|
||||
blockdriver_reply(m_ptr, ipc_status, r);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Query the thread ID. Upon failure, send the error code to the caller. */
|
||||
r = (*bdtab->bdr_thread)(m_ptr->DEVICE, &thread_id);
|
||||
|
||||
if (r != OK) {
|
||||
blockdriver_reply(m_ptr, ipc_status, r);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Start the thread if it is not already running. */
|
||||
assert(thread_id >= 0 && thread_id < DRIVER_MT_MAX_WORKERS);
|
||||
|
||||
wp = &worker[thread_id];
|
||||
|
||||
assert(wp->state != STATE_EXITED);
|
||||
|
||||
if (wp->state == STATE_DEAD)
|
||||
master_create_worker(wp, thread_id);
|
||||
|
||||
/* Enqueue the message for the thread, and possibly wake it up. */
|
||||
enqueue(wp, m_ptr, ipc_status);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* master_init *
|
||||
*===========================================================================*/
|
||||
PRIVATE void master_init(struct blockdriver *bdp)
|
||||
{
|
||||
/* Initialize the state of the master thread.
|
||||
*/
|
||||
int i;
|
||||
|
||||
assert(bdp != NULL);
|
||||
assert(bdp->bdr_thread != NULL);
|
||||
|
||||
mthread_init();
|
||||
|
||||
bdtab = bdp;
|
||||
|
||||
for (i = 0; i < DRIVER_MT_MAX_WORKERS; i++)
|
||||
worker[i].state = STATE_DEAD;
|
||||
|
||||
/* Initialize a per-thread key, where each worker thread stores its own
|
||||
* reference to the worker structure.
|
||||
*/
|
||||
if (mthread_key_create(&worker_key, NULL))
|
||||
panic("blockdriver_mt: error initializing worker key");
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_mt_receive *
|
||||
*===========================================================================*/
|
||||
PRIVATE void blockdriver_mt_receive(message *m_ptr, int *ipc_status)
|
||||
{
|
||||
/* Receive a message.
|
||||
*/
|
||||
int r;
|
||||
|
||||
r = sef_receive_status(ANY, m_ptr, ipc_status);
|
||||
|
||||
if (r != OK)
|
||||
panic("blockdriver_mt: sef_receive_status() returned %d", r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_mt_task *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_mt_task(struct blockdriver *driver_tab)
|
||||
{
|
||||
/* The multithreaded driver task.
|
||||
*/
|
||||
int ipc_status;
|
||||
message mess;
|
||||
|
||||
/* Initialize first if necessary. */
|
||||
if (!running) {
|
||||
master_init(driver_tab);
|
||||
|
||||
running = TRUE;
|
||||
}
|
||||
|
||||
/* The main message loop. */
|
||||
while (running) {
|
||||
/* Receive a message. */
|
||||
blockdriver_mt_receive(&mess, &ipc_status);
|
||||
|
||||
/* Dispatch the message. */
|
||||
if (is_ipc_notify(ipc_status))
|
||||
blockdriver_handle_notify(bdtab, &mess);
|
||||
else
|
||||
master_handle_request(&mess, ipc_status);
|
||||
|
||||
/* Let other threads run. */
|
||||
mthread_yield_all();
|
||||
|
||||
/* Clean up any exited threads. */
|
||||
if (num_exited > 0)
|
||||
master_handle_exits();
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_mt_terminate *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_mt_terminate(void)
|
||||
{
|
||||
/* Instruct libblockdriver to shut down.
|
||||
*/
|
||||
|
||||
running = FALSE;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_mt_sleep *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_mt_sleep(void)
|
||||
{
|
||||
/* Let the current thread sleep until it gets woken up by the master thread.
|
||||
*/
|
||||
worker_t *wp;
|
||||
|
||||
wp = (worker_t *) mthread_getspecific(worker_key);
|
||||
|
||||
if (wp == NULL)
|
||||
panic("blockdriver_mt: master thread cannot sleep");
|
||||
|
||||
event_wait(&wp->sleep_event);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_mt_wakeup *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_mt_wakeup(thread_id_t id)
|
||||
{
|
||||
/* Wake up a sleeping worker thread from the master thread.
|
||||
*/
|
||||
worker_t *wp;
|
||||
|
||||
assert(id >= 0 && id < DRIVER_MT_MAX_WORKERS);
|
||||
|
||||
wp = &worker[id];
|
||||
|
||||
assert(wp->state == STATE_RUNNING || wp->state == STATE_STOPPING);
|
||||
|
||||
event_fire(&wp->sleep_event);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_mt_stop *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_mt_stop(void)
|
||||
{
|
||||
/* Put up the current worker thread for termination. Once the current dispatch
|
||||
* call has finished, and there are no more messages in the thread's message
|
||||
* queue, the thread will be terminated. Any messages in the queue will undo
|
||||
* the effect of this call.
|
||||
*/
|
||||
worker_t *wp;
|
||||
|
||||
wp = (worker_t *) mthread_getspecific(worker_key);
|
||||
|
||||
assert(wp != NULL);
|
||||
assert(wp->state == STATE_RUNNING || wp->state == STATE_STOPPING);
|
||||
|
||||
wp->state = STATE_STOPPING;
|
||||
}
|
||||
98
lib/libblockdriver/driver_st.c
Normal file
98
lib/libblockdriver/driver_st.c
Normal file
@@ -0,0 +1,98 @@
|
||||
/* This file contains the singlethreaded device driver interface.
|
||||
*
|
||||
* Changes:
|
||||
* Aug 27, 2011 extracted from driver.c (A. Welzel)
|
||||
*
|
||||
* The entry points into this file are:
|
||||
* blockdriver_task: the main message loop of the driver
|
||||
* blockdriver_terminate: break out of the main message loop
|
||||
* blockdriver_handle_msg: handle a single received message
|
||||
* blockdriver_receive_mq: message receive interface with message queueing
|
||||
* blockdriver_mq_queue: queue an incoming message for later processing
|
||||
*/
|
||||
|
||||
#include <minix/drivers.h>
|
||||
#include <minix/blockdriver.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "mq.h"
|
||||
|
||||
PRIVATE int running;
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_receive_mq *
|
||||
*===========================================================================*/
|
||||
PUBLIC int blockdriver_receive_mq(message *m_ptr, int *status_ptr)
|
||||
{
|
||||
/* receive() interface for drivers with message queueing. */
|
||||
|
||||
/* Any queued messages? */
|
||||
if (mq_dequeue(MQ_SINGLE, m_ptr, status_ptr))
|
||||
return OK;
|
||||
|
||||
/* Fall back to standard receive() interface otherwise. */
|
||||
return driver_receive(ANY, m_ptr, status_ptr);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_terminate *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_terminate(void)
|
||||
{
|
||||
/* Break out of the main driver loop after finishing the current request. */
|
||||
|
||||
running = FALSE;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_task *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_task(struct blockdriver *bdp)
|
||||
{
|
||||
/* Main program of any block device driver task. */
|
||||
int r, ipc_status;
|
||||
message mess;
|
||||
|
||||
running = TRUE;
|
||||
|
||||
/* Here is the main loop of the disk task. It waits for a message, carries
|
||||
* it out, and sends a reply.
|
||||
*/
|
||||
while (running) {
|
||||
if ((r = blockdriver_receive_mq(&mess, &ipc_status)) != OK)
|
||||
panic("blockdriver_receive_mq failed: %d", r);
|
||||
|
||||
blockdriver_process(bdp, &mess, ipc_status);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_process *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_process(struct blockdriver *bdp, message *m_ptr,
|
||||
int ipc_status)
|
||||
{
|
||||
/* Handle the given received message. */
|
||||
int r;
|
||||
|
||||
/* Process the notification or request. */
|
||||
if (is_ipc_notify(ipc_status)) {
|
||||
blockdriver_handle_notify(bdp, m_ptr);
|
||||
|
||||
/* Do not reply to notifications. */
|
||||
} else {
|
||||
r = blockdriver_handle_request(bdp, m_ptr);
|
||||
|
||||
blockdriver_reply(m_ptr, ipc_status, r);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_mq_queue *
|
||||
*===========================================================================*/
|
||||
PUBLIC int blockdriver_mq_queue(message *m, int status)
|
||||
{
|
||||
/* Queue a message for later processing. */
|
||||
|
||||
return mq_enqueue(MQ_SINGLE, m, status);
|
||||
}
|
||||
229
lib/libblockdriver/drvlib.c
Normal file
229
lib/libblockdriver/drvlib.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/* IBM device driver utility functions. Author: Kees J. Bot
|
||||
* 7 Dec 1995
|
||||
* Entry point:
|
||||
* partition: partition a disk to the partition table(s) on it.
|
||||
*/
|
||||
|
||||
#include <minix/blockdriver.h>
|
||||
#include <minix/drvlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Extended partition? */
|
||||
#define ext_part(s) ((s) == 0x05 || (s) == 0x0F)
|
||||
|
||||
FORWARD _PROTOTYPE( void parse_part_table, (struct blockdriver *bdp,
|
||||
int device, int style, int atapi, u8_t *tmp_buf) );
|
||||
FORWARD _PROTOTYPE( void extpartition, (struct blockdriver *bdp, int extdev,
|
||||
unsigned long extbase, u8_t *tmp_buf) );
|
||||
FORWARD _PROTOTYPE( int get_part_table, (struct blockdriver *bdp, int device,
|
||||
unsigned long offset, struct part_entry *table, u8_t *tmp_buf) );
|
||||
FORWARD _PROTOTYPE( void sort, (struct part_entry *table) );
|
||||
|
||||
/*============================================================================*
|
||||
* partition *
|
||||
*============================================================================*/
|
||||
PUBLIC void partition(bdp, device, style, atapi)
|
||||
struct blockdriver *bdp; /* device dependent entry points */
|
||||
int device; /* device to partition */
|
||||
int style; /* partitioning style: floppy, primary, sub. */
|
||||
int atapi; /* atapi device */
|
||||
{
|
||||
/* This routine is called on first open to initialize the partition tables
|
||||
* of a device.
|
||||
*/
|
||||
u8_t *tmp_buf;
|
||||
|
||||
if ((*bdp->bdr_part)(device) == NULL)
|
||||
return;
|
||||
|
||||
/* For multithreaded drivers, multiple partition() calls may be made on
|
||||
* different devices in parallel. Hence we need a separate temporary buffer
|
||||
* for each request.
|
||||
*/
|
||||
if (!(tmp_buf = alloc_contig(CD_SECTOR_SIZE, AC_ALIGN4K, NULL)))
|
||||
panic("partition: unable to allocate temporary buffer");
|
||||
|
||||
parse_part_table(bdp, device, style, atapi, tmp_buf);
|
||||
|
||||
free_contig(tmp_buf, CD_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
/*============================================================================*
|
||||
* parse_part_table *
|
||||
*============================================================================*/
|
||||
PRIVATE void parse_part_table(bdp, device, style, atapi, tmp_buf)
|
||||
struct blockdriver *bdp; /* device dependent entry points */
|
||||
int device; /* device to partition */
|
||||
int style; /* partitioning style: floppy, primary, sub. */
|
||||
int atapi; /* atapi device */
|
||||
u8_t *tmp_buf; /* temporary buffer */
|
||||
{
|
||||
/* This routine reads and parses a partition table. It may be called
|
||||
* recursively. It makes sure that each partition falls safely within the
|
||||
* device's limits. Depending on the partition style we are either making
|
||||
* floppy partitions, primary partitions or subpartitions. Only primary
|
||||
* partitions are sorted, because they are shared with other operating
|
||||
* systems that expect this.
|
||||
*/
|
||||
struct part_entry table[NR_PARTITIONS], *pe;
|
||||
int disk, par;
|
||||
struct device *dv;
|
||||
unsigned long base, limit, part_limit;
|
||||
|
||||
/* Get the geometry of the device to partition */
|
||||
if ((dv = (*bdp->bdr_part)(device)) == NULL
|
||||
|| cmp64u(dv->dv_size, 0) == 0) return;
|
||||
base = div64u(dv->dv_base, SECTOR_SIZE);
|
||||
limit = base + div64u(dv->dv_size, SECTOR_SIZE);
|
||||
|
||||
/* Read the partition table for the device. */
|
||||
if(!get_part_table(bdp, device, 0L, table, tmp_buf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute the device number of the first partition. */
|
||||
switch (style) {
|
||||
case P_FLOPPY:
|
||||
device += MINOR_fd0p0;
|
||||
break;
|
||||
case P_PRIMARY:
|
||||
sort(table); /* sort a primary partition table */
|
||||
device += 1;
|
||||
break;
|
||||
case P_SUB:
|
||||
disk = device / DEV_PER_DRIVE;
|
||||
par = device % DEV_PER_DRIVE - 1;
|
||||
device = MINOR_d0p0s0 + (disk * NR_PARTITIONS + par) * NR_PARTITIONS;
|
||||
}
|
||||
|
||||
/* Find an array of devices. */
|
||||
if ((dv = (*bdp->bdr_part)(device)) == NULL) return;
|
||||
|
||||
/* Set the geometry of the partitions from the partition table. */
|
||||
for (par = 0; par < NR_PARTITIONS; par++, dv++) {
|
||||
/* Shrink the partition to fit within the device. */
|
||||
pe = &table[par];
|
||||
part_limit = pe->lowsec + pe->size;
|
||||
if (part_limit < pe->lowsec) part_limit = limit;
|
||||
if (part_limit > limit) part_limit = limit;
|
||||
if (pe->lowsec < base) pe->lowsec = base;
|
||||
if (part_limit < pe->lowsec) part_limit = pe->lowsec;
|
||||
|
||||
dv->dv_base = mul64u(pe->lowsec, SECTOR_SIZE);
|
||||
dv->dv_size = mul64u(part_limit - pe->lowsec, SECTOR_SIZE);
|
||||
|
||||
if (style == P_PRIMARY) {
|
||||
/* Each Minix primary partition can be subpartitioned. */
|
||||
if (pe->sysind == MINIX_PART)
|
||||
parse_part_table(bdp, device + par, P_SUB, atapi,
|
||||
tmp_buf);
|
||||
|
||||
/* An extended partition has logical partitions. */
|
||||
if (ext_part(pe->sysind))
|
||||
extpartition(bdp, device + par, pe->lowsec, tmp_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*============================================================================*
|
||||
* extpartition *
|
||||
*============================================================================*/
|
||||
PRIVATE void extpartition(bdp, extdev, extbase, tmp_buf)
|
||||
struct blockdriver *bdp; /* device dependent entry points */
|
||||
int extdev; /* extended partition to scan */
|
||||
unsigned long extbase; /* sector offset of the base ext. partition */
|
||||
u8_t *tmp_buf; /* temporary buffer */
|
||||
{
|
||||
/* Extended partitions cannot be ignored alas, because people like to move
|
||||
* files to and from DOS partitions. Avoid reading this code, it's no fun.
|
||||
*/
|
||||
struct part_entry table[NR_PARTITIONS], *pe;
|
||||
int subdev, disk, par;
|
||||
struct device *dv;
|
||||
unsigned long offset, nextoffset;
|
||||
|
||||
disk = extdev / DEV_PER_DRIVE;
|
||||
par = extdev % DEV_PER_DRIVE - 1;
|
||||
subdev = MINOR_d0p0s0 + (disk * NR_PARTITIONS + par) * NR_PARTITIONS;
|
||||
|
||||
offset = 0;
|
||||
do {
|
||||
if (!get_part_table(bdp, extdev, offset, table, tmp_buf)) return;
|
||||
sort(table);
|
||||
|
||||
/* The table should contain one logical partition and optionally
|
||||
* another extended partition. (It's a linked list.)
|
||||
*/
|
||||
nextoffset = 0;
|
||||
for (par = 0; par < NR_PARTITIONS; par++) {
|
||||
pe = &table[par];
|
||||
if (ext_part(pe->sysind)) {
|
||||
nextoffset = pe->lowsec;
|
||||
} else
|
||||
if (pe->sysind != NO_PART) {
|
||||
if ((dv = (*bdp->bdr_part)(subdev)) == NULL) return;
|
||||
|
||||
dv->dv_base = mul64u(extbase + offset + pe->lowsec,
|
||||
SECTOR_SIZE);
|
||||
dv->dv_size = mul64u(pe->size, SECTOR_SIZE);
|
||||
|
||||
/* Out of devices? */
|
||||
if (++subdev % NR_PARTITIONS == 0) return;
|
||||
}
|
||||
}
|
||||
} while ((offset = nextoffset) != 0);
|
||||
}
|
||||
|
||||
/*============================================================================*
|
||||
* get_part_table *
|
||||
*============================================================================*/
|
||||
PRIVATE int get_part_table(bdp, device, offset, table, tmp_buf)
|
||||
struct blockdriver *bdp;
|
||||
int device;
|
||||
unsigned long offset; /* sector offset to the table */
|
||||
struct part_entry *table; /* four entries */
|
||||
u8_t *tmp_buf; /* temporary buffer */
|
||||
{
|
||||
/* Read the partition table for the device, return true iff there were no
|
||||
* errors.
|
||||
*/
|
||||
iovec_t iovec1;
|
||||
u64_t position;
|
||||
int r;
|
||||
|
||||
position = mul64u(offset, SECTOR_SIZE);
|
||||
iovec1.iov_addr = (vir_bytes) tmp_buf;
|
||||
iovec1.iov_size = CD_SECTOR_SIZE;
|
||||
r = (*bdp->bdr_transfer)(device, FALSE /*do_write*/, position, SELF,
|
||||
&iovec1, 1, BDEV_NOFLAGS);
|
||||
if (r != CD_SECTOR_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
if (tmp_buf[510] != 0x55 || tmp_buf[511] != 0xAA) {
|
||||
/* Invalid partition table. */
|
||||
return 0;
|
||||
}
|
||||
memcpy(table, (tmp_buf + PART_TABLE_OFF), NR_PARTITIONS * sizeof(table[0]));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* sort *
|
||||
*===========================================================================*/
|
||||
PRIVATE void sort(table)
|
||||
struct part_entry *table;
|
||||
{
|
||||
/* Sort a partition table. */
|
||||
struct part_entry *pe, tmp;
|
||||
int n = NR_PARTITIONS;
|
||||
|
||||
do {
|
||||
for (pe = table; pe < table + NR_PARTITIONS-1; pe++) {
|
||||
if (pe[0].sysind == NO_PART
|
||||
|| (pe[0].lowsec > pe[1].lowsec
|
||||
&& pe[1].sysind != NO_PART)) {
|
||||
tmp = pe[0]; pe[0] = pe[1]; pe[1] = tmp;
|
||||
}
|
||||
}
|
||||
} while (--n > 0);
|
||||
}
|
||||
71
lib/libblockdriver/event.c
Normal file
71
lib/libblockdriver/event.c
Normal file
@@ -0,0 +1,71 @@
|
||||
/* This file contains a simple thread event implementation.
|
||||
*/
|
||||
|
||||
#include <minix/mthread.h>
|
||||
#include <minix/sysutil.h>
|
||||
|
||||
#include "event.h"
|
||||
|
||||
/*===========================================================================*
|
||||
* event_init *
|
||||
*===========================================================================*/
|
||||
PUBLIC void event_init(event_t *event)
|
||||
{
|
||||
/* Initialize an event object.
|
||||
*/
|
||||
int r;
|
||||
|
||||
if ((r = mthread_mutex_init(&event->mutex, NULL)) != 0)
|
||||
panic("libblockdriver: error initializing mutex (%d)", r);
|
||||
if ((r = mthread_cond_init(&event->cond, NULL)) != 0)
|
||||
panic("libblockdriver: error initializing condvar (%d)", r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* event_destroy *
|
||||
*===========================================================================*/
|
||||
PUBLIC void event_destroy(event_t *event)
|
||||
{
|
||||
/* Destroy an event object.
|
||||
*/
|
||||
int r;
|
||||
|
||||
if ((r = mthread_cond_destroy(&event->cond)) != 0)
|
||||
panic("libblockdriver: error destroying condvar (%d)", r);
|
||||
if ((r = mthread_mutex_destroy(&event->mutex)) != 0)
|
||||
panic("libblockdriver: error destroying mutex (%d)", r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* event_wait *
|
||||
*===========================================================================*/
|
||||
PUBLIC void event_wait(event_t *event)
|
||||
{
|
||||
/* Wait for an event, blocking the current thread in the process.
|
||||
*/
|
||||
int r;
|
||||
|
||||
if ((r = mthread_mutex_lock(&event->mutex)) != 0)
|
||||
panic("libblockdriver: error locking mutex (%d)", r);
|
||||
if ((r = mthread_cond_wait(&event->cond, &event->mutex)) != 0)
|
||||
panic("libblockdriver: error waiting for condvar (%d)", r);
|
||||
if ((r = mthread_mutex_unlock(&event->mutex)) != 0)
|
||||
panic("libblockdriver: error unlocking mutex (%d)", r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* event_fire *
|
||||
*===========================================================================*/
|
||||
PUBLIC void event_fire(event_t *event)
|
||||
{
|
||||
/* Fire an event, waking up any thread blocked on it without scheduling it.
|
||||
*/
|
||||
int r;
|
||||
|
||||
if ((r = mthread_mutex_lock(&event->mutex)) != 0)
|
||||
panic("libblockdriver: error locking mutex (%d)", r);
|
||||
if ((r = mthread_cond_signal(&event->cond)) != 0)
|
||||
panic("libblockdriver: error signaling condvar (%d)", r);
|
||||
if ((r = mthread_mutex_unlock(&event->mutex)) != 0)
|
||||
panic("libblockdriver: error unlocking mutex (%d)", r);
|
||||
}
|
||||
14
lib/libblockdriver/event.h
Normal file
14
lib/libblockdriver/event.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef _BLOCKDRIVER_EVENT_H
|
||||
#define _BLOCKDRIVER_EVENT_H
|
||||
|
||||
typedef struct {
|
||||
mthread_mutex_t mutex;
|
||||
mthread_cond_t cond;
|
||||
} event_t;
|
||||
|
||||
_PROTOTYPE( void event_init, (event_t *event) );
|
||||
_PROTOTYPE( void event_destroy, (event_t *event) );
|
||||
_PROTOTYPE( void event_wait, (event_t *event) );
|
||||
_PROTOTYPE( void event_fire, (event_t *event) );
|
||||
|
||||
#endif /* _BLOCKDRIVER_EVENT_H */
|
||||
97
lib/libblockdriver/mq.c
Normal file
97
lib/libblockdriver/mq.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/* This file contains a simple message queue implementation to support both
|
||||
* the singlethread and the multithreaded driver implementation.
|
||||
*
|
||||
* Changes:
|
||||
* Oct 27, 2011 rewritten to use sys/queue.h (D.C. van Moolenbroek)
|
||||
* Aug 27, 2011 integrated into libblockdriver (A. Welzel)
|
||||
*/
|
||||
|
||||
#include <minix/blockdriver_mt.h>
|
||||
#include <sys/queue.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "mq.h"
|
||||
|
||||
#define MQ_SIZE 128
|
||||
|
||||
struct mq_cell {
|
||||
message mess;
|
||||
int ipc_status;
|
||||
STAILQ_ENTRY(mq_cell) next;
|
||||
};
|
||||
|
||||
PRIVATE struct mq_cell pool[MQ_SIZE];
|
||||
|
||||
PRIVATE STAILQ_HEAD(queue, mq_cell) queue[DRIVER_MT_MAX_WORKERS];
|
||||
PRIVATE STAILQ_HEAD(free_list, mq_cell) free_list;
|
||||
|
||||
/*===========================================================================*
|
||||
* mq_init *
|
||||
*===========================================================================*/
|
||||
PUBLIC void mq_init(void)
|
||||
{
|
||||
/* Initialize the message queues and message cells.
|
||||
*/
|
||||
int i;
|
||||
|
||||
STAILQ_INIT(&free_list);
|
||||
|
||||
for (i = 0; i < DRIVER_MT_MAX_WORKERS; i++)
|
||||
STAILQ_INIT(&queue[i]);
|
||||
|
||||
for (i = 0; i < MQ_SIZE; i++)
|
||||
STAILQ_INSERT_HEAD(&free_list, &pool[i], next);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* mq_enqueue *
|
||||
*===========================================================================*/
|
||||
PUBLIC int mq_enqueue(thread_id_t thread_id, const message *mess,
|
||||
int ipc_status)
|
||||
{
|
||||
/* Add a message, including its IPC status, to the message queue of a thread.
|
||||
* Return TRUE iff the message was added successfully.
|
||||
*/
|
||||
struct mq_cell *cell;
|
||||
|
||||
assert(thread_id >= 0 && thread_id < DRIVER_MT_MAX_WORKERS);
|
||||
|
||||
if (STAILQ_EMPTY(&free_list))
|
||||
return FALSE;
|
||||
|
||||
cell = STAILQ_FIRST(&free_list);
|
||||
STAILQ_REMOVE_HEAD(&free_list, next);
|
||||
|
||||
cell->mess = *mess;
|
||||
cell->ipc_status = ipc_status;
|
||||
|
||||
STAILQ_INSERT_TAIL(&queue[thread_id], cell, next);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* mq_dequeue *
|
||||
*===========================================================================*/
|
||||
PUBLIC int mq_dequeue(thread_id_t thread_id, message *mess, int *ipc_status)
|
||||
{
|
||||
/* Return and remove a message, including its IPC status, from the message
|
||||
* queue of a thread. Return TRUE iff a message was available.
|
||||
*/
|
||||
struct mq_cell *cell;
|
||||
|
||||
assert(thread_id >= 0 && thread_id < DRIVER_MT_MAX_WORKERS);
|
||||
|
||||
if (STAILQ_EMPTY(&queue[thread_id]))
|
||||
return FALSE;
|
||||
|
||||
cell = STAILQ_FIRST(&queue[thread_id]);
|
||||
STAILQ_REMOVE_HEAD(&queue[thread_id], next);
|
||||
|
||||
*mess = cell->mess;
|
||||
*ipc_status = cell->ipc_status;
|
||||
|
||||
STAILQ_INSERT_HEAD(&free_list, cell, next);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
12
lib/libblockdriver/mq.h
Normal file
12
lib/libblockdriver/mq.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef _BLOCKDRIVER_MQ_H
|
||||
#define _BLOCKDRIVER_MQ_H
|
||||
|
||||
#define MQ_SINGLE 0 /* thread ID for single-threading */
|
||||
|
||||
_PROTOTYPE( void mq_init, (void) );
|
||||
_PROTOTYPE( int mq_enqueue, (thread_id_t thread_id, const message *mess,
|
||||
int ipc_status) );
|
||||
_PROTOTYPE( int mq_dequeue, (thread_id_t thread_id, message *mess,
|
||||
int *ipc_status) );
|
||||
|
||||
#endif /* _BLOCKDRIVER_MQ_H */
|
||||
Reference in New Issue
Block a user