Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / geekos / src / geekos / blockdev.c
diff --git a/geekos/src/geekos/blockdev.c b/geekos/src/geekos/blockdev.c
new file mode 100644 (file)
index 0000000..5a4bf07
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Block devices
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/errno.h>
+#include <geekos/screen.h>
+#include <geekos/string.h>
+#include <geekos/malloc.h>
+#include <geekos/int.h>
+#include <geekos/kthread.h>
+#include <geekos/synch.h>
+#include <geekos/blockdev.h>
+
+/*#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);
+}
+