X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=blobdiff_plain;f=geekos%2Fsrc%2Fgeekos%2Fblockdev.c;fp=geekos%2Fsrc%2Fgeekos%2Fblockdev.c;h=5a4bf07f2afc453389cb80f407a56688c1ace3fd;hp=0000000000000000000000000000000000000000;hb=ddc16b0737cf58f7aa90a69c6652cdf4090aec51;hpb=626595465a2c6987606a6bc697df65130ad8c2d3 diff --git a/geekos/src/geekos/blockdev.c b/geekos/src/geekos/blockdev.c new file mode 100644 index 0000000..5a4bf07 --- /dev/null +++ b/geekos/src/geekos/blockdev.c @@ -0,0 +1,265 @@ +/* + * Block devices + * Copyright (c) 2003 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/*#define BLOCKDEV_DEBUG */ +#ifdef BLOCKDEV_DEBUG +# define Debug(args...) Print(args) +#else +# define Debug(args...) +#endif + +/* ---------------------------------------------------------------------- + * Private data and functions + * ---------------------------------------------------------------------- */ + +/* + * Lock protecting access/modification of block device list. + */ +static struct Mutex s_blockdevLock; + +/* + * List datatype for list of block devices. + */ +DEFINE_LIST(Block_Device_List, Block_Device); +IMPLEMENT_LIST(Block_Device_List, Block_Device); + +/* + * The list in which all block devices in the system + * are registered. + */ +static struct Block_Device_List s_deviceList; + +/* + * Perform a block IO request. + * Returns 0 if successful, error code on failure. + */ +static int Do_Request(struct Block_Device *dev, enum Request_Type type, int blockNum, void *buf) +{ + struct Block_Request *request; + int rc; + + request = Create_Request(dev, type, blockNum, buf); + if (request == 0) + return ENOMEM; + Post_Request_And_Wait(request); + rc = request->errorCode; + Free(request); + return rc; +} + +/* ---------------------------------------------------------------------- + * Public functions + * ---------------------------------------------------------------------- */ + +/* + * Register a block device. + * This should be called by device drivers in their Init + * functions to register all detected devices. + * Returns 0 if successful, error code otherwise. + */ +int Register_Block_Device(const char *name, struct Block_Device_Ops *ops, + int unit, void *driverData, struct Thread_Queue *waitQueue, + struct Block_Request_List *requestQueue) +{ + struct Block_Device *dev; + + KASSERT(ops != 0); + KASSERT(waitQueue != 0); + KASSERT(requestQueue != 0); + + dev = (struct Block_Device*) Malloc(sizeof(*dev)); + if (dev == 0) + return ENOMEM; + + strcpy(dev->name, name); + dev->ops = ops; + dev->unit = unit; + dev->inUse = false; + dev->driverData = driverData; + dev->waitQueue = waitQueue; + dev->requestQueue = requestQueue; + + Mutex_Lock(&s_blockdevLock); + /* FIXME: handle name conflict with existing device */ + Debug("Registering block device %s\n", dev->name); + Add_To_Back_Of_Block_Device_List(&s_deviceList, dev); + Mutex_Unlock(&s_blockdevLock); + + return 0; +} + +/* + * Open a named block device. + * Return 0 if successful, error code on error. + */ +int Open_Block_Device(const char *name, struct Block_Device **pDev) +{ + struct Block_Device *dev; + int rc = 0; + + Mutex_Lock(&s_blockdevLock); + + dev = Get_Front_Of_Block_Device_List(&s_deviceList); + while (dev != 0) { + if (strcmp(dev->name, name) == 0) + break; + dev = Get_Next_In_Block_Device_List(dev); + } + + if (dev == 0) + rc = ENODEV; + else if (dev->inUse) + rc = EBUSY; + else { + rc = dev->ops->Open(dev); + if (rc == 0) { + *pDev = dev; + dev->inUse = true; + } + } + + Mutex_Unlock(&s_blockdevLock); + + return rc; +} + +/* + * Close given block device. + * Return 0 if successful, error code on error. + */ +int Close_Block_Device(struct Block_Device *dev) +{ + int rc; + + Mutex_Lock(&s_blockdevLock); + + KASSERT(dev->inUse); + rc = dev->ops->Close(dev); + if (rc == 0) + dev->inUse = false; + + Mutex_Unlock(&s_blockdevLock); + + return rc; +} + +/* + * Create a block device request to transfer a single block. + */ +struct Block_Request *Create_Request(struct Block_Device *dev, enum Request_Type type, + int blockNum, void *buf) +{ + struct Block_Request *request = Malloc(sizeof(*request)); + if (request != 0) { + request->dev = dev; + request->type = type; + request->blockNum = blockNum; + request->buf = buf; + request->state = PENDING; + Clear_Thread_Queue(&request->waitQueue); + } + return request; +} + +/* + * Send a block IO request to a device and wait for it to be handled. + * Returns when the driver completes the requests or signals + * an error. + */ +void Post_Request_And_Wait(struct Block_Request *request) +{ + struct Block_Device *dev; + + KASSERT(request != 0); + + dev = request->dev; + KASSERT(dev != 0); + + /* Send request to the driver */ + Debug("Posting block device request [@%x]...\n", request); + Disable_Interrupts(); + Add_To_Back_Of_Block_Request_List(dev->requestQueue, request); + Wake_Up(dev->waitQueue); + Enable_Interrupts(); + + /* Wait for request to be processed */ + Disable_Interrupts(); + while (request->state == PENDING) { + Debug("Waiting, state=%d\n", request->state); + Wait(&request->waitQueue); + } + Debug("Wait completed!\n"); + Enable_Interrupts(); +} + +/* + * Wait for a block request to arrive. + */ +struct Block_Request *Dequeue_Request(struct Block_Request_List *requestQueue, + struct Thread_Queue *waitQueue) +{ + struct Block_Request *request; + + Disable_Interrupts(); + while (Is_Block_Request_List_Empty(requestQueue)) + Wait(waitQueue); + request = Get_Front_Of_Block_Request_List(requestQueue); + Remove_From_Front_Of_Block_Request_List(requestQueue); + Enable_Interrupts(); + + return request; +} + +/* + * Signal the completion of a block request. + */ +void Notify_Request_Completion(struct Block_Request *request, enum Request_State state, int errorCode) +{ + Disable_Interrupts(); + request->state = state; + request->errorCode = errorCode; + Wake_Up(&request->waitQueue); + Enable_Interrupts(); +} + +/* + * Read a block from given device. + * Return 0 if successful, error code on error. + */ +int Block_Read(struct Block_Device *dev, int blockNum, void *buf) +{ + return Do_Request(dev, BLOCK_READ, blockNum, buf); +} + +/* + * Write a block to given device. + * Return 0 if successful, error code on error. + */ +int Block_Write(struct Block_Device *dev, int blockNum, void *buf) +{ + return Do_Request(dev, BLOCK_WRITE, blockNum, buf); +} + +/* + * Get number of blocks in given device. + */ +int Get_Num_Blocks(struct Block_Device *dev) +{ + return dev->ops->Get_Num_Blocks(dev); +} +