From e51bd967cda1989d606b5b6178ed6d2c8d151707 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Thu, 28 Feb 2002 20:39:54 +0000 Subject: 2002-02-28 Joel Sherrill * Submitted by Victor V. Vengerov and merged into the RTEMS source. * ChangeLog, Makefile.am, README, configure.ac, include/Makefile.am, include/rtems/bdbuf.h, include/rtems/blkdev.h, include/rtems/diskdevs.h, include/rtems/ramdisk.h, include/rtems/.cvsignore, include/.cvsignore, src/Makefile.am, src/bdbuf.c, src/blkdev.c, src/diskdevs.c, src/ramdisk.c, src/.cvsignore, .cvsignore: New files. --- c/src/exec/libblock/.cvsignore | 14 + c/src/exec/libblock/ChangeLog | 25 + c/src/exec/libblock/Makefile.am | 13 + c/src/exec/libblock/README | 14 + c/src/exec/libblock/configure.ac | 32 + c/src/exec/libblock/include/.cvsignore | 2 + c/src/exec/libblock/include/Makefile.am | 23 + c/src/exec/libblock/include/rtems/.cvsignore | 2 + c/src/exec/libblock/include/rtems/bdbuf.h | 282 ++++ c/src/exec/libblock/include/rtems/blkdev.h | 147 ++ c/src/exec/libblock/include/rtems/diskdevs.h | 206 +++ c/src/exec/libblock/include/rtems/ramdisk.h | 52 + c/src/exec/libblock/src/.cvsignore | 2 + c/src/exec/libblock/src/Makefile.am | 33 + c/src/exec/libblock/src/bdbuf.c | 1876 ++++++++++++++++++++++++++ c/src/exec/libblock/src/blkdev.c | 243 ++++ c/src/exec/libblock/src/diskdevs.c | 631 +++++++++ c/src/exec/libblock/src/ramdisk.c | 224 +++ c/src/libblock/.cvsignore | 14 + c/src/libblock/ChangeLog | 25 + c/src/libblock/Makefile.am | 13 + c/src/libblock/README | 14 + c/src/libblock/configure.ac | 32 + c/src/libblock/include/.cvsignore | 2 + c/src/libblock/include/Makefile.am | 23 + c/src/libblock/include/rtems/.cvsignore | 2 + c/src/libblock/include/rtems/bdbuf.h | 282 ++++ c/src/libblock/include/rtems/blkdev.h | 147 ++ c/src/libblock/include/rtems/diskdevs.h | 206 +++ c/src/libblock/include/rtems/ramdisk.h | 52 + c/src/libblock/src/.cvsignore | 2 + c/src/libblock/src/Makefile.am | 33 + c/src/libblock/src/bdbuf.c | 1876 ++++++++++++++++++++++++++ c/src/libblock/src/blkdev.c | 243 ++++ c/src/libblock/src/diskdevs.c | 631 +++++++++ c/src/libblock/src/ramdisk.c | 224 +++ 36 files changed, 7642 insertions(+) create mode 100644 c/src/exec/libblock/.cvsignore create mode 100644 c/src/exec/libblock/ChangeLog create mode 100644 c/src/exec/libblock/Makefile.am create mode 100644 c/src/exec/libblock/README create mode 100644 c/src/exec/libblock/configure.ac create mode 100644 c/src/exec/libblock/include/.cvsignore create mode 100644 c/src/exec/libblock/include/Makefile.am create mode 100644 c/src/exec/libblock/include/rtems/.cvsignore create mode 100644 c/src/exec/libblock/include/rtems/bdbuf.h create mode 100644 c/src/exec/libblock/include/rtems/blkdev.h create mode 100644 c/src/exec/libblock/include/rtems/diskdevs.h create mode 100644 c/src/exec/libblock/include/rtems/ramdisk.h create mode 100644 c/src/exec/libblock/src/.cvsignore create mode 100644 c/src/exec/libblock/src/Makefile.am create mode 100644 c/src/exec/libblock/src/bdbuf.c create mode 100644 c/src/exec/libblock/src/blkdev.c create mode 100644 c/src/exec/libblock/src/diskdevs.c create mode 100644 c/src/exec/libblock/src/ramdisk.c create mode 100644 c/src/libblock/.cvsignore create mode 100644 c/src/libblock/ChangeLog create mode 100644 c/src/libblock/Makefile.am create mode 100644 c/src/libblock/README create mode 100644 c/src/libblock/configure.ac create mode 100644 c/src/libblock/include/.cvsignore create mode 100644 c/src/libblock/include/Makefile.am create mode 100644 c/src/libblock/include/rtems/.cvsignore create mode 100644 c/src/libblock/include/rtems/bdbuf.h create mode 100644 c/src/libblock/include/rtems/blkdev.h create mode 100644 c/src/libblock/include/rtems/diskdevs.h create mode 100644 c/src/libblock/include/rtems/ramdisk.h create mode 100644 c/src/libblock/src/.cvsignore create mode 100644 c/src/libblock/src/Makefile.am create mode 100644 c/src/libblock/src/bdbuf.c create mode 100644 c/src/libblock/src/blkdev.c create mode 100644 c/src/libblock/src/diskdevs.c create mode 100644 c/src/libblock/src/ramdisk.c (limited to 'c/src') diff --git a/c/src/exec/libblock/.cvsignore b/c/src/exec/libblock/.cvsignore new file mode 100644 index 0000000000..d29e5050f5 --- /dev/null +++ b/c/src/exec/libblock/.cvsignore @@ -0,0 +1,14 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +config.cache +config.guess +config.log +config.status +config.sub +configure +depcomp +install-sh +missing +mkinstalldirs diff --git a/c/src/exec/libblock/ChangeLog b/c/src/exec/libblock/ChangeLog new file mode 100644 index 0000000000..bc000d4a46 --- /dev/null +++ b/c/src/exec/libblock/ChangeLog @@ -0,0 +1,25 @@ +2002-02-28 Joel Sherrill + + * Submitted by Victor V. Vengerov and merged + into the RTEMS source. + * ChangeLog, Makefile.am, README, configure.ac, include/Makefile.am, + include/rtems/bdbuf.h, include/rtems/blkdev.h, include/rtems/diskdevs.h, + include/rtems/ramdisk.h, include/rtems/.cvsignore, include/.cvsignore, + src/Makefile.am, src/bdbuf.c, src/blkdev.c, src/diskdevs.c, + src/ramdisk.c, src/.cvsignore, .cvsignore: New files. + +2001-11-29 Victor V. Vengerov + * AVL trees implementation added. + +2001-11-16 Victor V. Vengerov + * include/rtems/bdbuf.h, src/bdbuf.c(rtems_bdbuf_syncdev): New. + +2001-11-07 Victor V. Vengerov + + * ChangeLog: New file. + * src/, include/, include/rtems/: New directories. + * README, configure.ac, Makefile.am, src/Makefile.am, + include/Makefile.am: New files. + * include/rtems/bdbuf.h include/rtems/blkdev.h + include/rtems/diskdevs.h include/rtems/ramdisk.h + src/bdbuf.c src/blkdev.c src/diskdevs.c src/ramdisk.c: New files. diff --git a/c/src/exec/libblock/Makefile.am b/c/src/exec/libblock/Makefile.am new file mode 100644 index 0000000000..970205bc1f --- /dev/null +++ b/c/src/exec/libblock/Makefile.am @@ -0,0 +1,13 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.4 +ACLOCAL_AMFLAGS = -I ../../../aclocal + +SUBDIRS = include src + +EXTRA_DIST = README + +include $(top_srcdir)/../../../automake/subdirs.am +include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/exec/libblock/README b/c/src/exec/libblock/README new file mode 100644 index 0000000000..671cc7f645 --- /dev/null +++ b/c/src/exec/libblock/README @@ -0,0 +1,14 @@ +# +# $Id$ +# + +This directory contains the block device (HDD, CDROMs, etc) support code. +It includes: + - block device driver interface + - generic open/close/read/write/ioctl primitives for block device drivers + - disk I/O buffering + - logical disk support + - RAM disk block device driver + +Victor V. Vengerov, +November, 7 2001 diff --git a/c/src/exec/libblock/configure.ac b/c/src/exec/libblock/configure.ac new file mode 100644 index 0000000000..d0e5d61fb6 --- /dev/null +++ b/c/src/exec/libblock/configure.ac @@ -0,0 +1,32 @@ +## Process this file with autoconf to produce a configure script. +## +## $Id$ + +AC_PREREQ(2.52) +AC_INIT +AC_CONFIG_SRCDIR([src/bdbuf.c]) +RTEMS_TOP(../../..) +AC_CONFIG_AUX_DIR(../../..) + +RTEMS_CANONICAL_TARGET_CPU +RTEMS_CANONICAL_HOST + +AM_INIT_AUTOMAKE(rtems-c-src-libblock,$RTEMS_VERSION,no) +AM_MAINTAINER_MODE + +RTEMS_ENV_RTEMSBSP +RTEMS_CHECK_CPU +RTEMS_CHECK_CUSTOM_BSP(RTEMS_BSP) +RTEMS_PROG_CC_FOR_TARGET +RTEMS_ENABLE_BARE + +RTEMS_CANONICALIZE_TOOLS + +RTEMS_PROJECT_ROOT + +# Explicitly list all Makefiles here +AC_CONFIG_FILES([Makefile +include/Makefile +src/Makefile +]) +AC_OUTPUT diff --git a/c/src/exec/libblock/include/.cvsignore b/c/src/exec/libblock/include/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/c/src/exec/libblock/include/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/c/src/exec/libblock/include/Makefile.am b/c/src/exec/libblock/include/Makefile.am new file mode 100644 index 0000000000..298ae2974d --- /dev/null +++ b/c/src/exec/libblock/include/Makefile.am @@ -0,0 +1,23 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.5 + +include_rtemsdir = $(includedir)/rtems + +$(PROJECT_INCLUDE)/%.h: %.h + $(INSTALL_DATA) $< $@ + +$(PROJECT_INCLUDE)/rtems: + @$(mkinstalldirs) $@ + +include_rtems_HEADERS = \ + rtems/bdbuf.h rtems/blkdev.h rtems/diskdevs.h rtems/ramdisk.h + +PREINSTALL_FILES = $(PROJECT_INCLUDE)/rtems \ + $(include_rtems_HEADERS:%=$(PROJECT_INCLUDE)/%) + +all-local: $(PREINSTALL_FILES) + +include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/exec/libblock/include/rtems/.cvsignore b/c/src/exec/libblock/include/rtems/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/c/src/exec/libblock/include/rtems/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/c/src/exec/libblock/include/rtems/bdbuf.h b/c/src/exec/libblock/include/rtems/bdbuf.h new file mode 100644 index 0000000000..270b69598b --- /dev/null +++ b/c/src/exec/libblock/include/rtems/bdbuf.h @@ -0,0 +1,282 @@ +/* bdbuf.h -- block device buffer management + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#ifndef __RTEMS_LIBBLOCK_BDBUF_H__ +#define __RTEMS_LIBBLOCK_BDBUF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "rtems/blkdev.h" +#include "rtems/diskdevs.h" + + +/* + * To manage buffers we using Buffer Descriptors. + * To speed-up buffer lookup descriptors are organized in AVL-Tree. + * The fields 'dev' and 'block' are search key. + */ + +/* Buffer descriptors + * Descriptors organized in AVL-tree to speedup buffer lookup. + * dev and block fields are search key in AVL-tree. + * Modified buffers, free buffers and used buffers linked in 'mod', 'free' and + * 'lru' chains appropriately. + */ + +typedef struct bdbuf_buffer { + Chain_Node link; /* Link in the lru, mod or free chains */ + +#ifdef BINARY_TREE + struct bdbuf_avl_node { + struct bdbuf_buffer *left; /* link to the left sub-tree */ + struct bdbuf_buffer *right; /* link to the right sub-tree */ + + int bf; /* AVL tree node balance factor */ + } avl; /* AVL-tree links */ +#else /* AVL TREE */ + struct bdbuf_avl_node { + char cache; /* Cache */ + + struct bdbuf_buffer* link[2]; /* Left and Right Kids */ + + char bal; /* The balance of the sub-tree */ + } avl; +#endif + dev_t dev; /* device number */ + blkdev_bnum block; /* block number on the device */ + + char *buffer; /* Pointer to the buffer memory area */ + rtems_status_code status; /* Last I/O operation completion status */ + int error; /* If status != RTEMS_SUCCESSFUL, this field contains + errno value which can be used by user later */ + boolean modified:1; /* =1 if buffer was modified */ + boolean in_progress:1; /* =1 if exchange with disk is in progress; + need to wait on semaphore */ + boolean actual:1; /* Buffer contains actual data */ + int use_count; /* Usage counter; incremented when somebody use + this buffer; decremented when buffer released + without modification or when buffer is flushed + by swapout task */ + + rtems_bdpool_id pool; /* Identifier of buffer pool to which this buffer + belongs */ + CORE_mutex_Control transfer_sema; + /* Transfer operation semaphore */ +} bdbuf_buffer; + + + +/* bdbuf_config structure describes block configuration (size, + * amount, memory location) for buffering layer + */ +typedef struct rtems_bdbuf_config { + int size; /* Size of block */ + int num; /* Number of blocks of appropriate size */ + char *mem_area; /* Pointer to the blocks location or NULL, in this + case memory for blocks will be allocated by + Buffering Layer with the help of RTEMS partition + manager */ +} rtems_bdbuf_config; + +extern rtems_bdbuf_config rtems_bdbuf_configuration[]; +extern int rtems_bdbuf_configuration_size; + +/* rtems_bdbuf_init -- + * Prepare buffering layer to work - initialize buffer descritors + * and (if it is neccessary) buffers. Buffers will be allocated accoriding + * to the configuration table, each entry describes kind of block and + * amount requested. After initialization all blocks is placed into + * free elements lists. + * + * PARAMETERS: + * conf_table - pointer to the buffers configuration table + * size - number of entries in configuration table + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + */ +rtems_status_code +rtems_bdbuf_init(rtems_bdbuf_config *conf_table, int size); + + +/* rtems_bdbuf_get -- + * Obtain block buffer. If specified block already cached (i.e. there's + * block in the _modified_, or _recently_used_), return address + * of appropriate buffer descriptor and increment reference counter to 1. + * If block is not cached, allocate new buffer and return it. Data + * shouldn't be read to the buffer from media; buffer may contains + * arbitrary data. This primitive may be blocked if there are no free + * buffer descriptors available and there are no unused non-modified + * (or synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (constructed of major and minor device number) + * block - linear media block number + * bd - address of variable to store pointer to the buffer descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * bufget_sema semaphore obtained by this primitive. + */ +rtems_status_code +rtems_bdbuf_get(dev_t device, blkdev_bnum block, bdbuf_buffer **bdb_ptr); + +/* rtems_bdbuf_read -- + * (Similar to the rtems_bdbuf_get, except reading data from media) + * Obtain block buffer. If specified block already cached, return address + * of appropriate buffer and increment reference counter to 1. If block is + * not cached, allocate new buffer and read data to it from the media. + * This primitive may be blocked on waiting until data to be read from + * media, if there are no free buffer descriptors available and there are + * no unused non-modified (or synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (consists of major and minor device number) + * block - linear media block number + * bd - address of variable to store pointer to the buffer descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * bufget_sema and transfer_sema semaphores obtained by this primitive. + */ +rtems_status_code +rtems_bdbuf_read(dev_t device, blkdev_bnum block, bdbuf_buffer **bdb_ptr); + +/* rtems_bdbuf_release -- + * Release buffer allocated before. This primitive decrease the + * usage counter. If it is zero, further destiny of buffer depends on + * 'modified' status. If buffer was modified, it is placed to the end of + * mod list and flush task waken up. If buffer was not modified, + * it is placed to the end of lru list, and bufget_sema released, allowing + * to reuse this buffer. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * flush_sema and bufget_sema semaphores may be released by this primitive. + */ +rtems_status_code +rtems_bdbuf_release(bdbuf_buffer *bd_buf); + +/* rtems_bdbuf_release_modified -- + * Release buffer allocated before, assuming that it is _modified_ by + * it's owner. This primitive decrease usage counter for buffer, mark + * buffer descriptor as modified. If usage counter is 0, insert it at + * end of mod chain and release flush_sema semaphore to activate the + * flush task. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * flush_sema semaphore may be released by this primitive. + */ +rtems_status_code +rtems_bdbuf_release_modified(bdbuf_buffer *bd_buf); + +/* rtems_bdbuf_sync -- + * Wait until specified buffer synchronized with disk. Invoked on exchanges + * critical for data consistency on the media. This primitive mark owned + * block as modified, decrease usage counter. If usage counter is 0, + * block inserted to the mod chain and flush_sema semaphore released. + * Finally, primitives blocked on transfer_sema semaphore. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * Primitive may be blocked on transfer_sema semaphore. + */ +rtems_status_code +rtems_bdbuf_sync(bdbuf_buffer *bd_buf); + +/* rtems_bdbuf_syncdev -- + * Synchronize with disk all buffers containing the blocks belonging to + * specified device. + * + * PARAMETERS: + * dev - block device number + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + */ +rtems_status_code +rtems_bdbuf_syncdev(dev_t dev); + +/* rtems_bdbuf_find_pool -- + * Find first appropriate buffer pool. This primitive returns the index + * of first buffer pool which block size is greater than or equal to + * specified size. + * + * PARAMETERS: + * block_size - requested block size + * pool - placeholder for result + * + * RETURNS: + * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully, + * RTEMS_INVALID_SIZE if specified block size is invalid (not a power + * of 2), RTEMS_NOT_DEFINED if buffer pool for this or greater block size + * is not configured. + */ +rtems_status_code +rtems_bdbuf_find_pool(int block_size, rtems_bdpool_id *pool); + +/* rtems_bdbuf_get_pool_info -- + * Obtain characteristics of buffer pool with specified number. + * + * PARAMETERS: + * pool - buffer pool number + * block_size - block size for which buffer pool is configured returned + * there + * blocks - number of buffers in buffer pool returned there + * + * RETURNS: + * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully, + * RTEMS_INVALID_NUMBER if appropriate buffer pool is not configured. + * + * NOTE: + * Buffer pools enumerated contiguously starting from 0. + */ +rtems_status_code +rtems_bdbuf_get_pool_info(rtems_bdpool_id pool, int *block_size, int *blocks); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/exec/libblock/include/rtems/blkdev.h b/c/src/exec/libblock/include/rtems/blkdev.h new file mode 100644 index 0000000000..ca3b5ba5e2 --- /dev/null +++ b/c/src/exec/libblock/include/rtems/blkdev.h @@ -0,0 +1,147 @@ +/* + * blkdev.h - block device driver interface definitions + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#ifndef __RTEMS_LIBBLOCK_BLKDEV_H__ +#define __RTEMS_LIBBLOCK_BLKDEV_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Interface with device drivers + * Block device looks, initialized and behaves like traditional RTEMS device + * driver. Heart of the block device driver is in BIOREQUEST ioctl. This call + * puts I/O request to the block device queue, in priority order, for + * asynchronous processing. When driver executes request, req_done + * function invoked, so callee knows about it. Look for details below. + */ + + +/* Block device block number datatype */ +typedef rtems_unsigned32 blkdev_bnum; + +/* Block device request type */ +typedef enum blkdev_request_op { + BLKDEV_REQ_READ, /* Read operation */ + BLKDEV_REQ_WRITE /* Write operation */ +} blkdev_request_op; + +/* Type for block device request done callback function. + * + * PARAMETERS: + * arg - argument supplied in blkdev_request + * status - rtems status code for this operation + * errno - errno value to be passed to the user when + * status != RTEMS_SUCCESSFUL + */ +typedef void (* blkdev_request_cb)(void *arg, + rtems_status_code status, + int error); + +/* blkdev_sg_buffer + * Block device scatter/gather buffer structure + */ +typedef struct blkdev_sg_buffer { + rtems_unsigned32 length; /* Buffer length */ + void *buffer; /* Buffer pointer */ +} blkdev_sg_buffer; + +/* blkdev_request (Block Device Request) structure is + * used to read/write a number of blocks from/to device. + */ +typedef struct blkdev_request { + blkdev_request_op req; /* Block device operation (read or write) */ + blkdev_request_cb req_done; /* Callback function */ + void *done_arg; /* Argument to be passed to callback function*/ + blkdev_bnum start; /* Start block number */ + rtems_unsigned32 count; /* Number of blocks to be exchanged */ + rtems_unsigned32 bufnum; /* Number of buffers provided */ + + blkdev_sg_buffer bufs[0];/* List of scatter/gather buffers */ +} blkdev_request; + +/* Block device IOCTL request codes */ +#define BLKIO_REQUEST _IOWR('B', 1, blkdev_request) +#define BLKIO_GETBLKSIZE _IO('B', 2) +#define BLKIO_GETSIZE _IO('B', 3) +#define BLKIO_SYNCDEV _IO('B', 4) + +/* Device driver interface conventions suppose that driver may + * contain initialize/open/close/read/write/ioctl entry points. These + * primitives (except initialize) can be implemented in generic fashion, + * based upon supplied block device driver ioctl handler. Every block + * device driver should provide initialize entry point, which is register + * all block devices and appropriate ioctl handlers. + */ + +#define GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES \ + rtems_blkdev_generic_open, rtems_blkdev_generic_close, \ + rtems_blkdev_generic_read, rtems_blkdev_generic_write, \ + rtems_blkdev_generic_ioctl + +/* blkdev_generic_read -- + * Generic block device read primitive. Implemented using block device + * buffer management primitives. + */ +rtems_device_driver +rtems_blkdev_generic_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +/* blkdev_generic_write -- + * Generic block device driver write primitive. Implemented using block + * device buffer management primitives. + */ +rtems_device_driver +rtems_blkdev_generic_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +/* blkdev_generic_open -- + * Generic block device open primitive. + */ +rtems_device_driver +rtems_blkdev_generic_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +/* blkdev_generic_close -- + * Generic block device close primitive. + */ +rtems_device_driver +rtems_blkdev_generic_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +/* blkdev_generic_ioctl -- + * Generic block device ioctl primitive. + */ +rtems_device_driver +rtems_blkdev_generic_ioctl( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/exec/libblock/include/rtems/diskdevs.h b/c/src/exec/libblock/include/rtems/diskdevs.h new file mode 100644 index 0000000000..fab3231e62 --- /dev/null +++ b/c/src/exec/libblock/include/rtems/diskdevs.h @@ -0,0 +1,206 @@ +/* + * logdisk.h - Physical and logical block devices (disks) support + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#ifndef __RTEMS_LIBBLOCK_LOGDISK_H__ +#define __RTEMS_LIBBLOCK_LOGDISK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "rtems/blkdev.h" + +/* Buffer pool identifier */ +typedef int rtems_bdpool_id; + +/* Block device ioctl handler */ +typedef int (* block_device_ioctl) (dev_t dev, int req, void *argp); + +/* disk_device: Entry of this type created for every disk device (both for + * logical and physical disks). + * Array of arrays of pointers to disk_device structures maintained. First + * table indexed by major number and second table indexed by minor number. + * Such data organization allow quick lookup using data structure of + * moderated size. + */ +typedef struct disk_device { + dev_t dev; /* Device ID (major + minor) */ + struct disk_device *phys_dev; /* Physical device ID (the same + as dev if this entry specifies + the physical device) */ + char *name; /* Disk device name */ + int uses; /* Use counter. Device couldn't be + removed if it is in use. */ + int start; /* Starting block number (0 for + physical devices, block offset + on the related physical device + for logical device) */ + int size; /* Size of physical or logical disk + in disk blocks */ + int block_size; /* Size of device block (minimum + transfer unit) in bytes + (must be power of 2) */ + int block_size_log2; /* log2 of block_size */ + rtems_bdpool_id pool; /* Buffer pool assigned to this + device */ + block_device_ioctl ioctl; /* ioctl handler for this block + device */ +} disk_device; + +/* rtems_disk_create_phys -- + * Create physical disk entry. This function usually invoked from + * block device driver initialization code when physical device + * detected in the system. Device driver should provide ioctl handler + * to allow block device access operations. This primitive will register + * device in rtems (invoke rtems_io_register_name). + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * block_size - size of disk block (minimum data transfer unit); must be + * power of 2 + * disk_size - number of blocks on device + * handler - IOCTL handler (function providing basic block input/output + * request handling BIOREQUEST and other device management + * operations) + * name - character name of device (e.g. /dev/hda) + * + * RETURNS: + * RTEMS_SUCCESSFUL if information about new physical disk added, or + * error code if error occured (device already registered, wrong block + * size value, no memory available). + */ +rtems_status_code +rtems_disk_create_phys(dev_t dev, int block_size, int disk_size, + block_device_ioctl handler, + char *name); + +/* rtems_disk_create_log -- + * Create logical disk entry. Logical disk is contiguous area on physical + * disk. Disk may be splitted to several logical disks in several ways: + * manually or using information stored in blocks on physical disk + * (DOS-like partition table, BSD disk label, etc). This function usually + * invoked from application when application-specific splitting are in use, + * or from generic code which handle different logical disk organizations. + * This primitive will register device in rtems (invoke + * rtems_io_register_name). + * + * PARAMETERS: + * dev - logical device identifier (major, minor numbers) + * phys - physical device (block device which holds this logical disk) + * identifier + * start - starting block number on the physical device + * size - logical disk size in blocks + * name - logical disk name + * + * RETURNS: + * RTEMS_SUCCESSFUL if logical device successfully added, or error code + * if error occured (device already registered, no physical device + * exists, logical disk is out of physical disk boundaries, no memory + * available). + */ +rtems_status_code +rtems_disk_create_log(dev_t dev, dev_t phys, int start, int size, char *name); + +/* rtems_disk_delete -- + * Delete physical or logical disk device. Device may be deleted if its + * use counter (and use counters of all logical devices - if it is + * physical device) equal to 0. When physical device deleted, + * all logical devices deleted inherently. Appropriate devices removed + * from "/dev" filesystem. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * + * RETURNS: + * RTEMS_SUCCESSFUL if block device successfully deleted, or error code + * if error occured (device is not defined, device is in use). + */ +rtems_status_code +rtems_disk_delete(dev_t dev); + +/* rtems_disk_lookup -- + * Find block device descriptor by its device identifier. This function + * increment usage counter to 1. User should release disk_device structure + * by invoking rtems_disk_release primitive. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * + * RETURNS: + * pointer to the block device descriptor, or NULL if no such device + * exists. + */ +disk_device * +rtems_disk_lookup(dev_t dev); + +/* rtems_disk_release -- + * Release disk_device structure (decrement usage counter to 1). + * + * PARAMETERS: + * dd - pointer to disk device structure + * + * RETURNS: + * RTEMS_SUCCESSFUL + * + * NOTE: + * It should be implemented as inline function. + */ +rtems_status_code +rtems_disk_release(disk_device *dd); + +/* rtems_disk_next -- + * Disk device enumerator. Looking for device having device number larger + * than dev and return disk device descriptor for it. If there are no + * such device, NULL value returned. + * + * PARAMETERS: + * dev - device number (use -1 to start search) + * + * RETURNS: + * Pointer to the disk descriptor for next disk device, or NULL if all + * devices enumerated. */ +disk_device * +rtems_disk_next(dev_t dev); + +/* rtems_diskio_initialize -- + * Initialization of disk device library (initialize all data structures, + * etc.) + * + * PARAMETERS: + * none + * + * RETURNS: + * RTEMS_SUCCESSFUL if library initialized, or error code if error + * occured. + */ +rtems_status_code +rtems_disk_io_initialize(void); + +/* rtems_diskio_done -- + * Release all resources allocated for disk device interface. + * + * PARAMETERS: + * none + * + * RETURNS: + * RTEMS_SUCCESSFUL if all resources released, or error code if error + * occured. + */ +rtems_status_code +rtems_disk_io_done(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/exec/libblock/include/rtems/ramdisk.h b/c/src/exec/libblock/include/rtems/ramdisk.h new file mode 100644 index 0000000000..b9e8c238b8 --- /dev/null +++ b/c/src/exec/libblock/include/rtems/ramdisk.h @@ -0,0 +1,52 @@ +/* ramdisk.c -- RAM disk block device implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#ifndef __RTEMS_LIBBLOCK_RAMDISK_H__ +#define __RTEMS_LIBBLOCK_RAMDISK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "rtems/blkdev.h" + +/* RAM disk configuration table entry */ +typedef struct rtems_ramdisk_config { + int block_size; /* RAM disk block size */ + int block_num; /* Number of blocks on this RAM disk */ + void *location; /* RAM disk permanent location (out of RTEMS controlled + memory), or NULL if RAM disk memory should be + allocated dynamically */ +} rtems_ramdisk_config; + +/* If application want to use RAM disk, it should specify configuration of + * available RAM disks. + * The following is definitions for RAM disk configuration table + */ +extern rtems_ramdisk_config rtems_ramdisk_configuration[]; +extern int rtems_ramdisk_configuration_size; + +/* ramdisk_initialize -- + * RAM disk driver initialization entry point. + */ +rtems_device_driver +ramdisk_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg); + +#define RAMDISK_DRIVER_TABLE_ENTRY \ + { ramdisk_initialize, GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES } + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/exec/libblock/src/.cvsignore b/c/src/exec/libblock/src/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/c/src/exec/libblock/src/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/c/src/exec/libblock/src/Makefile.am b/c/src/exec/libblock/src/Makefile.am new file mode 100644 index 0000000000..4b563282df --- /dev/null +++ b/c/src/exec/libblock/src/Makefile.am @@ -0,0 +1,33 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.5 + +LIBNAME = libblock +LIB = ${ARCH}/${LIBNAME}.a + +C_FILES = bdbuf.c blkdev.c diskdevs.c ramdisk.c + +C_O_FILES = $(C_FILES:%.c=${ARCH}/%.o) + +SRCS = $(C_FILES) +OBJS = $(C_O_FILES) + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../../../automake/compile.am +include $(top_srcdir)/../../../automake/lib.am + +AM_CFLAGS += $(LIBC_DEFINES) + +TMPINSTALL_FILES += $(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a + +$(LIB): ${OBJS} + $(make-library) + +$(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a: $(LIB) + $(INSTALL_DATA) $< $@ + +all-local: ${ARCH} $(PREINSTALL_FILES) $(TMPINSTALL_FILES) + +include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/exec/libblock/src/bdbuf.c b/c/src/exec/libblock/src/bdbuf.c new file mode 100644 index 0000000000..806ad35243 --- /dev/null +++ b/c/src/exec/libblock/src/bdbuf.c @@ -0,0 +1,1876 @@ +/* + * Disk I/O buffering + * Buffer managment + * + * Copyright (C) 2001 OKTET Ltd., St.-Peterburg, Russia + * Author: Andrey G. Ivanov + * Victor V. Vengerov + * + * @(#) $Id$ + */ + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include +#include +#include +#include + +#include "rtems/bdbuf.h" + +/* Fatal errors: */ +#define BLKDEV_FATAL_ERROR(n) (('B' << 24) | ((n) & 0x00FFFFFF)) +#define BLKDEV_FATAL_BDBUF_CONSISTENCY BLKDEV_FATAL_ERROR(1) +#define BLKDEV_FATAL_BDBUF_SWAPOUT BLKDEV_FATAL_ERROR(2) + +enum balance_factor {BF_LEFT = -1, + BF_NONE = 0, + BF_RIGHT = 1}; + + +#define SWAPOUT_PRIORITY 15 +#define SWAPOUT_STACK_SIZE (RTEMS_MINIMUM_STACK_SIZE * 2) + +static rtems_task bdbuf_swapout_task(rtems_task_argument unused); + +/* + * The groups of the blocks with the same size are collected in the + * bd_pool. Note that a several of the buffer's groups with the + * same size can exists. + */ +typedef struct bdbuf_pool +{ + bdbuf_buffer *tree; /* Buffer descriptor lookup AVL tree root */ + + Chain_Control free; /* Free buffers list */ + Chain_Control lru; /* Last recently used list */ + + int blksize; /* The size of the blocks (in bytes) */ + int nblks; /* Number of blocks in this pool */ + rtems_id bufget_sema; /* Buffer obtain counting semaphore */ + void *mallocd_bufs; /* Pointer to the malloc'd buffer memory, + or NULL, if buffer memory provided in + buffer configuration */ + bdbuf_buffer *bdbufs; /* Pointer to table of buffer descriptors + allocated for this buffer pool. */ +} bdbuf_pool; + +/* Buffering layer context definition */ +struct bdbuf_context { + bdbuf_pool *pool; /* Table of buffer pools */ + int npools; /* Number of entries in pool table */ + + Chain_Control mod; /* Modified buffers list */ + rtems_id flush_sema; /* Buffer flush semaphore; counting + semaphore; incremented when buffer + flushed to the disk; decremented when + buffer modified */ + rtems_id swapout_task; /* Swapout task ID */ +}; + +/* Block device request with a single buffer provided */ +typedef struct blkdev_request1 { + blkdev_request req; + blkdev_sg_buffer sg[1]; +} blkdev_request1; + +/* The static context of buffering layer */ +static struct bdbuf_context bd_ctx; + +#define SAFE +#ifdef SAFE +typedef rtems_mode preemption_key; + +#define DISABLE_PREEMPTION(key) \ + do { \ + rtems_task_mode(RTEMS_PREEMPT_MASK, RTEMS_NO_PREEMPT, &(key)); \ + } while (0) + +#define ENABLE_PREEMPTION(key) \ + do { \ + rtems_mode temp; \ + rtems_task_mode(RTEMS_PREEMPT_MASK, (key), &temp); \ + } while (0) + +#else + +typedef boolean preemption_key; + +#define DISABLE_PREEMPTION(key) \ + do { \ + (key) = _Thread_Executing->is_preemptible; \ + _Thread_Executing->is_preemptible = 0; \ + } while (0) + +#define ENABLE_PREEMPTION(key) \ + do { \ + _Thread_Executing->is_preemptible = (key); \ + if (_Thread_Evaluate_mode()) \ + _Thread_Dispatch(); \ + } while (0) + +#endif + + +#ifdef BINARY_TREE +static bdbuf_buffer * +avl_search(bdbuf_buffer **root, dev_t dev, blkdev_bnum block) +{ + bdbuf_buffer *p = *root; + while ((p != NULL) && ((p->dev != dev) || (p->block != block))) + { + if ((p->dev < dev) || ((p->dev == dev) && (p->block < block))) + p = p->avl.right; + else + p = p->avl.left; + } + return p; +} + +static bdbuf_buffer * +avl_search_for_sync(bdbuf_buffer **root, disk_device *dd) +{ + bdbuf_buffer *p = *root; + bdbuf_buffer *s[32]; + bdbuf_buffer **sp = s; + dev_t dev = dd->phys_dev->dev; + blkdev_bnum b = dd->start; + blkdev_bnum e = b + dd->size - 1; + + while (p != NULL) + { + if ((p->dev < dev) || ((p->dev == dev) && (p->block < b))) + p = p->avl.right; + else if ((p->dev > dev) || ((p->dev == dev) && (p->block > e))) + p = p->avl.left; + else if (p->modified) + return p; + else + { + if (p->avl.right != NULL) + *sp++ = p->avl.right; + p = p->avl.left; + } + if ((p == NULL) && (sp > s)) + p = *--sp; + } + return p; +} + +static int +avl_insert(bdbuf_buffer **root, bdbuf_buffer *node) +{ + bdbuf_buffer **r = root; + node->avl.left = node->avl.right = NULL; + while (TRUE) + { + bdbuf_buffer *rr = *r; + if (rr == NULL) + { + *r = node; + return 0; + } + else if ((rr->dev < node->dev) || ((rr->dev == node->dev) && + (rr->block < node->block))) + { + r = &rr->avl.right; + } + else if ((rr->dev == node->dev) && (rr->block == node->block)) + { + return -1; + } + else + { + r = &rr->avl.left; + } + } +} + +static int +avl_remove(bdbuf_buffer **root, bdbuf_buffer *node) +{ + bdbuf_buffer **p = root; + dev_t dev = node->dev; + blkdev_bnum block = node->block; + + while ((*p != NULL) && (*p != node)) + { + if (((*p)->dev < dev) || (((*p)->dev == dev) && ((*p)->block < block))) + p = &(*p)->avl.right; + else + p = &(*p)->avl.left; + } + if (*p == NULL) + return -1; + + *p = node->avl.left; + while (*p != NULL) + p = &(*p)->avl.right; + *p = node->avl.right; + node->avl.left = node->avl.right = NULL; + return 0; +} + +#else + +/* The default maximum height of 32 allows for AVL trees having + between 5,704,880 and 4,294,967,295 nodes, depending on order of + insertion. You may change this compile-time constant as you + wish. */ +#ifndef AVL_MAX_HEIGHT +#define AVL_MAX_HEIGHT 32 +#endif + + + +/* + * avl_cmp_node_node -- + * Compares two avl nodes. Function compares dev/block pairs of + * the node1 and node2. + * + * PARAMETERS: + * node1 - Pointer to the first node to compare + * node2 - Pointer to the second node to compare + * + * RETURNS: + * 0 - dev/block of the nodes are equal + * 1 - dev/block of the second node are less + * -1 - dev/block of the first node are less + */ +static inline int +avl_cmp_node_node(const bdbuf_buffer *const node1, + const bdbuf_buffer *const node2) +{ + if (node1->dev < node2->dev) + return -1; + else if (node1->dev > node2->dev) + return +1; + else if (node1->block < node2->block) + return -1; + else if (node1->block > node2->block) + return +1; + else + return 0; +} + +/* + * avl_cmp_node_pattern - + * compares the dev/block of the node with specified two. + * + * PARAMETERS: + * node1 - Pointer to the node to compare + * dev/block - The pattern to compare with + * + * RETURNS: + * 0 - dev/block of the node and specified are equal + * 1 - dev/block specified are less + * -1 - dev/block of the first node are less + * + */ +static inline int +avl_cmp_node_pattern(const bdbuf_buffer *const node1, + dev_t dev, + blkdev_bnum block) +{ + if (node1->dev < dev) + return -1; + else if (node1->dev > dev) + return +1; + else if (node1->block < block) + return -1; + else if (node1->block > block) + return +1; + else + return 0; +} + + +/* + * avl_search -- + * Searches for the node with specified dev/block. + * + * PARAMETERS: + * root - pointer to the root node of the AVL-Tree. + * dev, block - search key + * + * RETURNS: + * NULL if node with specified dev/block not found + * non-NULL - pointer to the node with specified dev/block + */ +static bdbuf_buffer * +avl_search(bdbuf_buffer **root, dev_t dev, blkdev_bnum block) +{ + + bdbuf_buffer *p = *root; + while ((p != NULL) && ((p->dev != dev) || (p->block != block))) + { + if ((p->dev < dev) || ((p->dev == dev) && (p->block < block))) + p = p->avl.link[1]; + else + p = p->avl.link[0]; + } + return p; +} + +/* avl_search_for_sync -- + * Search in AVL tree for first modified buffer belongs to specified + * disk device. + * + * PARAMETERS: + * root - pointer to tree root + * dd - disk device descriptor + * + * RETURNS: + * Block buffer, or NULL if no modified blocks on specified device + * exists. + */ +static bdbuf_buffer * +avl_search_for_sync(bdbuf_buffer **root, disk_device *dd) +{ + bdbuf_buffer *p = *root; + bdbuf_buffer *s[AVL_MAX_HEIGHT]; + bdbuf_buffer **sp = s; + dev_t dev = dd->phys_dev->dev; + blkdev_bnum b = dd->start; + blkdev_bnum e = b + dd->size - 1; + + while (p != NULL) + { + if ((p->dev < dev) || ((p->dev == dev) && (p->block < b))) + p = p->avl.link[1]; + else if ((p->dev > dev) || ((p->dev == dev) && (p->block > e))) + p = p->avl.link[0]; + else if (p->modified) + return p; + else + { + if (p->avl.link[1] != NULL) + *sp++ = p->avl.link[1]; + p = p->avl.link[0]; + } + if ((p == NULL) && (sp > s)) + p = *--sp; + } + return p; +} + + +/* + * avl_insert -- + * Inserts the specified node to the AVl-Tree. + * + * PARAMETERS: + * root_addr - Pointer to pointer to the root node + * node - Pointer to the node to add. + * + * RETURNS: + * 0 - The node added successfully + * -1 - An error occured + */ +static int +avl_insert (bdbuf_buffer **root_addr, bdbuf_buffer *node) +{ + /* Uses Knuth's Algorithm 6.2.3A (balanced tree search and + insertion), but caches results of comparisons. In empirical + tests this eliminates about 25% of the comparisons seen under + random insertions. */ + + /* A1. */ + int t_modified = 0; + bdbuf_buffer *t; + bdbuf_buffer *s, *p, *q, *r; + + bdbuf_buffer *root_link = *root_addr;; + + t = root_link; + s = p = t; + + if (s == NULL) + { + q = t = node; + q->avl.link[0] = q->avl.link[1] = NULL; + q->avl.bal = 0; + *root_addr = t; + return 0; + } + + for (;;) + { + /* A2. */ + int diff = avl_cmp_node_node(node, p); + + /* A3. */ + if (diff < 0) + { + p->avl.cache = 0; + q = p->avl.link[0]; + if (q == NULL) + { + p->avl.link[0] = q = node; + break; + } + } + /* A4. */ + else + if (diff > 0) + { + p->avl.cache = 1; + q = p->avl.link[1]; + if (q == NULL) + { + p->avl.link[1] = q = node; + break; + } + } + else + /* A2. */ + { + /* + * The item found. Nothing changed. Have not to update + * root_adr*/ + + return -1; + } + + /* A3, A4. */ + if (q->avl.bal != 0) + { + t = p, s = q; + t_modified = 1; + } + p = q; + } + + /* A5. */ + q->avl.link[0] = q->avl.link[1] = NULL; + q->avl.bal = 0; + + /* A6. */ + r = p = s->avl.link[(int) s->avl.cache]; + while (p != q) + { + p->avl.bal = p->avl.cache * 2 - 1; + p = p->avl.link[(int) p->avl.cache]; + } + + /* A7. */ + if (s->avl.cache == 0) + { + /* a = -1. */ + if (s->avl.bal == 0) + { + s->avl.bal = -1; + *root_addr = root_link; + return 0; + } + else if (s->avl.bal == +1) + { + s->avl.bal = 0; + *root_addr = root_link; + return 0; + } + + assert (s->avl.bal == -1); + if (r->avl.bal == -1) + { + /* A8. */ + p = r; + s->avl.link[0] = r->avl.link[1]; + r->avl.link[1] = s; + s->avl.bal = r->avl.bal = 0; + } + else + { + /* A9. */ + assert(r->avl.bal == +1); + p = r->avl.link[1]; + r->avl.link[1] = p->avl.link[0]; + p->avl.link[0] = r; + s->avl.link[0] = p->avl.link[1]; + p->avl.link[1] = s; + if (p->avl.bal == -1) + s->avl.bal = 1, r->avl.bal = 0; + else + { + if (p->avl.bal == 0) + { + s->avl.bal = r->avl.bal = 0; + } + else + { + assert (p->avl.bal == +1); + s->avl.bal = 0, r->avl.bal = -1; + } + } + p->avl.bal = 0; + } + } + else + { + /* a == +1. */ + if (s->avl.bal == 0) + { + s->avl.bal = 1; + *root_addr = root_link; + return 0; + } + else if (s->avl.bal == -1) + { + s->avl.bal = 0; + *root_addr = root_link; + return 0; + } + + assert(s->avl.bal == +1); + if (r->avl.bal == +1) + { + /* A8. */ + p = r; + s->avl.link[1] = r->avl.link[0]; + r->avl.link[0] = s; + s->avl.bal = r->avl.bal = 0; + } + else + { + /* A9. */ + assert(r->avl.bal == -1); + p = r->avl.link[0]; + r->avl.link[0] = p->avl.link[1]; + p->avl.link[1] = r; + s->avl.link[1] = p->avl.link[0]; + p->avl.link[0] = s; + if (p->avl.bal == +1) + { + s->avl.bal = -1, r->avl.bal = 0; + } + else + { + if (p->avl.bal == 0) + { + s->avl.bal = r->avl.bal = 0; + } + else + { + assert(p->avl.bal == -1); + s->avl.bal = 0, r->avl.bal = 1; + } + } + p->avl.bal = 0; + } + } + + /* A10. */ + if (t_modified) + { + if (s == t->avl.link[1]) + t->avl.link[1] = p; + else + t->avl.link[0] = p; + } + else + { + root_link = p; + } + + *root_addr = root_link; + return 0; +} + + +/* avl_remove -- + * removes the node from the tree. + * + * PARAMETERS: + * root_addr - Pointer to pointer to the root node + * node - Pointer to the node to remove + * + * RETURNS: + * 0 - Item removed + * -1 - No such item found + */ +static int +avl_remove(bdbuf_buffer **root_addr, const bdbuf_buffer *node) +{ + /* Uses my Algorithm D, which can be found at + http://www.msu.edu/user/pfaffben/avl. Algorithm D is based on + Knuth's Algorithm 6.2.2D (Tree deletion) and 6.2.3A (Balanced + tree search and insertion), as well as the notes on pages 465-466 + of Vol. 3. */ + + /* D1. */ + bdbuf_buffer *pa[AVL_MAX_HEIGHT]; /* Stack P: Nodes. */ + char a[AVL_MAX_HEIGHT]; /* Stack P: Bits. */ + int k = 1; /* Stack P: Pointer. */ + + bdbuf_buffer **q; + bdbuf_buffer *p; + + + /* + * To avoid using unnessary instance of the 'bdbuf_buffer' (as pa[0]) + * we will catch all access to pa[0] and use &root_avl instead + */ + struct bdbuf_avl_node root_avl; + + root_avl.link[0] = *root_addr; + root_avl.link[1] = NULL; + root_avl.bal = 0; + root_avl.cache = 0; + + a[0] = 0; + + p = root_avl.link[0]; + + + k = 1; + + for (;;) + { + /* D2. */ + int diff; + + if (p == NULL) + return -1; + + diff = avl_cmp_node_node(node, p); + + if (diff == 0) + break; + + /* D3, D4. */ + pa[k] = p; + if (diff < 0) + { + p = p->avl.link[0]; + a[k] = 0; + } + else if (diff > 0) + { + p = p->avl.link[1]; + a[k] = 1; + } + k++; + } + + /* D5. */ + if (k == 1) + { + /* Andron */ + q = &root_avl.link[(int) a[k - 1]]; + } + else + { + q = &pa[k - 1]->avl.link[(int) a[k - 1]]; + } + if (p->avl.link[1] == NULL) + { + *q = p->avl.link[0]; + if (*q) + (*q)->avl.bal = 0; + } + else + { + /* D6. */ + bdbuf_buffer *r = p->avl.link[1]; + if (r->avl.link[0] == NULL) + { + r->avl.link[0] = p->avl.link[0]; + *q = r; + r->avl.bal = p->avl.bal; + a[k] = 1; + pa[k++] = r; + } + else + { + /* D7. */ + bdbuf_buffer *s = r->avl.link[0]; + int l = k++; + + a[k] = 0; + pa[k++] = r; + + /* D8. */ + while (s->avl.link[0] != NULL) + { + r = s; + s = r->avl.link[0]; + a[k] = 0; + pa[k++] = r; + } + + /* D9. */ + a[l] = 1; + pa[l] = s; + s->avl.link[0] = p->avl.link[0]; + r->avl.link[0] = s->avl.link[1]; + s->avl.link[1] = p->avl.link[1]; + s->avl.bal = p->avl.bal; + *q = s; + } + } + + assert(k > 0); + /* D10. */ + while (--k) + { + bdbuf_buffer *s = pa[k], *r; + + if (a[k] == 0) + { + /* D10. */ + if (s->avl.bal == -1) + { + s->avl.bal = 0; + continue; + } + else if (s->avl.bal == 0) + { + s->avl.bal = 1; + break; + } + + assert(s->avl.bal == +1); + r = s->avl.link[1]; + + assert(r != NULL); + if (r->avl.bal == 0) + { + /* D11. */ + s->avl.link[1] = r->avl.link[0]; + r->avl.link[0] = s; + r->avl.bal = -1; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = r; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = r; + } + break; + } + else + if (r->avl.bal == +1) + { + /* D12. */ + s->avl.link[1] = r->avl.link[0]; + r->avl.link[0] = s; + s->avl.bal = r->avl.bal = 0; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = r; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = r; + } + } + else + { + /* D13. */ + assert(r->avl.bal == -1); + p = r->avl.link[0]; + r->avl.link[0] = p->avl.link[1]; + p->avl.link[1] = r; + s->avl.link[1] = p->avl.link[0]; + p->avl.link[0] = s; + if (p->avl.bal == +1) + s->avl.bal = -1, r->avl.bal = 0; + else if (p->avl.bal == 0) + { + s->avl.bal = r->avl.bal = 0; + } + else + { + assert(p->avl.bal == -1); + s->avl.bal = 0, r->avl.bal = +1; + } + p->avl.bal = 0; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = p; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = p; + } + } + } + else + { + assert(a[k] == 1); + + /* D10. */ + if (s->avl.bal == +1) + { + s->avl.bal = 0; + continue; + } + else + if (s->avl.bal == 0) + { + s->avl.bal = -1; + break; + } + + assert(s->avl.bal == -1); + r = s->avl.link[0]; + + if (r == NULL || r->avl.bal == 0) + { + /* D11. */ + s->avl.link[0] = r->avl.link[1]; + r->avl.link[1] = s; + r->avl.bal = 1; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = r; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = r; + } + + break; + } + else + if (r->avl.bal == -1) + { + /* D12. */ + s->avl.link[0] = r->avl.link[1]; + r->avl.link[1] = s; + s->avl.bal = r->avl.bal = 0; + if (k == 1) + { + root_avl.link[(int) a[k - 1]] = r; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = r; + } + + } + else + if (r->avl.bal == +1) + { + /* D13. */ + p = r->avl.link[1]; + r->avl.link[1] = p->avl.link[0]; + p->avl.link[0] = r; + s->avl.link[0] = p->avl.link[1]; + p->avl.link[1] = s; + if (p->avl.bal == -1) + s->avl.bal = 1, r->avl.bal = 0; + else + if (p->avl.bal == 0) + s->avl.bal = r->avl.bal = 0; + else + { + assert(p->avl.bal == 1); + s->avl.bal = 0, r->avl.bal = -1; + } + p->avl.bal = 0; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = p; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = p; + } + } + } + } + + *root_addr = root_avl.link[0]; + return 0; +} + +#endif + +/* bdbuf_initialize_pool -- + * Initialize single buffer pool. + * + * PARAMETERS: + * config - buffer pool configuration + * pool - pool number + * + * RETURNS: + * RTEMS_SUCCESSFUL, if buffer pool initialized successfully, or error + * code if error occured. + */ +static rtems_status_code +bdbuf_initialize_pool(rtems_bdbuf_config *config, int pool) +{ + bdbuf_pool *p = bd_ctx.pool + pool; + unsigned char *bufs; + bdbuf_buffer *b; + rtems_status_code rc; + int i; + + p->blksize = config->size; + p->nblks = config->num; + p->tree = NULL; + + Chain_Initialize_empty(&p->free); + Chain_Initialize_empty(&p->lru); + + /* Allocate memory for buffer descriptors */ + p->bdbufs = calloc(config->num, sizeof(bdbuf_buffer)); + if (p->bdbufs == NULL) + { + return RTEMS_NO_MEMORY; + } + + /* Allocate memory for buffers if required */ + if (config->mem_area == NULL) + { + bufs = p->mallocd_bufs = malloc(config->num * config->size); + if (bufs == NULL) + { + free(p->bdbufs); + return RTEMS_NO_MEMORY; + } + } + else + { + bufs = config->mem_area; + p->mallocd_bufs = NULL; + } + + for (i = 0, b = p->bdbufs; i < p->nblks; i++, b++, bufs += p->blksize) + { + b->dev = -1; b->block = 0; + b->buffer = bufs; + b->actual = b->modified = b->in_progress = FALSE; + b->use_count = 0; + b->pool = pool; + _Chain_Append(&p->free, &b->link); + } + + rc = rtems_semaphore_create( + rtems_build_name('B', 'U', 'F', 'G'), + p->nblks, + RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, + 0, + &p->bufget_sema); + + if (rc != RTEMS_SUCCESSFUL) + { + free(p->bdbufs); + free(p->mallocd_bufs); + return rc; + } + + return RTEMS_SUCCESSFUL; +} + +/* bdbuf_release_pool -- + * Free resources allocated for buffer pool with specified number. + * + * PARAMETERS: + * pool - buffer pool number + * + * RETURNS: + * RTEMS_SUCCESSFUL + */ +static rtems_status_code +bdbuf_release_pool(rtems_bdpool_id pool) +{ + bdbuf_pool *p = bd_ctx.pool + pool; + rtems_semaphore_delete(p->bufget_sema); + free(p->bdbufs); + free(p->mallocd_bufs); + return RTEMS_SUCCESSFUL; +} + +/* rtems_bdbuf_init -- + * Prepare buffering layer to work - initialize buffer descritors + * and (if it is neccessary)buffers. Buffers will be allocated accoriding + * to the configuration table, each entry describes kind of block and + * amount requested. After initialization all blocks is placed into + * free elements lists. + * + * PARAMETERS: + * conf_table - pointer to the buffers configuration table + * size - number of entries in configuration table + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + */ +rtems_status_code +rtems_bdbuf_init(rtems_bdbuf_config *conf_table, int size) +{ + rtems_bdpool_id i; + rtems_status_code rc; + + if (size <= 0) + return RTEMS_INVALID_SIZE; + + bd_ctx.npools = size; + + /* + * Allocate memory for buffer pool descriptors + */ + bd_ctx.pool = calloc(size, sizeof(bdbuf_pool)); + if (bd_ctx.pool == NULL) + { + return RTEMS_NO_MEMORY; + } + + Chain_Initialize_empty(&bd_ctx.mod); + + /* Initialize buffer pools and roll out if something failed */ + for (i = 0; i < size; i++) + { + rc = bdbuf_initialize_pool(conf_table + i, i); + if (rc != RTEMS_SUCCESSFUL) + { + rtems_bdpool_id j; + for (j = 0; j < i - 1; j++) + { + bdbuf_release_pool(j); + } + return rc; + } + } + + /* Create buffer flush semaphore */ + rc = rtems_semaphore_create( + rtems_build_name('B', 'F', 'L', 'U'), 0, + RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, + &bd_ctx.flush_sema); + if (rc != RTEMS_SUCCESSFUL) + { + for (i = 0; i < size; i++) + bdbuf_release_pool(i); + free(bd_ctx.pool); + return rc; + } + + /* Create and start swapout task */ + rc = rtems_task_create( + rtems_build_name('B', 'S', 'W', 'P'), + SWAPOUT_PRIORITY, + SWAPOUT_STACK_SIZE, + RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT, + RTEMS_DEFAULT_ATTRIBUTES, + &bd_ctx.swapout_task); + if (rc != RTEMS_SUCCESSFUL) + { + rtems_semaphore_delete(bd_ctx.flush_sema); + for (i = 0; i < size; i++) + bdbuf_release_pool(i); + free(bd_ctx.pool); + return rc; + } + + rc = rtems_task_start(bd_ctx.swapout_task, bdbuf_swapout_task, 0); + if (rc != RTEMS_SUCCESSFUL) + { + rtems_task_delete(bd_ctx.swapout_task); + rtems_semaphore_delete(bd_ctx.flush_sema); + for (i = 0; i < size; i++) + bdbuf_release_pool(i); + free(bd_ctx.pool); + return rc; + } + + return RTEMS_SUCCESSFUL; +} + +/* find_or_assign_buffer -- + * Looks for buffer already assigned for this dev/block. If one is found + * obtain block buffer. If specified block already cached (i.e. there's + * block in the _modified_, or _recently_used_), return address + * of appropriate buffer descriptor and increment reference counter to 1. + * If block is not cached, allocate new buffer and return it. Data + * shouldn't be read to the buffer from media; buffer contains arbitrary + * data. This primitive may be blocked if there are no free buffer + * descriptors available and there are no unused non-modified (or + * synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (constructed of major and minor device number + * block - linear media block number + * ret_buf - address of the variable to store address of found descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFEECTS: + * bufget_sema may be obtained by this primitive + * + * NOTE: + * It is assumed that primitive invoked when thread preemption is disabled. + */ +static rtems_status_code +find_or_assign_buffer(disk_device *dd, + blkdev_bnum block, + bdbuf_buffer **ret_buf) +{ + bdbuf_buffer *bd_buf; + bdbuf_pool *bd_pool; + rtems_status_code rc; + dev_t device; + ISR_Level level; + + int blksize; + + device = dd->dev; + bd_pool = bd_ctx.pool + dd->pool; + blksize = dd->block_size; + +again: + /* Looking for buffer descriptor used for this dev/block. */ + bd_buf = avl_search(&bd_pool->tree, device, block); + + if (bd_buf == NULL) + { + /* Try to obtain semaphore without waiting first. It is the most + frequent case when reasonable number of buffers configured. If + it is failed, obtain semaphore blocking on it. In this case + it should be checked that appropriate buffer hasn't been loaded + by another thread, because this thread is preempted */ + rc = rtems_semaphore_obtain(bd_pool->bufget_sema, RTEMS_NO_WAIT, 0); + if (rc == RTEMS_UNSATISFIED) + { + rc = rtems_semaphore_obtain(bd_pool->bufget_sema, + RTEMS_WAIT, RTEMS_NO_TIMEOUT); + bd_buf = avl_search(&bd_pool->tree, device, block); + if (bd_buf != NULL) + rtems_semaphore_release(bd_pool->bufget_sema); + } + } + + if (bd_buf == NULL) + { + /* Assign new buffer descriptor */ + if (_Chain_Is_empty(&bd_pool->free)) + { + bd_buf = (bdbuf_buffer *)Chain_Get(&bd_pool->lru); + if (bd_buf != NULL) + { + int avl_result; + avl_result = avl_remove(&bd_pool->tree, bd_buf); + if (avl_result != 0) + { + rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY); + return RTEMS_INTERNAL_ERROR; + } + } + } + else + { + bd_buf = (bdbuf_buffer *)Chain_Get(&(bd_pool->free)); + } + + if (bd_buf == NULL) + { + goto again; + } + else + { + bd_buf->dev = device; + bd_buf->block = block; +#ifdef BINARY_TREE + bd_buf->avl.left = NULL; + bd_buf->avl.right = NULL; +#else + bd_buf->avl.link[0] = NULL; + bd_buf->avl.link[1] = NULL; +#endif + bd_buf->use_count = 1; + bd_buf->modified = bd_buf->actual = bd_buf->in_progress = FALSE; + bd_buf->status = RTEMS_SUCCESSFUL; + + if (avl_insert(&bd_pool->tree, bd_buf) != 0) + { + rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY); + return RTEMS_INTERNAL_ERROR; + } + + *ret_buf = bd_buf; + + return RTEMS_SUCCESSFUL; + } + } + else + { + /* Buffer descriptor already assigned for this dev/block */ + if (bd_buf->use_count == 0) + { + /* If we are removing from lru list, obtain the bufget_sema + * first. If we are removing from mod list, obtain flush sema. + * It should be obtained without blocking because we know + * that our buffer descriptor is in the list. */ + if (bd_buf->modified) + { + rc = rtems_semaphore_obtain(bd_ctx.flush_sema, + RTEMS_NO_WAIT, 0); + } + else + { + rc = rtems_semaphore_obtain(bd_pool->bufget_sema, + RTEMS_NO_WAIT, 0); + } + /* It is possible that we couldn't obtain flush or bufget sema + * although buffer in the appropriate chain is available: + * semaphore may be released to swapout task, but this task + * actually did not start to process it. */ + if (rc == RTEMS_UNSATISFIED) + rc = RTEMS_SUCCESSFUL; + if (rc != RTEMS_SUCCESSFUL) + { + rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY); + return RTEMS_INTERNAL_ERROR; + } + + /* Buffer descriptor is linked to the lru or mod chain. Remove + it from there. */ + Chain_Extract(&bd_buf->link); + } + bd_buf->use_count++; + while (bd_buf->in_progress != 0) + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, + WATCHDOG_NO_TIMEOUT, level); + } + + *ret_buf = bd_buf; + return RTEMS_SUCCESSFUL; + } +} + +/* rtems_bdbuf_get -- + * Obtain block buffer. If specified block already cached (i.e. there's + * block in the _modified_, or _recently_used_), return address + * of appropriate buffer descriptor and increment reference counter to 1. + * If block is not cached, allocate new buffer and return it. Data + * shouldn't be read to the buffer from media; buffer may contains + * arbitrary data. This primitive may be blocked if there are no free + * buffer descriptors available and there are no unused non-modified + * (or synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (constructed of major and minor device number) + * block - linear media block number + * bd - address of variable to store pointer to the buffer descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * bufget_sema semaphore obtained by this primitive. + */ +rtems_status_code +rtems_bdbuf_get(dev_t device, blkdev_bnum block, bdbuf_buffer **bd) +{ + rtems_status_code rc; + disk_device *dd; + disk_device *pdd; + preemption_key key; + + /* + * Convert logical dev/block to physical one + */ + dd = rtems_disk_lookup(device); + if (dd == NULL) + return RTEMS_INVALID_ID; + + if (block >= dd->size) + { + rtems_disk_release(dd); + return RTEMS_INVALID_NUMBER; + } + + pdd = dd->phys_dev; + block += dd->start; + rtems_disk_release(dd); + + DISABLE_PREEMPTION(key); + rc = find_or_assign_buffer(pdd, block, bd); + ENABLE_PREEMPTION(key); + + if (rc != RTEMS_SUCCESSFUL) + return rc; + + return RTEMS_SUCCESSFUL; +} + +/* bdbuf_initialize_transfer_sema -- + * Initialize transfer_sema mutex semaphore associated with buffer + * descriptor. + */ +static inline void +bdbuf_initialize_transfer_sema(bdbuf_buffer *bd_buf) +{ + CORE_mutex_Attributes mutex_attr; + mutex_attr.lock_nesting_behavior = CORE_MUTEX_NESTING_BLOCKS; + mutex_attr.only_owner_release = FALSE; + mutex_attr.discipline = CORE_MUTEX_DISCIPLINES_FIFO; + mutex_attr.priority_ceiling = 0; + + _CORE_mutex_Initialize(&bd_buf->transfer_sema, OBJECTS_NO_CLASS, + &mutex_attr, CORE_MUTEX_LOCKED, NULL); +} + +/* bdbuf_write_transfer_done -- + * Callout function. Invoked by block device driver when data transfer + * to device (write) is completed. This function may be invoked from + * interrupt handler. + * + * PARAMETERS: + * arg - arbitrary argument specified in block device request + * structure (in this case - pointer to the appropriate + * bdbuf_buffer buffer descriptor structure). + * status - I/O completion status + * error - errno error code if status != RTEMS_SUCCESSFUL + * + * RETURNS: + * none + */ +static void +bdbuf_write_transfer_done(void *arg, rtems_status_code status, int error) +{ + bdbuf_buffer *bd_buf = arg; + bd_buf->status = status; + bd_buf->error = error; + bd_buf->in_progress = bd_buf->modified = FALSE; + _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL); + _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL, + CORE_MUTEX_STATUS_SUCCESSFUL); +} + +/* bdbuf_read_transfer_done -- + * Callout function. Invoked by block device driver when data transfer + * from device (read) is completed. This function may be invoked from + * interrupt handler. + * + * PARAMETERS: + * arg - arbitrary argument specified in block device request + * structure (in this case - pointer to the appropriate + * bdbuf_buffer buffer descriptor structure). + * status - I/O completion status + * error - errno error code if status != RTEMS_SUCCESSFUL + * + * RETURNS: + * none + */ +static void +bdbuf_read_transfer_done(void *arg, rtems_status_code status, int error) +{ + bdbuf_buffer *bd_buf = arg; + bd_buf->status = status; + bd_buf->error = error; + _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL); + _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL, + CORE_MUTEX_STATUS_SUCCESSFUL); +} + +/* rtems_bdbuf_read -- + * (Similar to the rtems_bdbuf_get, except reading data from media) + * Obtain block buffer. If specified block already cached, return address + * of appropriate buffer and increment reference counter to 1. If block is + * not cached, allocate new buffer and read data to it from the media. + * This primitive may be blocked on waiting until data to be read from + * media, if there are no free buffer descriptors available and there are + * no unused non-modified (or synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (consists of major and minor device number) + * block - linear media block number + * bd - address of variable to store pointer to the buffer descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * bufget_sema and transfer_sema semaphores obtained by this primitive. + */ +rtems_status_code +rtems_bdbuf_read(dev_t device, + blkdev_bnum block, + bdbuf_buffer **bd) +{ + preemption_key key; + ISR_Level level; + + bdbuf_buffer *bd_buf; + rtems_status_code rc; + int result; + disk_device *dd; + disk_device *pdd; + blkdev_request1 req; + + dd = rtems_disk_lookup(device); + if (dd == NULL) + return RTEMS_INVALID_ID; + + if (block >= dd->size) + { + rtems_disk_release(dd); + return RTEMS_INVALID_NUMBER; + } + + pdd = dd->phys_dev; + block += dd->start; + + DISABLE_PREEMPTION(key); + rc = find_or_assign_buffer(pdd, block, &bd_buf); + + if (rc != RTEMS_SUCCESSFUL) + { + ENABLE_PREEMPTION(key); + rtems_disk_release(dd); + return rc; + } + + if (!bd_buf->actual) + { + bd_buf->in_progress = 1; + + req.req.req = BLKDEV_REQ_READ; + req.req.req_done = bdbuf_read_transfer_done; + req.req.done_arg = bd_buf; + req.req.start = block; + req.req.count = 1; + req.req.bufnum = 1; + req.req.bufs[0].length = dd->block_size; + req.req.bufs[0].buffer = bd_buf->buffer; + + bdbuf_initialize_transfer_sema(bd_buf); + result = dd->ioctl(device, BLKIO_REQUEST, &req); + if (result == -1) + { + bd_buf->status = RTEMS_IO_ERROR; + bd_buf->error = errno; + bd_buf->actual = FALSE; + } + else + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, + WATCHDOG_NO_TIMEOUT, level); + bd_buf->actual = TRUE; + } + bd_buf->in_progress = FALSE; + } + rtems_disk_release(dd); + + ENABLE_PREEMPTION(key); + + *bd = bd_buf; + + return RTEMS_SUCCESSFUL; +} + + +/* bdbuf_release -- + * Release buffer. Decrease buffer usage counter. If it is zero, further + * processing depends on modified attribute. If buffer was modified, it + * is inserted into mod chain and swapout task waken up. If buffer was + * not modified, it is returned to the end of lru chain making it available + * for further use. + * + * PARAMETERS: + * bd_buf - pointer to the released buffer descriptor. + * + * RETURNS: + * RTEMS_SUCCESSFUL if buffer released successfully, or error code if + * error occured. + * + * NOTE: + * This is internal function. It is assumed that task made non-preemptive + * before its invocation. + */ +static rtems_status_code +bdbuf_release(bdbuf_buffer *bd_buf) +{ + bdbuf_pool *bd_pool; + rtems_status_code rc = RTEMS_SUCCESSFUL; + + if (bd_buf->use_count <= 0) + return RTEMS_INTERNAL_ERROR; + + bd_pool = bd_ctx.pool + bd_buf->pool; + + bd_buf->use_count--; + + if (bd_buf->use_count == 0) + { + if (bd_buf->modified) + { + + /* Buffer was modified. Insert buffer to the modified buffers + * list and initiate flushing. */ + Chain_Append(&bd_ctx.mod, &bd_buf->link); + + /* Release the flush_sema */ + rc = rtems_semaphore_release(bd_ctx.flush_sema); + } + else + { + /* Buffer was not modified. Add this descriptor to the + * end of lru chain and make it available for reuse. */ + Chain_Append(&bd_pool->lru, &bd_buf->link); + rc = rtems_semaphore_release(bd_pool->bufget_sema); + } + } + return rc; +} + + +/* rtems_bdbuf_release -- + * Release buffer allocated before. This primitive decrease the + * usage counter. If it is zero, further destiny of buffer depends on + * 'modified' status. If buffer was modified, it is placed to the end of + * mod list and flush task waken up. If buffer was not modified, + * it is placed to the end of lru list, and bufget_sema released, allowing + * to reuse this buffer. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * flush_sema and bufget_sema semaphores may be released by this primitive. + */ +rtems_status_code +rtems_bdbuf_release(bdbuf_buffer *bd_buf) +{ + preemption_key key; + rtems_status_code rc = RTEMS_SUCCESSFUL; + + if (bd_buf == NULL) + return RTEMS_INVALID_ADDRESS; + + DISABLE_PREEMPTION(key); + + rc = bdbuf_release(bd_buf); + + ENABLE_PREEMPTION(key); + + return rc; +} + +/* rtems_bdbuf_release_modified -- + * Release buffer allocated before, assuming that it is _modified_ by + * it's owner. This primitive decrease usage counter for buffer, mark + * buffer descriptor as modified. If usage counter is 0, insert it at + * end of mod chain and release flush_sema semaphore to activate the + * flush task. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * flush_sema semaphore may be released by this primitive. + */ +rtems_status_code +rtems_bdbuf_release_modified(bdbuf_buffer *bd_buf) +{ + preemption_key key; + rtems_status_code rc = RTEMS_SUCCESSFUL; + + if (bd_buf == NULL) + return RTEMS_INVALID_ADDRESS; + + DISABLE_PREEMPTION(key); + + if (!bd_buf->modified) + { + bdbuf_initialize_transfer_sema(bd_buf); + } + bd_buf->modified = TRUE; + bd_buf->actual = TRUE; + rc = bdbuf_release(bd_buf); + + ENABLE_PREEMPTION(key); + + return rc; +} + +/* rtems_bdbuf_sync -- + * Wait until specified buffer synchronized with disk. Invoked on exchanges + * critical for data consistency on the media. This primitive mark owned + * block as modified, decrease usage counter. If usage counter is 0, + * block inserted to the mod chain and flush_sema semaphore released. + * Finally, primitives blocked on transfer_sema semaphore. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * Primitive may be blocked on transfer_sema semaphore. + */ +rtems_status_code +rtems_bdbuf_sync(bdbuf_buffer *bd_buf) +{ + preemption_key key; + ISR_Level level; + rtems_status_code rc = RTEMS_SUCCESSFUL; + + if (bd_buf == NULL) + return RTEMS_INVALID_ADDRESS; + + DISABLE_PREEMPTION(key); + + if (!bd_buf->modified) + { + bdbuf_initialize_transfer_sema(bd_buf); + } + bd_buf->modified = TRUE; + bd_buf->actual = TRUE; + + rc = bdbuf_release(bd_buf); + + if (rc == RTEMS_SUCCESSFUL) + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, + WATCHDOG_NO_TIMEOUT, level); + } + + ENABLE_PREEMPTION(key); + + return rc; +} + +/* rtems_bdbuf_syncdev -- + * Synchronize with disk all buffers containing the blocks belonging to + * specified device. + * + * PARAMETERS: + * dev - block device number + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + */ +rtems_status_code +rtems_bdbuf_syncdev(dev_t dev) +{ + preemption_key key; + ISR_Level level; + + bdbuf_buffer *bd_buf; + disk_device *dd; + bdbuf_pool *pool; + + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_ID; + + pool = bd_ctx.pool + dd->pool; + + DISABLE_PREEMPTION(key); + do { + bd_buf = avl_search_for_sync(&pool->tree, dd); + if (bd_buf != NULL /* && bd_buf->modified */) + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, + WATCHDOG_NO_TIMEOUT, level); + } + } while (bd_buf != NULL); + ENABLE_PREEMPTION(key); + return rtems_disk_release(dd); +} + +/* bdbuf_swapout_task -- + * Body of task which take care on flushing modified buffers to the + * disk. + */ +static rtems_task +bdbuf_swapout_task(rtems_task_argument unused) +{ + rtems_status_code rc; + int result; + ISR_Level level; + bdbuf_buffer *bd_buf; + bdbuf_pool *bd_pool; + disk_device *dd; + blkdev_request1 req; + + while (1) + { + rc = rtems_semaphore_obtain(bd_ctx.flush_sema, RTEMS_WAIT, 0); + if (rc != RTEMS_SUCCESSFUL) + { + rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_SWAPOUT); + } + + bd_buf = (bdbuf_buffer *)Chain_Get(&bd_ctx.mod); + if (bd_buf == NULL) + { + /* It is possible that flush_sema semaphore will be released, but + * buffer to be removed from mod chain before swapout task start + * its processing. */ + continue; + } + + bd_buf->in_progress = TRUE; + bd_buf->use_count++; + bd_pool = bd_ctx.pool + bd_buf->pool; + dd = rtems_disk_lookup(bd_buf->dev); + + req.req.req = BLKDEV_REQ_WRITE; + req.req.req_done = bdbuf_write_transfer_done; + req.req.done_arg = bd_buf; + req.req.start = bd_buf->block + dd->start; + req.req.count = 1; + req.req.bufnum = 1; + req.req.bufs[0].length = dd->block_size; + req.req.bufs[0].buffer = bd_buf->buffer; + + /* transfer_sema initialized when bd_buf inserted in the mod chain + first time */ + result = dd->ioctl(dd->phys_dev->dev, BLKIO_REQUEST, &req); + + rtems_disk_release(dd); + + if (result == -1) + { + bd_buf->status = RTEMS_IO_ERROR; + bd_buf->error = errno; + /* Release tasks waiting on syncing this buffer */ + _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL, + CORE_MUTEX_STATUS_SUCCESSFUL); + } + else + { + if (bd_buf->in_progress) + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, 0, level); + } + } + bd_buf->use_count--; + + /* Another task have chance to use this buffer, or even + * modify it. If buffer is not in use, insert it in appropriate chain + * and release semaphore */ + if (bd_buf->use_count == 0) + { + if (bd_buf->modified) + { + Chain_Append(&bd_ctx.mod, &bd_buf->link); + rc = rtems_semaphore_release(bd_ctx.flush_sema); + } + else + { + Chain_Append(&bd_pool->lru, &bd_buf->link); + rc = rtems_semaphore_release(bd_pool->bufget_sema); + } + } + } +} + +/* rtems_bdbuf_find_pool -- + * Find first appropriate buffer pool. This primitive returns the index + * of first buffer pool which block size is greater than or equal to + * specified size. + * + * PARAMETERS: + * block_size - requested block size + * pool - placeholder for result + * + * RETURNS: + * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully, + * RTEMS_INVALID_SIZE if specified block size is invalid (not a power + * of 2), RTEMS_NOT_DEFINED if buffer pool for this or greater block size + * is not configured. + */ +rtems_status_code +rtems_bdbuf_find_pool(int block_size, rtems_bdpool_id *pool) +{ + rtems_bdpool_id i; + bdbuf_pool *p; + int cursize = INT_MAX; + rtems_bdpool_id curid = -1; + rtems_boolean found = FALSE; + int j; + + for (j = block_size; (j != 0) && ((j & 1) == 0); j >>= 1); + if (j != 1) + return RTEMS_INVALID_SIZE; + + for (i = 0, p = bd_ctx.pool; i < bd_ctx.npools; i++, p++) + { + if ((p->blksize >= block_size) && + (p->blksize < cursize)) + { + curid = i; + cursize = p->blksize; + found = TRUE; + } + } + + if (found) + { + if (pool != NULL) + *pool = curid; + return RTEMS_SUCCESSFUL; + } + else + { + return RTEMS_NOT_DEFINED; + } +} + +/* rtems_bdbuf_get_pool_info -- + * Obtain characteristics of buffer pool with specified number. + * + * PARAMETERS: + * pool - buffer pool number + * block_size - block size for which buffer pool is configured returned + * there + * blocks - number of buffers in buffer pool returned there + * + * RETURNS: + * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully, + * RTEMS_INVALID_NUMBER if appropriate buffer pool is not configured. + * + * NOTE: + * Buffer pools enumerated contiguously starting from 0. + */ +rtems_status_code +rtems_bdbuf_get_pool_info(rtems_bdpool_id pool, int *block_size, + int *blocks) +{ + if (pool >= bd_ctx.npools) + return RTEMS_INVALID_NUMBER; + + if (block_size != NULL) + { + *block_size = bd_ctx.pool[pool].blksize; + } + + if (blocks != NULL) + { + *blocks = bd_ctx.pool[pool].nblks; + } + + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/exec/libblock/src/blkdev.c b/c/src/exec/libblock/src/blkdev.c new file mode 100644 index 0000000000..1004ec4de3 --- /dev/null +++ b/c/src/exec/libblock/src/blkdev.c @@ -0,0 +1,243 @@ +/* + * blkdev.h - block device driver generic support + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#include +#include +#include + +#include "rtems/diskdevs.h" +#include "rtems/bdbuf.h" + +/* rtems_blkdev_generic_read -- + * Generic block device read primitive. Implemented using block device + * buffer management primitives. + */ +rtems_device_driver +rtems_blkdev_generic_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + rtems_libio_rw_args_t *args = arg; + int block_size_log2; + int block_size; + char *buf; + unsigned int count; + unsigned int block; + unsigned int blkofs; + dev_t dev; + disk_device *dd; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + block_size_log2 = dd->block_size_log2; + block_size = dd->block_size; + + buf = args->buffer; + count = args->count; + args->bytes_moved = 0; + + block = args->offset >> block_size_log2; + blkofs = args->offset & (block_size - 1); + + while (count > 0) + { + bdbuf_buffer *diskbuf; + int copy; + rtems_status_code rc; + + rc = rtems_bdbuf_read(dev, block, &diskbuf); + if (rc != RTEMS_SUCCESSFUL) + return rc; + copy = block_size - blkofs; + if (copy > count) + copy = count; + memcpy(buf, (char *)diskbuf->buffer + blkofs, copy); + rc = rtems_bdbuf_release(diskbuf); + args->bytes_moved += copy; + if (rc != RTEMS_SUCCESSFUL) + return rc; + count -= copy; + buf += copy; + blkofs = 0; + block++; + } + return RTEMS_SUCCESSFUL; +} + +/* rtems_blkdev_generic_write -- + * Generic block device write primitive. Implemented using block device + * buffer management primitives. + */ +rtems_device_driver +rtems_blkdev_generic_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + rtems_libio_rw_args_t *args = arg; + int block_size_log2; + int block_size; + char *buf; + unsigned int count; + unsigned int block; + unsigned int blkofs; + dev_t dev; + rtems_status_code rc; + disk_device *dd; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + block_size_log2 = dd->block_size_log2; + block_size = dd->block_size; + + buf = args->buffer; + count = args->count; + args->bytes_moved = 0; + + block = args->offset >> block_size_log2; + blkofs = args->offset & (block_size - 1); + + while (count > 0) + { + bdbuf_buffer *diskbuf; + int copy; + + if ((blkofs == 0) && (count > block_size)) + rc = rtems_bdbuf_get(dev, block, &diskbuf); + else + rc = rtems_bdbuf_read(dev, block, &diskbuf); + if (rc != RTEMS_SUCCESSFUL) + return rc; + + copy = block_size - blkofs; + if (copy > count) + copy = count; + memcpy((char *)diskbuf->buffer + blkofs, buf, copy); + args->bytes_moved += copy; + + rc = rtems_bdbuf_release_modified(diskbuf); + if (rc != RTEMS_SUCCESSFUL) + return rc; + + count -= copy; + buf += copy; + blkofs = 0; + block++; + } + return RTEMS_SUCCESSFUL; +} + +/* blkdev_generic_open -- + * Generic block device open primitive. + */ +rtems_device_driver +rtems_blkdev_generic_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + dev_t dev; + disk_device *dd; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + dd->uses++; + + rtems_disk_release(dd); + + return RTEMS_SUCCESSFUL; +} + + +/* blkdev_generic_close -- + * Generic block device close primitive. + */ +rtems_device_driver +rtems_blkdev_generic_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + dev_t dev; + disk_device *dd; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + dd->uses--; + + rtems_disk_release(dd); + + return RTEMS_SUCCESSFUL; +} + +/* blkdev_generic_ioctl -- + * Generic block device ioctl primitive. + */ +rtems_device_driver +rtems_blkdev_generic_ioctl( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + rtems_libio_ioctl_args_t *args = arg; + dev_t dev; + disk_device *dd; + int rc; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + switch (args->command) + { + case BLKIO_GETBLKSIZE: + args->ioctl_return = dd->block_size; + break; + + case BLKIO_GETSIZE: + args->ioctl_return = dd->size; + break; + + case BLKIO_SYNCDEV: + rc = rtems_bdbuf_syncdev(dd->dev); + args->ioctl_return = (rc == RTEMS_SUCCESSFUL ? 0 : -1); + break; + + case BLKIO_REQUEST: + { + blkdev_request *req = args->buffer; + req->start += dd->start; + args->ioctl_return = dd->ioctl(dd->phys_dev->dev, args->command, + req); + break; + } + + default: + args->ioctl_return = dd->ioctl(dd->phys_dev->dev, args->command, + args->buffer); + break; + } + rtems_disk_release(dd); + + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/exec/libblock/src/diskdevs.c b/c/src/exec/libblock/src/diskdevs.c new file mode 100644 index 0000000000..a9d6035e8b --- /dev/null +++ b/c/src/exec/libblock/src/diskdevs.c @@ -0,0 +1,631 @@ +/* + * diskdevs.c - Physical and logical block devices (disks) support + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + + +#include +#include +#include +#include + +#include "rtems/diskdevs.h" +#include "rtems/bdbuf.h" + +#define DISKTAB_INITIAL_SIZE 32 + +/* Table of disk devices having the same major number */ +struct disk_device_table { + disk_device **minor; /* minor-indexed disk device table */ + int size; /* Number of entries in the table */ +}; + +/* Pointer to [major].minor[minor] indexed array of disk devices */ +static struct disk_device_table *disktab; + +/* Number of allocated entries in disktab table */ +static int disktab_size; + +/* Mutual exclusion semaphore for disk devices table */ +static rtems_id diskdevs_mutex; + +/* Flag meaning that disk I/O, buffering etc. already has been initialized. */ +static boolean disk_io_initialized = FALSE; + +/* diskdevs data structures protection flag. + * Normally, only table lookup operations performed. It is quite fast, so + * it is possible to done lookups when interrupts are disabled, avoiding + * obtaining the semaphore. This flags sets immediately after entering in + * mutex-protected section and cleared before leaving this section in + * "big" primitives like add/delete new device etc. Lookup function first + * disable interrupts and check this flag. If it is set, lookup function + * will be blocked on semaphore and lookup operation will be performed in + * semaphore-protected code. If it is not set (very-very frequent case), + * we can do lookup safely, enable interrupts and return result. + */ +static volatile rtems_boolean diskdevs_protected; + +/* create_disk_entry -- + * Return pointer to the disk_entry structure for the specified device, or + * create one if it is not exists. + * + * PARAMETERS: + * dev - device id (major, minor) + * + * RETURNS: + * pointer to the disk device descirptor entry, or NULL if no memory + * available for its creation. + */ +static disk_device * +create_disk_entry(dev_t dev) +{ + rtems_device_major_number major; + rtems_device_minor_number minor; + struct disk_device **d; + + rtems_filesystem_split_dev_t (dev, major, minor); + + if (major >= disktab_size) + { + struct disk_device_table *p; + int newsize; + int i; + newsize = disktab_size * 2; + if (major >= newsize) + newsize = major + 1; + p = realloc(disktab, sizeof(struct disk_device_table) * newsize); + if (p == NULL) + return NULL; + p += disktab_size; + for (i = disktab_size; i < newsize; i++, p++) + { + p->minor = NULL; + p->size = 0; + } + disktab_size = newsize; + } + + if ((disktab[major].minor == NULL) || + (minor >= disktab[major].size)) + { + int newsize; + disk_device **p; + int i; + int s = disktab[major].size; + + if (s == 0) + newsize = DISKTAB_INITIAL_SIZE; + else + newsize = s * 2; + if (minor >= newsize) + newsize = minor + 1; + + p = realloc(disktab[major].minor, sizeof(disk_device *) * newsize); + if (p == NULL) + return NULL; + disktab[major].minor = p; + p += s; + for (i = s; i < newsize; i++, p++) + *p = NULL; + disktab[major].size = newsize; + } + + d = disktab[major].minor + minor; + if (*d == NULL) + { + *d = calloc(1, sizeof(disk_device)); + } + return *d; +} + +/* get_disk_entry -- + * Get disk device descriptor by device number. + * + * PARAMETERS: + * dev - block device number + * + * RETURNS: + * Pointer to the disk device descriptor corresponding to the specified + * device number, or NULL if disk device with such number not exists. + */ +static inline disk_device * +get_disk_entry(dev_t dev) +{ + rtems_device_major_number major; + rtems_device_minor_number minor; + struct disk_device_table *dtab; + + rtems_filesystem_split_dev_t (dev, major, minor); + + if ((major >= disktab_size) || (disktab == NULL)) + return NULL; + + dtab = disktab + major; + + if ((minor >= dtab->size) || (dtab->minor == NULL)) + return NULL; + + return dtab->minor[minor]; +} + +/* create_disk -- + * Check that disk entry for specified device number is not defined + * and create it. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * name - character name of device (e.g. /dev/hda) + * disdev - placeholder for pointer to created disk descriptor + * + * RETURNS: + * RTEMS_SUCCESSFUL if disk entry successfully created, or + * error code if error occured (device already registered, + * no memory available). + */ +static rtems_status_code +create_disk(dev_t dev, char *name, disk_device **diskdev) +{ + disk_device *dd; + char *n; + + dd = get_disk_entry(dev); + if (dd != NULL) + { + return RTEMS_RESOURCE_IN_USE; + } + + if (name == NULL) + { + n = NULL; + } + else + { + int nlen = strlen(name) + 1; + n = malloc(nlen); + if (n == NULL) + return RTEMS_NO_MEMORY; + strncpy(n, name, nlen); + } + + dd = create_disk_entry(dev); + if (dd == NULL) + { + free(n); + return RTEMS_NO_MEMORY; + } + + dd->dev = dev; + dd->name = n; + + *diskdev = dd; + + return RTEMS_SUCCESSFUL; +} + +/* rtems_disk_create_phys -- + * Create physical disk entry. This function usually invoked from + * block device driver initialization code when physical device + * detected in the system. Device driver should provide ioctl handler + * to allow block device access operations. This primitive will register + * device in rtems (invoke rtems_io_register_name). + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * block_size - size of disk block (minimum data transfer unit); must be + * power of 2 + * disk_size - number of blocks on device + * handler - IOCTL handler (function providing basic block input/output + * request handling BIOREQUEST and other device management + * operations) + * name - character name of device (e.g. /dev/hda) + * + * RETURNS: + * RTEMS_SUCCESSFUL if information about new physical disk added, or + * error code if error occured (device already registered, wrong block + * size value, no memory available). + */ +rtems_status_code +rtems_disk_create_phys(dev_t dev, int block_size, int disk_size, + block_device_ioctl handler, + char *name) +{ + int bs_log2; + int i; + disk_device *dd; + rtems_status_code rc; + rtems_bdpool_id pool; + rtems_device_major_number major; + rtems_device_minor_number minor; + + rtems_filesystem_split_dev_t (dev, major, minor); + + + for (bs_log2 = 0, i = block_size; (i & 1) == 0; i >>= 1, bs_log2++); + if ((bs_log2 < 9) || (i != 1)) /* block size < 512 or not power of 2 */ + return RTEMS_INVALID_NUMBER; + + rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + return rc; + diskdevs_protected = TRUE; + + rc = rtems_bdbuf_find_pool(block_size, &pool); + if (rc != RTEMS_SUCCESSFUL) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return rc; + } + + rc = create_disk(dev, name, &dd); + if (rc != RTEMS_SUCCESSFUL) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return rc; + } + + dd->phys_dev = dd; + dd->uses = 0; + dd->start = 0; + dd->size = disk_size; + dd->block_size = block_size; + dd->block_size_log2 = bs_log2; + dd->ioctl = handler; + dd->pool = pool; + + rc = rtems_io_register_name(name, major, minor); + + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + + return rc; +} + +/* rtems_disk_create_log -- + * Create logical disk entry. Logical disk is contiguous area on physical + * disk. Disk may be splitted to several logical disks in several ways: + * manually or using information stored in blocks on physical disk + * (DOS-like partition table, BSD disk label, etc). This function usually + * invoked from application when application-specific splitting are in use, + * or from generic code which handle different logical disk organizations. + * This primitive will register device in rtems (invoke + * rtems_io_register_name). + * + * PARAMETERS: + * dev - logical device identifier (major, minor numbers) + * phys - physical device (block device which holds this logical disk) + * identifier + * start - starting block number on the physical device + * size - logical disk size in blocks + * name - logical disk name + * + * RETURNS: + * RTEMS_SUCCESSFUL if logical device successfully added, or error code + * if error occured (device already registered, no physical device + * exists, logical disk is out of physical disk boundaries, no memory + * available). + */ +rtems_status_code +rtems_disk_create_log(dev_t dev, dev_t phys, int start, int size, char *name) +{ + disk_device *dd; + disk_device *pdd; + rtems_status_code rc; + rtems_device_major_number major; + rtems_device_minor_number minor; + + rtems_filesystem_split_dev_t (dev, major, minor); + + rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + return rc; + diskdevs_protected = TRUE; + + pdd = get_disk_entry(phys); + if (pdd == NULL) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return RTEMS_INVALID_NUMBER; + } + + rc = create_disk(dev, name, &dd); + if (rc != RTEMS_SUCCESSFUL) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return rc; + } + + dd->phys_dev = pdd; + dd->uses = 0; + dd->start = start; + dd->size = size; + dd->block_size = pdd->block_size; + dd->block_size_log2 = pdd->block_size_log2; + dd->ioctl = pdd->ioctl; + + rc = rtems_io_register_name(name, major, minor); + + diskdevs_protected = FALSE; + rc = rtems_semaphore_release(diskdevs_mutex); + + return rc; +} + +/* rtems_disk_delete -- + * Delete physical or logical disk device. Device may be deleted if its + * use counter (and use counters of all logical devices - if it is + * physical device) equal to 0. When physical device deleted, + * all logical devices deleted inherently. Appropriate devices removed + * from "/dev" filesystem. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * + * RETURNS: + * RTEMS_SUCCESSFUL if block device successfully deleted, or error code + * if error occured (device is not defined, device is in use). + */ +rtems_status_code +rtems_disk_delete(dev_t dev) +{ + rtems_status_code rc; + int used; + rtems_device_major_number maj; + rtems_device_minor_number min; + + rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + return rc; + diskdevs_protected = TRUE; + + /* Check if this device is in use -- calculate usage counter */ + used = 0; + for (maj = 0; maj < disktab_size; maj++) + { + struct disk_device_table *dtab = disktab + maj; + if (dtab != NULL) + { + for (min = 0; min < dtab->size; min++) + { + disk_device *dd = dtab->minor[min]; + if ((dd != NULL) && (dd->phys_dev->dev == dev)) + used += dd->uses; + } + } + } + + if (used != 0) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return RTEMS_RESOURCE_IN_USE; + } + + /* Delete this device and all of its logical devices */ + for (maj = 0; maj < disktab_size; maj++) + { + struct disk_device_table *dtab = disktab +maj; + if (dtab != NULL) + { + for (min = 0; min < dtab->size; min++) + { + disk_device *dd = dtab->minor[min]; + if ((dd != NULL) && (dd->phys_dev->dev == dev)) + { + unlink(dd->name); + free(dd->name); + free(dd); + dtab->minor[min] = NULL; + } + } + } + } + + diskdevs_protected = FALSE; + rc = rtems_semaphore_release(diskdevs_mutex); + return rc; +} + +/* rtems_disk_lookup -- + * Find block device descriptor by its device identifier. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * + * RETURNS: + * pointer to the block device descriptor, or NULL if no such device + * exists. + */ +disk_device * +rtems_disk_lookup(dev_t dev) +{ + rtems_interrupt_level level; + disk_device *dd; + rtems_status_code rc; + + rtems_interrupt_disable(level); + if (diskdevs_protected) + { + rtems_interrupt_enable(level); + rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, + RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + return NULL; + diskdevs_protected = TRUE; + dd = get_disk_entry(dev); + dd->uses++; + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return dd; + } + else + { + /* Frequent and quickest case */ + dd = get_disk_entry(dev); + dd->uses++; + rtems_interrupt_enable(level); + return dd; + } +} + +/* rtems_disk_release -- + * Release disk_device structure (decrement usage counter to 1). + * + * PARAMETERS: + * dd - pointer to disk device structure + * + * RETURNS: + * RTEMS_SUCCESSFUL + */ +rtems_status_code +rtems_disk_release(disk_device *dd) +{ + rtems_interrupt_level level; + rtems_interrupt_disable(level); + dd->uses--; + rtems_interrupt_enable(level); + return RTEMS_SUCCESSFUL; +} + +/* rtems_disk_next -- + * Disk device enumerator. Looking for device having device number larger + * than dev and return disk device descriptor for it. If there are no + * such device, NULL value returned. + * + * PARAMETERS: + * dev - device number (use -1 to start search) + * + * RETURNS: + * Pointer to the disk descriptor for next disk device, or NULL if all + * devices enumerated. + */ +disk_device * +rtems_disk_next(dev_t dev) +{ + rtems_device_major_number major; + rtems_device_minor_number minor; + struct disk_device_table *dtab; + + dev++; + rtems_filesystem_split_dev_t (dev, major, minor); + + if (major >= disktab_size) + return NULL; + + dtab = disktab + major; + while (TRUE) + { + if ((dtab == NULL) || (minor > dtab->size)) + { + major++; minor = 0; + if (major >= disktab_size) + return NULL; + dtab = disktab + major; + } + else if (dtab->minor[minor] == NULL) + { + minor++; + } + else + return dtab->minor[minor]; + } +} + +/* rtems_disk_initialize -- + * Initialization of disk device library (initialize all data structures, + * etc.) + * + * PARAMETERS: + * none + * + * RETURNS: + * RTEMS_SUCCESSFUL if library initialized, or error code if error + * occured. + */ +rtems_status_code +rtems_disk_io_initialize(void) +{ + rtems_status_code rc; + + if (disk_io_initialized) + return RTEMS_SUCCESSFUL; + + disktab_size = DISKTAB_INITIAL_SIZE; + disktab = calloc(disktab_size, sizeof(struct disk_device_table)); + if (disktab == NULL) + return RTEMS_NO_MEMORY; + + diskdevs_protected = FALSE; + rc = rtems_semaphore_create( + rtems_build_name('D', 'D', 'E', 'V'), 1, + RTEMS_FIFO | RTEMS_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, &diskdevs_mutex); + + if (rc != RTEMS_SUCCESSFUL) + { + free(disktab); + return rc; + } + + rc = rtems_bdbuf_init(rtems_bdbuf_configuration, + rtems_bdbuf_configuration_size); + + if (rc != RTEMS_SUCCESSFUL) + { + rtems_semaphore_delete(diskdevs_mutex); + free(disktab); + return rc; + } + + disk_io_initialized = 1; + return RTEMS_SUCCESSFUL; +} + +/* rtems_disk_io_done -- + * Release all resources allocated for disk device interface. + * + * PARAMETERS: + * none + * + * RETURNS: + * RTEMS_SUCCESSFUL if all resources released, or error code if error + * occured. + */ +rtems_status_code +rtems_disk_io_done(void) +{ + rtems_device_major_number maj; + rtems_device_minor_number min; + rtems_status_code rc; + + /* Free data structures */ + for (maj = 0; maj < disktab_size; maj++) + { + struct disk_device_table *dtab = disktab + maj; + if (dtab != NULL) + { + for (min = 0; min < dtab->size; min++) + { + disk_device *dd = dtab->minor[min]; + unlink(dd->name); + free(dd->name); + free(dd); + } + free(dtab); + } + } + free(disktab); + + rc = rtems_semaphore_release(diskdevs_mutex); + + /* XXX bdbuf should be released too! */ + disk_io_initialized = 0; + return rc; +} diff --git a/c/src/exec/libblock/src/ramdisk.c b/c/src/exec/libblock/src/ramdisk.c new file mode 100644 index 0000000000..08f42846e0 --- /dev/null +++ b/c/src/exec/libblock/src/ramdisk.c @@ -0,0 +1,224 @@ +/* ramdisk.c -- RAM disk block device implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#include +#include +#include +#include +#include + +#include "rtems/blkdev.h" +#include "rtems/diskdevs.h" +#include "rtems/ramdisk.h" + +#define RAMDISK_DEVICE_BASE_NAME "/dev/ramdisk" + +/* Internal RAM disk descriptor */ +struct ramdisk { + int block_size; /* RAM disk block size */ + int block_num; /* Number of blocks on this RAM disk */ + void *area; /* RAM disk memory area */ + rtems_boolean initialized;/* RAM disk is initialized */ + rtems_boolean malloced; /* != 0, if memory allocated by malloc for this + RAM disk */ +}; + +static struct ramdisk *ramdisk; +static int nramdisks; + +/* ramdisk_read -- + * RAM disk READ request handler. This primitive copies data from RAM + * disk to supplied buffer and invoke the callout function to inform + * upper layer that reading is completed. + * + * PARAMETERS: + * req - pointer to the READ block device request info + * + * RETURNS: + * ioctl return value + */ +static int +ramdisk_read(struct ramdisk *rd, blkdev_request *req) +{ + char *from; + rtems_unsigned32 i; + blkdev_sg_buffer *sg; + rtems_unsigned32 remains; + + from = (char *)rd->area + (req->start * rd->block_size); + remains = rd->block_size * req->count; + sg = req->bufs; + for (i = 0; (remains > 0) && (i < req->bufnum); i++, sg++) + { + int count = sg->length; + if (count > remains) + count = remains; + memcpy(sg->buffer, from, count); + remains -= count; + } + req->req_done(req->done_arg, RTEMS_SUCCESSFUL, 0); + return 0; +} + +/* ramdisk_write -- + * RAM disk WRITE request handler. This primitive copies data from + * supplied buffer to RAM disk and invoke the callout function to inform + * upper layer that writing is completed. + * + * PARAMETERS: + * req - pointer to the WRITE block device request info + * + * RETURNS: + * ioctl return value + */ +static int +ramdisk_write(struct ramdisk *rd, blkdev_request *req) +{ + char *to; + rtems_unsigned32 i; + blkdev_sg_buffer *sg; + rtems_unsigned32 remains; + + to = (char *)rd->area + (req->start * rd->block_size); + remains = rd->block_size * req->count; + sg = req->bufs; + for (i = 0; (remains > 0) && (i < req->bufnum); i++, sg++) + { + int count = sg->length; + if (count > remains) + count = remains; + memcpy(to, sg->buffer, count); + remains -= count; + } + req->req_done(req->done_arg, RTEMS_SUCCESSFUL, 0); + return 0; +} + +/* ramdisk_ioctl -- + * IOCTL handler for RAM disk device. + * + * PARAMETERS: + * dev - device number (major, minor number) + * req - IOCTL request code + * argp - IOCTL argument + * + * RETURNS: + * IOCTL return value + */ +static int +ramdisk_ioctl(dev_t dev, int req, void *argp) +{ + switch (req) + { + case BLKIO_REQUEST: + { + rtems_device_minor_number minor; + blkdev_request *r = argp; + struct ramdisk *rd; + + minor = rtems_filesystem_dev_minor_t(dev); + if ((minor >= nramdisks) || !ramdisk[minor].initialized) + { + errno = ENODEV; + return -1; + } + + rd = ramdisk + minor; + + switch (r->req) + { + case BLKDEV_REQ_READ: + return ramdisk_read(rd, r); + + case BLKDEV_REQ_WRITE: + return ramdisk_write(rd, r); + + default: + errno = EBADRQC; + return -1; + } + break; + } + + default: + errno = EBADRQC; + return -1; + } +} + +/* ramdisk_initialize -- + * RAM disk device driver initialization. Run through RAM disk + * configuration information and configure appropriate RAM disks. + * + * PARAMETERS: + * major - RAM disk major device number + * minor - minor device number, not applicable + * arg - initialization argument, not applicable + * + * RETURNS: + * none + */ +rtems_device_driver +ramdisk_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + rtems_device_minor_number i; + rtems_ramdisk_config *c = rtems_ramdisk_configuration; + struct ramdisk *r; + rtems_status_code rc; + + rc = rtems_disk_io_initialize(); + if (rc != RTEMS_SUCCESSFUL) + return rc; + + r = ramdisk = calloc(rtems_ramdisk_configuration_size, + sizeof(struct ramdisk)); + + for (i = 0; i < rtems_ramdisk_configuration_size; i++, c++, r++) + { + dev_t dev = rtems_filesystem_make_dev_t(major, i); + char name[sizeof(RAMDISK_DEVICE_BASE_NAME "0123456789")]; + snprintf(name, sizeof(name), RAMDISK_DEVICE_BASE_NAME "%d", i); + r->block_size = c->block_size; + r->block_num = c->block_num; + if (c->location == NULL) + { + r->malloced = TRUE; + r->area = malloc(r->block_size * r->block_num); + if (r->area == NULL) /* No enough memory for this disk */ + { + r->initialized = FALSE; + continue; + } + else + { + r->initialized = TRUE; + } + } + else + { + r->malloced = FALSE; + r->initialized = TRUE; + r->area = c->location; + } + rc = rtems_disk_create_phys(dev, c->block_size, c->block_num, + ramdisk_ioctl, name); + if (rc != RTEMS_SUCCESSFUL) + { + if (r->malloced) + { + free(r->area); + } + r->initialized = FALSE; + } + } + nramdisks = rtems_ramdisk_configuration_size; + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/libblock/.cvsignore b/c/src/libblock/.cvsignore new file mode 100644 index 0000000000..d29e5050f5 --- /dev/null +++ b/c/src/libblock/.cvsignore @@ -0,0 +1,14 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +config.cache +config.guess +config.log +config.status +config.sub +configure +depcomp +install-sh +missing +mkinstalldirs diff --git a/c/src/libblock/ChangeLog b/c/src/libblock/ChangeLog new file mode 100644 index 0000000000..bc000d4a46 --- /dev/null +++ b/c/src/libblock/ChangeLog @@ -0,0 +1,25 @@ +2002-02-28 Joel Sherrill + + * Submitted by Victor V. Vengerov and merged + into the RTEMS source. + * ChangeLog, Makefile.am, README, configure.ac, include/Makefile.am, + include/rtems/bdbuf.h, include/rtems/blkdev.h, include/rtems/diskdevs.h, + include/rtems/ramdisk.h, include/rtems/.cvsignore, include/.cvsignore, + src/Makefile.am, src/bdbuf.c, src/blkdev.c, src/diskdevs.c, + src/ramdisk.c, src/.cvsignore, .cvsignore: New files. + +2001-11-29 Victor V. Vengerov + * AVL trees implementation added. + +2001-11-16 Victor V. Vengerov + * include/rtems/bdbuf.h, src/bdbuf.c(rtems_bdbuf_syncdev): New. + +2001-11-07 Victor V. Vengerov + + * ChangeLog: New file. + * src/, include/, include/rtems/: New directories. + * README, configure.ac, Makefile.am, src/Makefile.am, + include/Makefile.am: New files. + * include/rtems/bdbuf.h include/rtems/blkdev.h + include/rtems/diskdevs.h include/rtems/ramdisk.h + src/bdbuf.c src/blkdev.c src/diskdevs.c src/ramdisk.c: New files. diff --git a/c/src/libblock/Makefile.am b/c/src/libblock/Makefile.am new file mode 100644 index 0000000000..970205bc1f --- /dev/null +++ b/c/src/libblock/Makefile.am @@ -0,0 +1,13 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.4 +ACLOCAL_AMFLAGS = -I ../../../aclocal + +SUBDIRS = include src + +EXTRA_DIST = README + +include $(top_srcdir)/../../../automake/subdirs.am +include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/libblock/README b/c/src/libblock/README new file mode 100644 index 0000000000..671cc7f645 --- /dev/null +++ b/c/src/libblock/README @@ -0,0 +1,14 @@ +# +# $Id$ +# + +This directory contains the block device (HDD, CDROMs, etc) support code. +It includes: + - block device driver interface + - generic open/close/read/write/ioctl primitives for block device drivers + - disk I/O buffering + - logical disk support + - RAM disk block device driver + +Victor V. Vengerov, +November, 7 2001 diff --git a/c/src/libblock/configure.ac b/c/src/libblock/configure.ac new file mode 100644 index 0000000000..d0e5d61fb6 --- /dev/null +++ b/c/src/libblock/configure.ac @@ -0,0 +1,32 @@ +## Process this file with autoconf to produce a configure script. +## +## $Id$ + +AC_PREREQ(2.52) +AC_INIT +AC_CONFIG_SRCDIR([src/bdbuf.c]) +RTEMS_TOP(../../..) +AC_CONFIG_AUX_DIR(../../..) + +RTEMS_CANONICAL_TARGET_CPU +RTEMS_CANONICAL_HOST + +AM_INIT_AUTOMAKE(rtems-c-src-libblock,$RTEMS_VERSION,no) +AM_MAINTAINER_MODE + +RTEMS_ENV_RTEMSBSP +RTEMS_CHECK_CPU +RTEMS_CHECK_CUSTOM_BSP(RTEMS_BSP) +RTEMS_PROG_CC_FOR_TARGET +RTEMS_ENABLE_BARE + +RTEMS_CANONICALIZE_TOOLS + +RTEMS_PROJECT_ROOT + +# Explicitly list all Makefiles here +AC_CONFIG_FILES([Makefile +include/Makefile +src/Makefile +]) +AC_OUTPUT diff --git a/c/src/libblock/include/.cvsignore b/c/src/libblock/include/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/c/src/libblock/include/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/c/src/libblock/include/Makefile.am b/c/src/libblock/include/Makefile.am new file mode 100644 index 0000000000..298ae2974d --- /dev/null +++ b/c/src/libblock/include/Makefile.am @@ -0,0 +1,23 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.5 + +include_rtemsdir = $(includedir)/rtems + +$(PROJECT_INCLUDE)/%.h: %.h + $(INSTALL_DATA) $< $@ + +$(PROJECT_INCLUDE)/rtems: + @$(mkinstalldirs) $@ + +include_rtems_HEADERS = \ + rtems/bdbuf.h rtems/blkdev.h rtems/diskdevs.h rtems/ramdisk.h + +PREINSTALL_FILES = $(PROJECT_INCLUDE)/rtems \ + $(include_rtems_HEADERS:%=$(PROJECT_INCLUDE)/%) + +all-local: $(PREINSTALL_FILES) + +include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/libblock/include/rtems/.cvsignore b/c/src/libblock/include/rtems/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/c/src/libblock/include/rtems/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/c/src/libblock/include/rtems/bdbuf.h b/c/src/libblock/include/rtems/bdbuf.h new file mode 100644 index 0000000000..270b69598b --- /dev/null +++ b/c/src/libblock/include/rtems/bdbuf.h @@ -0,0 +1,282 @@ +/* bdbuf.h -- block device buffer management + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#ifndef __RTEMS_LIBBLOCK_BDBUF_H__ +#define __RTEMS_LIBBLOCK_BDBUF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "rtems/blkdev.h" +#include "rtems/diskdevs.h" + + +/* + * To manage buffers we using Buffer Descriptors. + * To speed-up buffer lookup descriptors are organized in AVL-Tree. + * The fields 'dev' and 'block' are search key. + */ + +/* Buffer descriptors + * Descriptors organized in AVL-tree to speedup buffer lookup. + * dev and block fields are search key in AVL-tree. + * Modified buffers, free buffers and used buffers linked in 'mod', 'free' and + * 'lru' chains appropriately. + */ + +typedef struct bdbuf_buffer { + Chain_Node link; /* Link in the lru, mod or free chains */ + +#ifdef BINARY_TREE + struct bdbuf_avl_node { + struct bdbuf_buffer *left; /* link to the left sub-tree */ + struct bdbuf_buffer *right; /* link to the right sub-tree */ + + int bf; /* AVL tree node balance factor */ + } avl; /* AVL-tree links */ +#else /* AVL TREE */ + struct bdbuf_avl_node { + char cache; /* Cache */ + + struct bdbuf_buffer* link[2]; /* Left and Right Kids */ + + char bal; /* The balance of the sub-tree */ + } avl; +#endif + dev_t dev; /* device number */ + blkdev_bnum block; /* block number on the device */ + + char *buffer; /* Pointer to the buffer memory area */ + rtems_status_code status; /* Last I/O operation completion status */ + int error; /* If status != RTEMS_SUCCESSFUL, this field contains + errno value which can be used by user later */ + boolean modified:1; /* =1 if buffer was modified */ + boolean in_progress:1; /* =1 if exchange with disk is in progress; + need to wait on semaphore */ + boolean actual:1; /* Buffer contains actual data */ + int use_count; /* Usage counter; incremented when somebody use + this buffer; decremented when buffer released + without modification or when buffer is flushed + by swapout task */ + + rtems_bdpool_id pool; /* Identifier of buffer pool to which this buffer + belongs */ + CORE_mutex_Control transfer_sema; + /* Transfer operation semaphore */ +} bdbuf_buffer; + + + +/* bdbuf_config structure describes block configuration (size, + * amount, memory location) for buffering layer + */ +typedef struct rtems_bdbuf_config { + int size; /* Size of block */ + int num; /* Number of blocks of appropriate size */ + char *mem_area; /* Pointer to the blocks location or NULL, in this + case memory for blocks will be allocated by + Buffering Layer with the help of RTEMS partition + manager */ +} rtems_bdbuf_config; + +extern rtems_bdbuf_config rtems_bdbuf_configuration[]; +extern int rtems_bdbuf_configuration_size; + +/* rtems_bdbuf_init -- + * Prepare buffering layer to work - initialize buffer descritors + * and (if it is neccessary) buffers. Buffers will be allocated accoriding + * to the configuration table, each entry describes kind of block and + * amount requested. After initialization all blocks is placed into + * free elements lists. + * + * PARAMETERS: + * conf_table - pointer to the buffers configuration table + * size - number of entries in configuration table + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + */ +rtems_status_code +rtems_bdbuf_init(rtems_bdbuf_config *conf_table, int size); + + +/* rtems_bdbuf_get -- + * Obtain block buffer. If specified block already cached (i.e. there's + * block in the _modified_, or _recently_used_), return address + * of appropriate buffer descriptor and increment reference counter to 1. + * If block is not cached, allocate new buffer and return it. Data + * shouldn't be read to the buffer from media; buffer may contains + * arbitrary data. This primitive may be blocked if there are no free + * buffer descriptors available and there are no unused non-modified + * (or synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (constructed of major and minor device number) + * block - linear media block number + * bd - address of variable to store pointer to the buffer descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * bufget_sema semaphore obtained by this primitive. + */ +rtems_status_code +rtems_bdbuf_get(dev_t device, blkdev_bnum block, bdbuf_buffer **bdb_ptr); + +/* rtems_bdbuf_read -- + * (Similar to the rtems_bdbuf_get, except reading data from media) + * Obtain block buffer. If specified block already cached, return address + * of appropriate buffer and increment reference counter to 1. If block is + * not cached, allocate new buffer and read data to it from the media. + * This primitive may be blocked on waiting until data to be read from + * media, if there are no free buffer descriptors available and there are + * no unused non-modified (or synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (consists of major and minor device number) + * block - linear media block number + * bd - address of variable to store pointer to the buffer descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * bufget_sema and transfer_sema semaphores obtained by this primitive. + */ +rtems_status_code +rtems_bdbuf_read(dev_t device, blkdev_bnum block, bdbuf_buffer **bdb_ptr); + +/* rtems_bdbuf_release -- + * Release buffer allocated before. This primitive decrease the + * usage counter. If it is zero, further destiny of buffer depends on + * 'modified' status. If buffer was modified, it is placed to the end of + * mod list and flush task waken up. If buffer was not modified, + * it is placed to the end of lru list, and bufget_sema released, allowing + * to reuse this buffer. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * flush_sema and bufget_sema semaphores may be released by this primitive. + */ +rtems_status_code +rtems_bdbuf_release(bdbuf_buffer *bd_buf); + +/* rtems_bdbuf_release_modified -- + * Release buffer allocated before, assuming that it is _modified_ by + * it's owner. This primitive decrease usage counter for buffer, mark + * buffer descriptor as modified. If usage counter is 0, insert it at + * end of mod chain and release flush_sema semaphore to activate the + * flush task. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * flush_sema semaphore may be released by this primitive. + */ +rtems_status_code +rtems_bdbuf_release_modified(bdbuf_buffer *bd_buf); + +/* rtems_bdbuf_sync -- + * Wait until specified buffer synchronized with disk. Invoked on exchanges + * critical for data consistency on the media. This primitive mark owned + * block as modified, decrease usage counter. If usage counter is 0, + * block inserted to the mod chain and flush_sema semaphore released. + * Finally, primitives blocked on transfer_sema semaphore. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * Primitive may be blocked on transfer_sema semaphore. + */ +rtems_status_code +rtems_bdbuf_sync(bdbuf_buffer *bd_buf); + +/* rtems_bdbuf_syncdev -- + * Synchronize with disk all buffers containing the blocks belonging to + * specified device. + * + * PARAMETERS: + * dev - block device number + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + */ +rtems_status_code +rtems_bdbuf_syncdev(dev_t dev); + +/* rtems_bdbuf_find_pool -- + * Find first appropriate buffer pool. This primitive returns the index + * of first buffer pool which block size is greater than or equal to + * specified size. + * + * PARAMETERS: + * block_size - requested block size + * pool - placeholder for result + * + * RETURNS: + * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully, + * RTEMS_INVALID_SIZE if specified block size is invalid (not a power + * of 2), RTEMS_NOT_DEFINED if buffer pool for this or greater block size + * is not configured. + */ +rtems_status_code +rtems_bdbuf_find_pool(int block_size, rtems_bdpool_id *pool); + +/* rtems_bdbuf_get_pool_info -- + * Obtain characteristics of buffer pool with specified number. + * + * PARAMETERS: + * pool - buffer pool number + * block_size - block size for which buffer pool is configured returned + * there + * blocks - number of buffers in buffer pool returned there + * + * RETURNS: + * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully, + * RTEMS_INVALID_NUMBER if appropriate buffer pool is not configured. + * + * NOTE: + * Buffer pools enumerated contiguously starting from 0. + */ +rtems_status_code +rtems_bdbuf_get_pool_info(rtems_bdpool_id pool, int *block_size, int *blocks); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/libblock/include/rtems/blkdev.h b/c/src/libblock/include/rtems/blkdev.h new file mode 100644 index 0000000000..ca3b5ba5e2 --- /dev/null +++ b/c/src/libblock/include/rtems/blkdev.h @@ -0,0 +1,147 @@ +/* + * blkdev.h - block device driver interface definitions + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#ifndef __RTEMS_LIBBLOCK_BLKDEV_H__ +#define __RTEMS_LIBBLOCK_BLKDEV_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Interface with device drivers + * Block device looks, initialized and behaves like traditional RTEMS device + * driver. Heart of the block device driver is in BIOREQUEST ioctl. This call + * puts I/O request to the block device queue, in priority order, for + * asynchronous processing. When driver executes request, req_done + * function invoked, so callee knows about it. Look for details below. + */ + + +/* Block device block number datatype */ +typedef rtems_unsigned32 blkdev_bnum; + +/* Block device request type */ +typedef enum blkdev_request_op { + BLKDEV_REQ_READ, /* Read operation */ + BLKDEV_REQ_WRITE /* Write operation */ +} blkdev_request_op; + +/* Type for block device request done callback function. + * + * PARAMETERS: + * arg - argument supplied in blkdev_request + * status - rtems status code for this operation + * errno - errno value to be passed to the user when + * status != RTEMS_SUCCESSFUL + */ +typedef void (* blkdev_request_cb)(void *arg, + rtems_status_code status, + int error); + +/* blkdev_sg_buffer + * Block device scatter/gather buffer structure + */ +typedef struct blkdev_sg_buffer { + rtems_unsigned32 length; /* Buffer length */ + void *buffer; /* Buffer pointer */ +} blkdev_sg_buffer; + +/* blkdev_request (Block Device Request) structure is + * used to read/write a number of blocks from/to device. + */ +typedef struct blkdev_request { + blkdev_request_op req; /* Block device operation (read or write) */ + blkdev_request_cb req_done; /* Callback function */ + void *done_arg; /* Argument to be passed to callback function*/ + blkdev_bnum start; /* Start block number */ + rtems_unsigned32 count; /* Number of blocks to be exchanged */ + rtems_unsigned32 bufnum; /* Number of buffers provided */ + + blkdev_sg_buffer bufs[0];/* List of scatter/gather buffers */ +} blkdev_request; + +/* Block device IOCTL request codes */ +#define BLKIO_REQUEST _IOWR('B', 1, blkdev_request) +#define BLKIO_GETBLKSIZE _IO('B', 2) +#define BLKIO_GETSIZE _IO('B', 3) +#define BLKIO_SYNCDEV _IO('B', 4) + +/* Device driver interface conventions suppose that driver may + * contain initialize/open/close/read/write/ioctl entry points. These + * primitives (except initialize) can be implemented in generic fashion, + * based upon supplied block device driver ioctl handler. Every block + * device driver should provide initialize entry point, which is register + * all block devices and appropriate ioctl handlers. + */ + +#define GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES \ + rtems_blkdev_generic_open, rtems_blkdev_generic_close, \ + rtems_blkdev_generic_read, rtems_blkdev_generic_write, \ + rtems_blkdev_generic_ioctl + +/* blkdev_generic_read -- + * Generic block device read primitive. Implemented using block device + * buffer management primitives. + */ +rtems_device_driver +rtems_blkdev_generic_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +/* blkdev_generic_write -- + * Generic block device driver write primitive. Implemented using block + * device buffer management primitives. + */ +rtems_device_driver +rtems_blkdev_generic_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +/* blkdev_generic_open -- + * Generic block device open primitive. + */ +rtems_device_driver +rtems_blkdev_generic_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +/* blkdev_generic_close -- + * Generic block device close primitive. + */ +rtems_device_driver +rtems_blkdev_generic_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +/* blkdev_generic_ioctl -- + * Generic block device ioctl primitive. + */ +rtems_device_driver +rtems_blkdev_generic_ioctl( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/libblock/include/rtems/diskdevs.h b/c/src/libblock/include/rtems/diskdevs.h new file mode 100644 index 0000000000..fab3231e62 --- /dev/null +++ b/c/src/libblock/include/rtems/diskdevs.h @@ -0,0 +1,206 @@ +/* + * logdisk.h - Physical and logical block devices (disks) support + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#ifndef __RTEMS_LIBBLOCK_LOGDISK_H__ +#define __RTEMS_LIBBLOCK_LOGDISK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "rtems/blkdev.h" + +/* Buffer pool identifier */ +typedef int rtems_bdpool_id; + +/* Block device ioctl handler */ +typedef int (* block_device_ioctl) (dev_t dev, int req, void *argp); + +/* disk_device: Entry of this type created for every disk device (both for + * logical and physical disks). + * Array of arrays of pointers to disk_device structures maintained. First + * table indexed by major number and second table indexed by minor number. + * Such data organization allow quick lookup using data structure of + * moderated size. + */ +typedef struct disk_device { + dev_t dev; /* Device ID (major + minor) */ + struct disk_device *phys_dev; /* Physical device ID (the same + as dev if this entry specifies + the physical device) */ + char *name; /* Disk device name */ + int uses; /* Use counter. Device couldn't be + removed if it is in use. */ + int start; /* Starting block number (0 for + physical devices, block offset + on the related physical device + for logical device) */ + int size; /* Size of physical or logical disk + in disk blocks */ + int block_size; /* Size of device block (minimum + transfer unit) in bytes + (must be power of 2) */ + int block_size_log2; /* log2 of block_size */ + rtems_bdpool_id pool; /* Buffer pool assigned to this + device */ + block_device_ioctl ioctl; /* ioctl handler for this block + device */ +} disk_device; + +/* rtems_disk_create_phys -- + * Create physical disk entry. This function usually invoked from + * block device driver initialization code when physical device + * detected in the system. Device driver should provide ioctl handler + * to allow block device access operations. This primitive will register + * device in rtems (invoke rtems_io_register_name). + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * block_size - size of disk block (minimum data transfer unit); must be + * power of 2 + * disk_size - number of blocks on device + * handler - IOCTL handler (function providing basic block input/output + * request handling BIOREQUEST and other device management + * operations) + * name - character name of device (e.g. /dev/hda) + * + * RETURNS: + * RTEMS_SUCCESSFUL if information about new physical disk added, or + * error code if error occured (device already registered, wrong block + * size value, no memory available). + */ +rtems_status_code +rtems_disk_create_phys(dev_t dev, int block_size, int disk_size, + block_device_ioctl handler, + char *name); + +/* rtems_disk_create_log -- + * Create logical disk entry. Logical disk is contiguous area on physical + * disk. Disk may be splitted to several logical disks in several ways: + * manually or using information stored in blocks on physical disk + * (DOS-like partition table, BSD disk label, etc). This function usually + * invoked from application when application-specific splitting are in use, + * or from generic code which handle different logical disk organizations. + * This primitive will register device in rtems (invoke + * rtems_io_register_name). + * + * PARAMETERS: + * dev - logical device identifier (major, minor numbers) + * phys - physical device (block device which holds this logical disk) + * identifier + * start - starting block number on the physical device + * size - logical disk size in blocks + * name - logical disk name + * + * RETURNS: + * RTEMS_SUCCESSFUL if logical device successfully added, or error code + * if error occured (device already registered, no physical device + * exists, logical disk is out of physical disk boundaries, no memory + * available). + */ +rtems_status_code +rtems_disk_create_log(dev_t dev, dev_t phys, int start, int size, char *name); + +/* rtems_disk_delete -- + * Delete physical or logical disk device. Device may be deleted if its + * use counter (and use counters of all logical devices - if it is + * physical device) equal to 0. When physical device deleted, + * all logical devices deleted inherently. Appropriate devices removed + * from "/dev" filesystem. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * + * RETURNS: + * RTEMS_SUCCESSFUL if block device successfully deleted, or error code + * if error occured (device is not defined, device is in use). + */ +rtems_status_code +rtems_disk_delete(dev_t dev); + +/* rtems_disk_lookup -- + * Find block device descriptor by its device identifier. This function + * increment usage counter to 1. User should release disk_device structure + * by invoking rtems_disk_release primitive. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * + * RETURNS: + * pointer to the block device descriptor, or NULL if no such device + * exists. + */ +disk_device * +rtems_disk_lookup(dev_t dev); + +/* rtems_disk_release -- + * Release disk_device structure (decrement usage counter to 1). + * + * PARAMETERS: + * dd - pointer to disk device structure + * + * RETURNS: + * RTEMS_SUCCESSFUL + * + * NOTE: + * It should be implemented as inline function. + */ +rtems_status_code +rtems_disk_release(disk_device *dd); + +/* rtems_disk_next -- + * Disk device enumerator. Looking for device having device number larger + * than dev and return disk device descriptor for it. If there are no + * such device, NULL value returned. + * + * PARAMETERS: + * dev - device number (use -1 to start search) + * + * RETURNS: + * Pointer to the disk descriptor for next disk device, or NULL if all + * devices enumerated. */ +disk_device * +rtems_disk_next(dev_t dev); + +/* rtems_diskio_initialize -- + * Initialization of disk device library (initialize all data structures, + * etc.) + * + * PARAMETERS: + * none + * + * RETURNS: + * RTEMS_SUCCESSFUL if library initialized, or error code if error + * occured. + */ +rtems_status_code +rtems_disk_io_initialize(void); + +/* rtems_diskio_done -- + * Release all resources allocated for disk device interface. + * + * PARAMETERS: + * none + * + * RETURNS: + * RTEMS_SUCCESSFUL if all resources released, or error code if error + * occured. + */ +rtems_status_code +rtems_disk_io_done(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/libblock/include/rtems/ramdisk.h b/c/src/libblock/include/rtems/ramdisk.h new file mode 100644 index 0000000000..b9e8c238b8 --- /dev/null +++ b/c/src/libblock/include/rtems/ramdisk.h @@ -0,0 +1,52 @@ +/* ramdisk.c -- RAM disk block device implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#ifndef __RTEMS_LIBBLOCK_RAMDISK_H__ +#define __RTEMS_LIBBLOCK_RAMDISK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "rtems/blkdev.h" + +/* RAM disk configuration table entry */ +typedef struct rtems_ramdisk_config { + int block_size; /* RAM disk block size */ + int block_num; /* Number of blocks on this RAM disk */ + void *location; /* RAM disk permanent location (out of RTEMS controlled + memory), or NULL if RAM disk memory should be + allocated dynamically */ +} rtems_ramdisk_config; + +/* If application want to use RAM disk, it should specify configuration of + * available RAM disks. + * The following is definitions for RAM disk configuration table + */ +extern rtems_ramdisk_config rtems_ramdisk_configuration[]; +extern int rtems_ramdisk_configuration_size; + +/* ramdisk_initialize -- + * RAM disk driver initialization entry point. + */ +rtems_device_driver +ramdisk_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg); + +#define RAMDISK_DRIVER_TABLE_ENTRY \ + { ramdisk_initialize, GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES } + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/libblock/src/.cvsignore b/c/src/libblock/src/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/c/src/libblock/src/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/c/src/libblock/src/Makefile.am b/c/src/libblock/src/Makefile.am new file mode 100644 index 0000000000..4b563282df --- /dev/null +++ b/c/src/libblock/src/Makefile.am @@ -0,0 +1,33 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.5 + +LIBNAME = libblock +LIB = ${ARCH}/${LIBNAME}.a + +C_FILES = bdbuf.c blkdev.c diskdevs.c ramdisk.c + +C_O_FILES = $(C_FILES:%.c=${ARCH}/%.o) + +SRCS = $(C_FILES) +OBJS = $(C_O_FILES) + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../../../automake/compile.am +include $(top_srcdir)/../../../automake/lib.am + +AM_CFLAGS += $(LIBC_DEFINES) + +TMPINSTALL_FILES += $(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a + +$(LIB): ${OBJS} + $(make-library) + +$(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a: $(LIB) + $(INSTALL_DATA) $< $@ + +all-local: ${ARCH} $(PREINSTALL_FILES) $(TMPINSTALL_FILES) + +include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/libblock/src/bdbuf.c b/c/src/libblock/src/bdbuf.c new file mode 100644 index 0000000000..806ad35243 --- /dev/null +++ b/c/src/libblock/src/bdbuf.c @@ -0,0 +1,1876 @@ +/* + * Disk I/O buffering + * Buffer managment + * + * Copyright (C) 2001 OKTET Ltd., St.-Peterburg, Russia + * Author: Andrey G. Ivanov + * Victor V. Vengerov + * + * @(#) $Id$ + */ + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include +#include +#include +#include + +#include "rtems/bdbuf.h" + +/* Fatal errors: */ +#define BLKDEV_FATAL_ERROR(n) (('B' << 24) | ((n) & 0x00FFFFFF)) +#define BLKDEV_FATAL_BDBUF_CONSISTENCY BLKDEV_FATAL_ERROR(1) +#define BLKDEV_FATAL_BDBUF_SWAPOUT BLKDEV_FATAL_ERROR(2) + +enum balance_factor {BF_LEFT = -1, + BF_NONE = 0, + BF_RIGHT = 1}; + + +#define SWAPOUT_PRIORITY 15 +#define SWAPOUT_STACK_SIZE (RTEMS_MINIMUM_STACK_SIZE * 2) + +static rtems_task bdbuf_swapout_task(rtems_task_argument unused); + +/* + * The groups of the blocks with the same size are collected in the + * bd_pool. Note that a several of the buffer's groups with the + * same size can exists. + */ +typedef struct bdbuf_pool +{ + bdbuf_buffer *tree; /* Buffer descriptor lookup AVL tree root */ + + Chain_Control free; /* Free buffers list */ + Chain_Control lru; /* Last recently used list */ + + int blksize; /* The size of the blocks (in bytes) */ + int nblks; /* Number of blocks in this pool */ + rtems_id bufget_sema; /* Buffer obtain counting semaphore */ + void *mallocd_bufs; /* Pointer to the malloc'd buffer memory, + or NULL, if buffer memory provided in + buffer configuration */ + bdbuf_buffer *bdbufs; /* Pointer to table of buffer descriptors + allocated for this buffer pool. */ +} bdbuf_pool; + +/* Buffering layer context definition */ +struct bdbuf_context { + bdbuf_pool *pool; /* Table of buffer pools */ + int npools; /* Number of entries in pool table */ + + Chain_Control mod; /* Modified buffers list */ + rtems_id flush_sema; /* Buffer flush semaphore; counting + semaphore; incremented when buffer + flushed to the disk; decremented when + buffer modified */ + rtems_id swapout_task; /* Swapout task ID */ +}; + +/* Block device request with a single buffer provided */ +typedef struct blkdev_request1 { + blkdev_request req; + blkdev_sg_buffer sg[1]; +} blkdev_request1; + +/* The static context of buffering layer */ +static struct bdbuf_context bd_ctx; + +#define SAFE +#ifdef SAFE +typedef rtems_mode preemption_key; + +#define DISABLE_PREEMPTION(key) \ + do { \ + rtems_task_mode(RTEMS_PREEMPT_MASK, RTEMS_NO_PREEMPT, &(key)); \ + } while (0) + +#define ENABLE_PREEMPTION(key) \ + do { \ + rtems_mode temp; \ + rtems_task_mode(RTEMS_PREEMPT_MASK, (key), &temp); \ + } while (0) + +#else + +typedef boolean preemption_key; + +#define DISABLE_PREEMPTION(key) \ + do { \ + (key) = _Thread_Executing->is_preemptible; \ + _Thread_Executing->is_preemptible = 0; \ + } while (0) + +#define ENABLE_PREEMPTION(key) \ + do { \ + _Thread_Executing->is_preemptible = (key); \ + if (_Thread_Evaluate_mode()) \ + _Thread_Dispatch(); \ + } while (0) + +#endif + + +#ifdef BINARY_TREE +static bdbuf_buffer * +avl_search(bdbuf_buffer **root, dev_t dev, blkdev_bnum block) +{ + bdbuf_buffer *p = *root; + while ((p != NULL) && ((p->dev != dev) || (p->block != block))) + { + if ((p->dev < dev) || ((p->dev == dev) && (p->block < block))) + p = p->avl.right; + else + p = p->avl.left; + } + return p; +} + +static bdbuf_buffer * +avl_search_for_sync(bdbuf_buffer **root, disk_device *dd) +{ + bdbuf_buffer *p = *root; + bdbuf_buffer *s[32]; + bdbuf_buffer **sp = s; + dev_t dev = dd->phys_dev->dev; + blkdev_bnum b = dd->start; + blkdev_bnum e = b + dd->size - 1; + + while (p != NULL) + { + if ((p->dev < dev) || ((p->dev == dev) && (p->block < b))) + p = p->avl.right; + else if ((p->dev > dev) || ((p->dev == dev) && (p->block > e))) + p = p->avl.left; + else if (p->modified) + return p; + else + { + if (p->avl.right != NULL) + *sp++ = p->avl.right; + p = p->avl.left; + } + if ((p == NULL) && (sp > s)) + p = *--sp; + } + return p; +} + +static int +avl_insert(bdbuf_buffer **root, bdbuf_buffer *node) +{ + bdbuf_buffer **r = root; + node->avl.left = node->avl.right = NULL; + while (TRUE) + { + bdbuf_buffer *rr = *r; + if (rr == NULL) + { + *r = node; + return 0; + } + else if ((rr->dev < node->dev) || ((rr->dev == node->dev) && + (rr->block < node->block))) + { + r = &rr->avl.right; + } + else if ((rr->dev == node->dev) && (rr->block == node->block)) + { + return -1; + } + else + { + r = &rr->avl.left; + } + } +} + +static int +avl_remove(bdbuf_buffer **root, bdbuf_buffer *node) +{ + bdbuf_buffer **p = root; + dev_t dev = node->dev; + blkdev_bnum block = node->block; + + while ((*p != NULL) && (*p != node)) + { + if (((*p)->dev < dev) || (((*p)->dev == dev) && ((*p)->block < block))) + p = &(*p)->avl.right; + else + p = &(*p)->avl.left; + } + if (*p == NULL) + return -1; + + *p = node->avl.left; + while (*p != NULL) + p = &(*p)->avl.right; + *p = node->avl.right; + node->avl.left = node->avl.right = NULL; + return 0; +} + +#else + +/* The default maximum height of 32 allows for AVL trees having + between 5,704,880 and 4,294,967,295 nodes, depending on order of + insertion. You may change this compile-time constant as you + wish. */ +#ifndef AVL_MAX_HEIGHT +#define AVL_MAX_HEIGHT 32 +#endif + + + +/* + * avl_cmp_node_node -- + * Compares two avl nodes. Function compares dev/block pairs of + * the node1 and node2. + * + * PARAMETERS: + * node1 - Pointer to the first node to compare + * node2 - Pointer to the second node to compare + * + * RETURNS: + * 0 - dev/block of the nodes are equal + * 1 - dev/block of the second node are less + * -1 - dev/block of the first node are less + */ +static inline int +avl_cmp_node_node(const bdbuf_buffer *const node1, + const bdbuf_buffer *const node2) +{ + if (node1->dev < node2->dev) + return -1; + else if (node1->dev > node2->dev) + return +1; + else if (node1->block < node2->block) + return -1; + else if (node1->block > node2->block) + return +1; + else + return 0; +} + +/* + * avl_cmp_node_pattern - + * compares the dev/block of the node with specified two. + * + * PARAMETERS: + * node1 - Pointer to the node to compare + * dev/block - The pattern to compare with + * + * RETURNS: + * 0 - dev/block of the node and specified are equal + * 1 - dev/block specified are less + * -1 - dev/block of the first node are less + * + */ +static inline int +avl_cmp_node_pattern(const bdbuf_buffer *const node1, + dev_t dev, + blkdev_bnum block) +{ + if (node1->dev < dev) + return -1; + else if (node1->dev > dev) + return +1; + else if (node1->block < block) + return -1; + else if (node1->block > block) + return +1; + else + return 0; +} + + +/* + * avl_search -- + * Searches for the node with specified dev/block. + * + * PARAMETERS: + * root - pointer to the root node of the AVL-Tree. + * dev, block - search key + * + * RETURNS: + * NULL if node with specified dev/block not found + * non-NULL - pointer to the node with specified dev/block + */ +static bdbuf_buffer * +avl_search(bdbuf_buffer **root, dev_t dev, blkdev_bnum block) +{ + + bdbuf_buffer *p = *root; + while ((p != NULL) && ((p->dev != dev) || (p->block != block))) + { + if ((p->dev < dev) || ((p->dev == dev) && (p->block < block))) + p = p->avl.link[1]; + else + p = p->avl.link[0]; + } + return p; +} + +/* avl_search_for_sync -- + * Search in AVL tree for first modified buffer belongs to specified + * disk device. + * + * PARAMETERS: + * root - pointer to tree root + * dd - disk device descriptor + * + * RETURNS: + * Block buffer, or NULL if no modified blocks on specified device + * exists. + */ +static bdbuf_buffer * +avl_search_for_sync(bdbuf_buffer **root, disk_device *dd) +{ + bdbuf_buffer *p = *root; + bdbuf_buffer *s[AVL_MAX_HEIGHT]; + bdbuf_buffer **sp = s; + dev_t dev = dd->phys_dev->dev; + blkdev_bnum b = dd->start; + blkdev_bnum e = b + dd->size - 1; + + while (p != NULL) + { + if ((p->dev < dev) || ((p->dev == dev) && (p->block < b))) + p = p->avl.link[1]; + else if ((p->dev > dev) || ((p->dev == dev) && (p->block > e))) + p = p->avl.link[0]; + else if (p->modified) + return p; + else + { + if (p->avl.link[1] != NULL) + *sp++ = p->avl.link[1]; + p = p->avl.link[0]; + } + if ((p == NULL) && (sp > s)) + p = *--sp; + } + return p; +} + + +/* + * avl_insert -- + * Inserts the specified node to the AVl-Tree. + * + * PARAMETERS: + * root_addr - Pointer to pointer to the root node + * node - Pointer to the node to add. + * + * RETURNS: + * 0 - The node added successfully + * -1 - An error occured + */ +static int +avl_insert (bdbuf_buffer **root_addr, bdbuf_buffer *node) +{ + /* Uses Knuth's Algorithm 6.2.3A (balanced tree search and + insertion), but caches results of comparisons. In empirical + tests this eliminates about 25% of the comparisons seen under + random insertions. */ + + /* A1. */ + int t_modified = 0; + bdbuf_buffer *t; + bdbuf_buffer *s, *p, *q, *r; + + bdbuf_buffer *root_link = *root_addr;; + + t = root_link; + s = p = t; + + if (s == NULL) + { + q = t = node; + q->avl.link[0] = q->avl.link[1] = NULL; + q->avl.bal = 0; + *root_addr = t; + return 0; + } + + for (;;) + { + /* A2. */ + int diff = avl_cmp_node_node(node, p); + + /* A3. */ + if (diff < 0) + { + p->avl.cache = 0; + q = p->avl.link[0]; + if (q == NULL) + { + p->avl.link[0] = q = node; + break; + } + } + /* A4. */ + else + if (diff > 0) + { + p->avl.cache = 1; + q = p->avl.link[1]; + if (q == NULL) + { + p->avl.link[1] = q = node; + break; + } + } + else + /* A2. */ + { + /* + * The item found. Nothing changed. Have not to update + * root_adr*/ + + return -1; + } + + /* A3, A4. */ + if (q->avl.bal != 0) + { + t = p, s = q; + t_modified = 1; + } + p = q; + } + + /* A5. */ + q->avl.link[0] = q->avl.link[1] = NULL; + q->avl.bal = 0; + + /* A6. */ + r = p = s->avl.link[(int) s->avl.cache]; + while (p != q) + { + p->avl.bal = p->avl.cache * 2 - 1; + p = p->avl.link[(int) p->avl.cache]; + } + + /* A7. */ + if (s->avl.cache == 0) + { + /* a = -1. */ + if (s->avl.bal == 0) + { + s->avl.bal = -1; + *root_addr = root_link; + return 0; + } + else if (s->avl.bal == +1) + { + s->avl.bal = 0; + *root_addr = root_link; + return 0; + } + + assert (s->avl.bal == -1); + if (r->avl.bal == -1) + { + /* A8. */ + p = r; + s->avl.link[0] = r->avl.link[1]; + r->avl.link[1] = s; + s->avl.bal = r->avl.bal = 0; + } + else + { + /* A9. */ + assert(r->avl.bal == +1); + p = r->avl.link[1]; + r->avl.link[1] = p->avl.link[0]; + p->avl.link[0] = r; + s->avl.link[0] = p->avl.link[1]; + p->avl.link[1] = s; + if (p->avl.bal == -1) + s->avl.bal = 1, r->avl.bal = 0; + else + { + if (p->avl.bal == 0) + { + s->avl.bal = r->avl.bal = 0; + } + else + { + assert (p->avl.bal == +1); + s->avl.bal = 0, r->avl.bal = -1; + } + } + p->avl.bal = 0; + } + } + else + { + /* a == +1. */ + if (s->avl.bal == 0) + { + s->avl.bal = 1; + *root_addr = root_link; + return 0; + } + else if (s->avl.bal == -1) + { + s->avl.bal = 0; + *root_addr = root_link; + return 0; + } + + assert(s->avl.bal == +1); + if (r->avl.bal == +1) + { + /* A8. */ + p = r; + s->avl.link[1] = r->avl.link[0]; + r->avl.link[0] = s; + s->avl.bal = r->avl.bal = 0; + } + else + { + /* A9. */ + assert(r->avl.bal == -1); + p = r->avl.link[0]; + r->avl.link[0] = p->avl.link[1]; + p->avl.link[1] = r; + s->avl.link[1] = p->avl.link[0]; + p->avl.link[0] = s; + if (p->avl.bal == +1) + { + s->avl.bal = -1, r->avl.bal = 0; + } + else + { + if (p->avl.bal == 0) + { + s->avl.bal = r->avl.bal = 0; + } + else + { + assert(p->avl.bal == -1); + s->avl.bal = 0, r->avl.bal = 1; + } + } + p->avl.bal = 0; + } + } + + /* A10. */ + if (t_modified) + { + if (s == t->avl.link[1]) + t->avl.link[1] = p; + else + t->avl.link[0] = p; + } + else + { + root_link = p; + } + + *root_addr = root_link; + return 0; +} + + +/* avl_remove -- + * removes the node from the tree. + * + * PARAMETERS: + * root_addr - Pointer to pointer to the root node + * node - Pointer to the node to remove + * + * RETURNS: + * 0 - Item removed + * -1 - No such item found + */ +static int +avl_remove(bdbuf_buffer **root_addr, const bdbuf_buffer *node) +{ + /* Uses my Algorithm D, which can be found at + http://www.msu.edu/user/pfaffben/avl. Algorithm D is based on + Knuth's Algorithm 6.2.2D (Tree deletion) and 6.2.3A (Balanced + tree search and insertion), as well as the notes on pages 465-466 + of Vol. 3. */ + + /* D1. */ + bdbuf_buffer *pa[AVL_MAX_HEIGHT]; /* Stack P: Nodes. */ + char a[AVL_MAX_HEIGHT]; /* Stack P: Bits. */ + int k = 1; /* Stack P: Pointer. */ + + bdbuf_buffer **q; + bdbuf_buffer *p; + + + /* + * To avoid using unnessary instance of the 'bdbuf_buffer' (as pa[0]) + * we will catch all access to pa[0] and use &root_avl instead + */ + struct bdbuf_avl_node root_avl; + + root_avl.link[0] = *root_addr; + root_avl.link[1] = NULL; + root_avl.bal = 0; + root_avl.cache = 0; + + a[0] = 0; + + p = root_avl.link[0]; + + + k = 1; + + for (;;) + { + /* D2. */ + int diff; + + if (p == NULL) + return -1; + + diff = avl_cmp_node_node(node, p); + + if (diff == 0) + break; + + /* D3, D4. */ + pa[k] = p; + if (diff < 0) + { + p = p->avl.link[0]; + a[k] = 0; + } + else if (diff > 0) + { + p = p->avl.link[1]; + a[k] = 1; + } + k++; + } + + /* D5. */ + if (k == 1) + { + /* Andron */ + q = &root_avl.link[(int) a[k - 1]]; + } + else + { + q = &pa[k - 1]->avl.link[(int) a[k - 1]]; + } + if (p->avl.link[1] == NULL) + { + *q = p->avl.link[0]; + if (*q) + (*q)->avl.bal = 0; + } + else + { + /* D6. */ + bdbuf_buffer *r = p->avl.link[1]; + if (r->avl.link[0] == NULL) + { + r->avl.link[0] = p->avl.link[0]; + *q = r; + r->avl.bal = p->avl.bal; + a[k] = 1; + pa[k++] = r; + } + else + { + /* D7. */ + bdbuf_buffer *s = r->avl.link[0]; + int l = k++; + + a[k] = 0; + pa[k++] = r; + + /* D8. */ + while (s->avl.link[0] != NULL) + { + r = s; + s = r->avl.link[0]; + a[k] = 0; + pa[k++] = r; + } + + /* D9. */ + a[l] = 1; + pa[l] = s; + s->avl.link[0] = p->avl.link[0]; + r->avl.link[0] = s->avl.link[1]; + s->avl.link[1] = p->avl.link[1]; + s->avl.bal = p->avl.bal; + *q = s; + } + } + + assert(k > 0); + /* D10. */ + while (--k) + { + bdbuf_buffer *s = pa[k], *r; + + if (a[k] == 0) + { + /* D10. */ + if (s->avl.bal == -1) + { + s->avl.bal = 0; + continue; + } + else if (s->avl.bal == 0) + { + s->avl.bal = 1; + break; + } + + assert(s->avl.bal == +1); + r = s->avl.link[1]; + + assert(r != NULL); + if (r->avl.bal == 0) + { + /* D11. */ + s->avl.link[1] = r->avl.link[0]; + r->avl.link[0] = s; + r->avl.bal = -1; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = r; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = r; + } + break; + } + else + if (r->avl.bal == +1) + { + /* D12. */ + s->avl.link[1] = r->avl.link[0]; + r->avl.link[0] = s; + s->avl.bal = r->avl.bal = 0; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = r; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = r; + } + } + else + { + /* D13. */ + assert(r->avl.bal == -1); + p = r->avl.link[0]; + r->avl.link[0] = p->avl.link[1]; + p->avl.link[1] = r; + s->avl.link[1] = p->avl.link[0]; + p->avl.link[0] = s; + if (p->avl.bal == +1) + s->avl.bal = -1, r->avl.bal = 0; + else if (p->avl.bal == 0) + { + s->avl.bal = r->avl.bal = 0; + } + else + { + assert(p->avl.bal == -1); + s->avl.bal = 0, r->avl.bal = +1; + } + p->avl.bal = 0; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = p; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = p; + } + } + } + else + { + assert(a[k] == 1); + + /* D10. */ + if (s->avl.bal == +1) + { + s->avl.bal = 0; + continue; + } + else + if (s->avl.bal == 0) + { + s->avl.bal = -1; + break; + } + + assert(s->avl.bal == -1); + r = s->avl.link[0]; + + if (r == NULL || r->avl.bal == 0) + { + /* D11. */ + s->avl.link[0] = r->avl.link[1]; + r->avl.link[1] = s; + r->avl.bal = 1; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = r; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = r; + } + + break; + } + else + if (r->avl.bal == -1) + { + /* D12. */ + s->avl.link[0] = r->avl.link[1]; + r->avl.link[1] = s; + s->avl.bal = r->avl.bal = 0; + if (k == 1) + { + root_avl.link[(int) a[k - 1]] = r; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = r; + } + + } + else + if (r->avl.bal == +1) + { + /* D13. */ + p = r->avl.link[1]; + r->avl.link[1] = p->avl.link[0]; + p->avl.link[0] = r; + s->avl.link[0] = p->avl.link[1]; + p->avl.link[1] = s; + if (p->avl.bal == -1) + s->avl.bal = 1, r->avl.bal = 0; + else + if (p->avl.bal == 0) + s->avl.bal = r->avl.bal = 0; + else + { + assert(p->avl.bal == 1); + s->avl.bal = 0, r->avl.bal = -1; + } + p->avl.bal = 0; + if (k == 1) + { + /* Andron */ + root_avl.link[(int) a[k - 1]] = p; + } + else + { + pa[k - 1]->avl.link[(int) a[k - 1]] = p; + } + } + } + } + + *root_addr = root_avl.link[0]; + return 0; +} + +#endif + +/* bdbuf_initialize_pool -- + * Initialize single buffer pool. + * + * PARAMETERS: + * config - buffer pool configuration + * pool - pool number + * + * RETURNS: + * RTEMS_SUCCESSFUL, if buffer pool initialized successfully, or error + * code if error occured. + */ +static rtems_status_code +bdbuf_initialize_pool(rtems_bdbuf_config *config, int pool) +{ + bdbuf_pool *p = bd_ctx.pool + pool; + unsigned char *bufs; + bdbuf_buffer *b; + rtems_status_code rc; + int i; + + p->blksize = config->size; + p->nblks = config->num; + p->tree = NULL; + + Chain_Initialize_empty(&p->free); + Chain_Initialize_empty(&p->lru); + + /* Allocate memory for buffer descriptors */ + p->bdbufs = calloc(config->num, sizeof(bdbuf_buffer)); + if (p->bdbufs == NULL) + { + return RTEMS_NO_MEMORY; + } + + /* Allocate memory for buffers if required */ + if (config->mem_area == NULL) + { + bufs = p->mallocd_bufs = malloc(config->num * config->size); + if (bufs == NULL) + { + free(p->bdbufs); + return RTEMS_NO_MEMORY; + } + } + else + { + bufs = config->mem_area; + p->mallocd_bufs = NULL; + } + + for (i = 0, b = p->bdbufs; i < p->nblks; i++, b++, bufs += p->blksize) + { + b->dev = -1; b->block = 0; + b->buffer = bufs; + b->actual = b->modified = b->in_progress = FALSE; + b->use_count = 0; + b->pool = pool; + _Chain_Append(&p->free, &b->link); + } + + rc = rtems_semaphore_create( + rtems_build_name('B', 'U', 'F', 'G'), + p->nblks, + RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, + 0, + &p->bufget_sema); + + if (rc != RTEMS_SUCCESSFUL) + { + free(p->bdbufs); + free(p->mallocd_bufs); + return rc; + } + + return RTEMS_SUCCESSFUL; +} + +/* bdbuf_release_pool -- + * Free resources allocated for buffer pool with specified number. + * + * PARAMETERS: + * pool - buffer pool number + * + * RETURNS: + * RTEMS_SUCCESSFUL + */ +static rtems_status_code +bdbuf_release_pool(rtems_bdpool_id pool) +{ + bdbuf_pool *p = bd_ctx.pool + pool; + rtems_semaphore_delete(p->bufget_sema); + free(p->bdbufs); + free(p->mallocd_bufs); + return RTEMS_SUCCESSFUL; +} + +/* rtems_bdbuf_init -- + * Prepare buffering layer to work - initialize buffer descritors + * and (if it is neccessary)buffers. Buffers will be allocated accoriding + * to the configuration table, each entry describes kind of block and + * amount requested. After initialization all blocks is placed into + * free elements lists. + * + * PARAMETERS: + * conf_table - pointer to the buffers configuration table + * size - number of entries in configuration table + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + */ +rtems_status_code +rtems_bdbuf_init(rtems_bdbuf_config *conf_table, int size) +{ + rtems_bdpool_id i; + rtems_status_code rc; + + if (size <= 0) + return RTEMS_INVALID_SIZE; + + bd_ctx.npools = size; + + /* + * Allocate memory for buffer pool descriptors + */ + bd_ctx.pool = calloc(size, sizeof(bdbuf_pool)); + if (bd_ctx.pool == NULL) + { + return RTEMS_NO_MEMORY; + } + + Chain_Initialize_empty(&bd_ctx.mod); + + /* Initialize buffer pools and roll out if something failed */ + for (i = 0; i < size; i++) + { + rc = bdbuf_initialize_pool(conf_table + i, i); + if (rc != RTEMS_SUCCESSFUL) + { + rtems_bdpool_id j; + for (j = 0; j < i - 1; j++) + { + bdbuf_release_pool(j); + } + return rc; + } + } + + /* Create buffer flush semaphore */ + rc = rtems_semaphore_create( + rtems_build_name('B', 'F', 'L', 'U'), 0, + RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, + &bd_ctx.flush_sema); + if (rc != RTEMS_SUCCESSFUL) + { + for (i = 0; i < size; i++) + bdbuf_release_pool(i); + free(bd_ctx.pool); + return rc; + } + + /* Create and start swapout task */ + rc = rtems_task_create( + rtems_build_name('B', 'S', 'W', 'P'), + SWAPOUT_PRIORITY, + SWAPOUT_STACK_SIZE, + RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT, + RTEMS_DEFAULT_ATTRIBUTES, + &bd_ctx.swapout_task); + if (rc != RTEMS_SUCCESSFUL) + { + rtems_semaphore_delete(bd_ctx.flush_sema); + for (i = 0; i < size; i++) + bdbuf_release_pool(i); + free(bd_ctx.pool); + return rc; + } + + rc = rtems_task_start(bd_ctx.swapout_task, bdbuf_swapout_task, 0); + if (rc != RTEMS_SUCCESSFUL) + { + rtems_task_delete(bd_ctx.swapout_task); + rtems_semaphore_delete(bd_ctx.flush_sema); + for (i = 0; i < size; i++) + bdbuf_release_pool(i); + free(bd_ctx.pool); + return rc; + } + + return RTEMS_SUCCESSFUL; +} + +/* find_or_assign_buffer -- + * Looks for buffer already assigned for this dev/block. If one is found + * obtain block buffer. If specified block already cached (i.e. there's + * block in the _modified_, or _recently_used_), return address + * of appropriate buffer descriptor and increment reference counter to 1. + * If block is not cached, allocate new buffer and return it. Data + * shouldn't be read to the buffer from media; buffer contains arbitrary + * data. This primitive may be blocked if there are no free buffer + * descriptors available and there are no unused non-modified (or + * synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (constructed of major and minor device number + * block - linear media block number + * ret_buf - address of the variable to store address of found descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFEECTS: + * bufget_sema may be obtained by this primitive + * + * NOTE: + * It is assumed that primitive invoked when thread preemption is disabled. + */ +static rtems_status_code +find_or_assign_buffer(disk_device *dd, + blkdev_bnum block, + bdbuf_buffer **ret_buf) +{ + bdbuf_buffer *bd_buf; + bdbuf_pool *bd_pool; + rtems_status_code rc; + dev_t device; + ISR_Level level; + + int blksize; + + device = dd->dev; + bd_pool = bd_ctx.pool + dd->pool; + blksize = dd->block_size; + +again: + /* Looking for buffer descriptor used for this dev/block. */ + bd_buf = avl_search(&bd_pool->tree, device, block); + + if (bd_buf == NULL) + { + /* Try to obtain semaphore without waiting first. It is the most + frequent case when reasonable number of buffers configured. If + it is failed, obtain semaphore blocking on it. In this case + it should be checked that appropriate buffer hasn't been loaded + by another thread, because this thread is preempted */ + rc = rtems_semaphore_obtain(bd_pool->bufget_sema, RTEMS_NO_WAIT, 0); + if (rc == RTEMS_UNSATISFIED) + { + rc = rtems_semaphore_obtain(bd_pool->bufget_sema, + RTEMS_WAIT, RTEMS_NO_TIMEOUT); + bd_buf = avl_search(&bd_pool->tree, device, block); + if (bd_buf != NULL) + rtems_semaphore_release(bd_pool->bufget_sema); + } + } + + if (bd_buf == NULL) + { + /* Assign new buffer descriptor */ + if (_Chain_Is_empty(&bd_pool->free)) + { + bd_buf = (bdbuf_buffer *)Chain_Get(&bd_pool->lru); + if (bd_buf != NULL) + { + int avl_result; + avl_result = avl_remove(&bd_pool->tree, bd_buf); + if (avl_result != 0) + { + rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY); + return RTEMS_INTERNAL_ERROR; + } + } + } + else + { + bd_buf = (bdbuf_buffer *)Chain_Get(&(bd_pool->free)); + } + + if (bd_buf == NULL) + { + goto again; + } + else + { + bd_buf->dev = device; + bd_buf->block = block; +#ifdef BINARY_TREE + bd_buf->avl.left = NULL; + bd_buf->avl.right = NULL; +#else + bd_buf->avl.link[0] = NULL; + bd_buf->avl.link[1] = NULL; +#endif + bd_buf->use_count = 1; + bd_buf->modified = bd_buf->actual = bd_buf->in_progress = FALSE; + bd_buf->status = RTEMS_SUCCESSFUL; + + if (avl_insert(&bd_pool->tree, bd_buf) != 0) + { + rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY); + return RTEMS_INTERNAL_ERROR; + } + + *ret_buf = bd_buf; + + return RTEMS_SUCCESSFUL; + } + } + else + { + /* Buffer descriptor already assigned for this dev/block */ + if (bd_buf->use_count == 0) + { + /* If we are removing from lru list, obtain the bufget_sema + * first. If we are removing from mod list, obtain flush sema. + * It should be obtained without blocking because we know + * that our buffer descriptor is in the list. */ + if (bd_buf->modified) + { + rc = rtems_semaphore_obtain(bd_ctx.flush_sema, + RTEMS_NO_WAIT, 0); + } + else + { + rc = rtems_semaphore_obtain(bd_pool->bufget_sema, + RTEMS_NO_WAIT, 0); + } + /* It is possible that we couldn't obtain flush or bufget sema + * although buffer in the appropriate chain is available: + * semaphore may be released to swapout task, but this task + * actually did not start to process it. */ + if (rc == RTEMS_UNSATISFIED) + rc = RTEMS_SUCCESSFUL; + if (rc != RTEMS_SUCCESSFUL) + { + rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY); + return RTEMS_INTERNAL_ERROR; + } + + /* Buffer descriptor is linked to the lru or mod chain. Remove + it from there. */ + Chain_Extract(&bd_buf->link); + } + bd_buf->use_count++; + while (bd_buf->in_progress != 0) + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, + WATCHDOG_NO_TIMEOUT, level); + } + + *ret_buf = bd_buf; + return RTEMS_SUCCESSFUL; + } +} + +/* rtems_bdbuf_get -- + * Obtain block buffer. If specified block already cached (i.e. there's + * block in the _modified_, or _recently_used_), return address + * of appropriate buffer descriptor and increment reference counter to 1. + * If block is not cached, allocate new buffer and return it. Data + * shouldn't be read to the buffer from media; buffer may contains + * arbitrary data. This primitive may be blocked if there are no free + * buffer descriptors available and there are no unused non-modified + * (or synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (constructed of major and minor device number) + * block - linear media block number + * bd - address of variable to store pointer to the buffer descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * bufget_sema semaphore obtained by this primitive. + */ +rtems_status_code +rtems_bdbuf_get(dev_t device, blkdev_bnum block, bdbuf_buffer **bd) +{ + rtems_status_code rc; + disk_device *dd; + disk_device *pdd; + preemption_key key; + + /* + * Convert logical dev/block to physical one + */ + dd = rtems_disk_lookup(device); + if (dd == NULL) + return RTEMS_INVALID_ID; + + if (block >= dd->size) + { + rtems_disk_release(dd); + return RTEMS_INVALID_NUMBER; + } + + pdd = dd->phys_dev; + block += dd->start; + rtems_disk_release(dd); + + DISABLE_PREEMPTION(key); + rc = find_or_assign_buffer(pdd, block, bd); + ENABLE_PREEMPTION(key); + + if (rc != RTEMS_SUCCESSFUL) + return rc; + + return RTEMS_SUCCESSFUL; +} + +/* bdbuf_initialize_transfer_sema -- + * Initialize transfer_sema mutex semaphore associated with buffer + * descriptor. + */ +static inline void +bdbuf_initialize_transfer_sema(bdbuf_buffer *bd_buf) +{ + CORE_mutex_Attributes mutex_attr; + mutex_attr.lock_nesting_behavior = CORE_MUTEX_NESTING_BLOCKS; + mutex_attr.only_owner_release = FALSE; + mutex_attr.discipline = CORE_MUTEX_DISCIPLINES_FIFO; + mutex_attr.priority_ceiling = 0; + + _CORE_mutex_Initialize(&bd_buf->transfer_sema, OBJECTS_NO_CLASS, + &mutex_attr, CORE_MUTEX_LOCKED, NULL); +} + +/* bdbuf_write_transfer_done -- + * Callout function. Invoked by block device driver when data transfer + * to device (write) is completed. This function may be invoked from + * interrupt handler. + * + * PARAMETERS: + * arg - arbitrary argument specified in block device request + * structure (in this case - pointer to the appropriate + * bdbuf_buffer buffer descriptor structure). + * status - I/O completion status + * error - errno error code if status != RTEMS_SUCCESSFUL + * + * RETURNS: + * none + */ +static void +bdbuf_write_transfer_done(void *arg, rtems_status_code status, int error) +{ + bdbuf_buffer *bd_buf = arg; + bd_buf->status = status; + bd_buf->error = error; + bd_buf->in_progress = bd_buf->modified = FALSE; + _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL); + _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL, + CORE_MUTEX_STATUS_SUCCESSFUL); +} + +/* bdbuf_read_transfer_done -- + * Callout function. Invoked by block device driver when data transfer + * from device (read) is completed. This function may be invoked from + * interrupt handler. + * + * PARAMETERS: + * arg - arbitrary argument specified in block device request + * structure (in this case - pointer to the appropriate + * bdbuf_buffer buffer descriptor structure). + * status - I/O completion status + * error - errno error code if status != RTEMS_SUCCESSFUL + * + * RETURNS: + * none + */ +static void +bdbuf_read_transfer_done(void *arg, rtems_status_code status, int error) +{ + bdbuf_buffer *bd_buf = arg; + bd_buf->status = status; + bd_buf->error = error; + _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL); + _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL, + CORE_MUTEX_STATUS_SUCCESSFUL); +} + +/* rtems_bdbuf_read -- + * (Similar to the rtems_bdbuf_get, except reading data from media) + * Obtain block buffer. If specified block already cached, return address + * of appropriate buffer and increment reference counter to 1. If block is + * not cached, allocate new buffer and read data to it from the media. + * This primitive may be blocked on waiting until data to be read from + * media, if there are no free buffer descriptors available and there are + * no unused non-modified (or synchronized with media) buffers available. + * + * PARAMETERS: + * device - device number (consists of major and minor device number) + * block - linear media block number + * bd - address of variable to store pointer to the buffer descriptor + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * bufget_sema and transfer_sema semaphores obtained by this primitive. + */ +rtems_status_code +rtems_bdbuf_read(dev_t device, + blkdev_bnum block, + bdbuf_buffer **bd) +{ + preemption_key key; + ISR_Level level; + + bdbuf_buffer *bd_buf; + rtems_status_code rc; + int result; + disk_device *dd; + disk_device *pdd; + blkdev_request1 req; + + dd = rtems_disk_lookup(device); + if (dd == NULL) + return RTEMS_INVALID_ID; + + if (block >= dd->size) + { + rtems_disk_release(dd); + return RTEMS_INVALID_NUMBER; + } + + pdd = dd->phys_dev; + block += dd->start; + + DISABLE_PREEMPTION(key); + rc = find_or_assign_buffer(pdd, block, &bd_buf); + + if (rc != RTEMS_SUCCESSFUL) + { + ENABLE_PREEMPTION(key); + rtems_disk_release(dd); + return rc; + } + + if (!bd_buf->actual) + { + bd_buf->in_progress = 1; + + req.req.req = BLKDEV_REQ_READ; + req.req.req_done = bdbuf_read_transfer_done; + req.req.done_arg = bd_buf; + req.req.start = block; + req.req.count = 1; + req.req.bufnum = 1; + req.req.bufs[0].length = dd->block_size; + req.req.bufs[0].buffer = bd_buf->buffer; + + bdbuf_initialize_transfer_sema(bd_buf); + result = dd->ioctl(device, BLKIO_REQUEST, &req); + if (result == -1) + { + bd_buf->status = RTEMS_IO_ERROR; + bd_buf->error = errno; + bd_buf->actual = FALSE; + } + else + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, + WATCHDOG_NO_TIMEOUT, level); + bd_buf->actual = TRUE; + } + bd_buf->in_progress = FALSE; + } + rtems_disk_release(dd); + + ENABLE_PREEMPTION(key); + + *bd = bd_buf; + + return RTEMS_SUCCESSFUL; +} + + +/* bdbuf_release -- + * Release buffer. Decrease buffer usage counter. If it is zero, further + * processing depends on modified attribute. If buffer was modified, it + * is inserted into mod chain and swapout task waken up. If buffer was + * not modified, it is returned to the end of lru chain making it available + * for further use. + * + * PARAMETERS: + * bd_buf - pointer to the released buffer descriptor. + * + * RETURNS: + * RTEMS_SUCCESSFUL if buffer released successfully, or error code if + * error occured. + * + * NOTE: + * This is internal function. It is assumed that task made non-preemptive + * before its invocation. + */ +static rtems_status_code +bdbuf_release(bdbuf_buffer *bd_buf) +{ + bdbuf_pool *bd_pool; + rtems_status_code rc = RTEMS_SUCCESSFUL; + + if (bd_buf->use_count <= 0) + return RTEMS_INTERNAL_ERROR; + + bd_pool = bd_ctx.pool + bd_buf->pool; + + bd_buf->use_count--; + + if (bd_buf->use_count == 0) + { + if (bd_buf->modified) + { + + /* Buffer was modified. Insert buffer to the modified buffers + * list and initiate flushing. */ + Chain_Append(&bd_ctx.mod, &bd_buf->link); + + /* Release the flush_sema */ + rc = rtems_semaphore_release(bd_ctx.flush_sema); + } + else + { + /* Buffer was not modified. Add this descriptor to the + * end of lru chain and make it available for reuse. */ + Chain_Append(&bd_pool->lru, &bd_buf->link); + rc = rtems_semaphore_release(bd_pool->bufget_sema); + } + } + return rc; +} + + +/* rtems_bdbuf_release -- + * Release buffer allocated before. This primitive decrease the + * usage counter. If it is zero, further destiny of buffer depends on + * 'modified' status. If buffer was modified, it is placed to the end of + * mod list and flush task waken up. If buffer was not modified, + * it is placed to the end of lru list, and bufget_sema released, allowing + * to reuse this buffer. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * flush_sema and bufget_sema semaphores may be released by this primitive. + */ +rtems_status_code +rtems_bdbuf_release(bdbuf_buffer *bd_buf) +{ + preemption_key key; + rtems_status_code rc = RTEMS_SUCCESSFUL; + + if (bd_buf == NULL) + return RTEMS_INVALID_ADDRESS; + + DISABLE_PREEMPTION(key); + + rc = bdbuf_release(bd_buf); + + ENABLE_PREEMPTION(key); + + return rc; +} + +/* rtems_bdbuf_release_modified -- + * Release buffer allocated before, assuming that it is _modified_ by + * it's owner. This primitive decrease usage counter for buffer, mark + * buffer descriptor as modified. If usage counter is 0, insert it at + * end of mod chain and release flush_sema semaphore to activate the + * flush task. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * flush_sema semaphore may be released by this primitive. + */ +rtems_status_code +rtems_bdbuf_release_modified(bdbuf_buffer *bd_buf) +{ + preemption_key key; + rtems_status_code rc = RTEMS_SUCCESSFUL; + + if (bd_buf == NULL) + return RTEMS_INVALID_ADDRESS; + + DISABLE_PREEMPTION(key); + + if (!bd_buf->modified) + { + bdbuf_initialize_transfer_sema(bd_buf); + } + bd_buf->modified = TRUE; + bd_buf->actual = TRUE; + rc = bdbuf_release(bd_buf); + + ENABLE_PREEMPTION(key); + + return rc; +} + +/* rtems_bdbuf_sync -- + * Wait until specified buffer synchronized with disk. Invoked on exchanges + * critical for data consistency on the media. This primitive mark owned + * block as modified, decrease usage counter. If usage counter is 0, + * block inserted to the mod chain and flush_sema semaphore released. + * Finally, primitives blocked on transfer_sema semaphore. + * + * PARAMETERS: + * bd_buf - pointer to the bdbuf_buffer structure previously obtained using + * get/read primitive. + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + * + * SIDE EFFECTS: + * Primitive may be blocked on transfer_sema semaphore. + */ +rtems_status_code +rtems_bdbuf_sync(bdbuf_buffer *bd_buf) +{ + preemption_key key; + ISR_Level level; + rtems_status_code rc = RTEMS_SUCCESSFUL; + + if (bd_buf == NULL) + return RTEMS_INVALID_ADDRESS; + + DISABLE_PREEMPTION(key); + + if (!bd_buf->modified) + { + bdbuf_initialize_transfer_sema(bd_buf); + } + bd_buf->modified = TRUE; + bd_buf->actual = TRUE; + + rc = bdbuf_release(bd_buf); + + if (rc == RTEMS_SUCCESSFUL) + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, + WATCHDOG_NO_TIMEOUT, level); + } + + ENABLE_PREEMPTION(key); + + return rc; +} + +/* rtems_bdbuf_syncdev -- + * Synchronize with disk all buffers containing the blocks belonging to + * specified device. + * + * PARAMETERS: + * dev - block device number + * + * RETURNS: + * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully + * or error code if error is occured) + */ +rtems_status_code +rtems_bdbuf_syncdev(dev_t dev) +{ + preemption_key key; + ISR_Level level; + + bdbuf_buffer *bd_buf; + disk_device *dd; + bdbuf_pool *pool; + + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_ID; + + pool = bd_ctx.pool + dd->pool; + + DISABLE_PREEMPTION(key); + do { + bd_buf = avl_search_for_sync(&pool->tree, dd); + if (bd_buf != NULL /* && bd_buf->modified */) + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, + WATCHDOG_NO_TIMEOUT, level); + } + } while (bd_buf != NULL); + ENABLE_PREEMPTION(key); + return rtems_disk_release(dd); +} + +/* bdbuf_swapout_task -- + * Body of task which take care on flushing modified buffers to the + * disk. + */ +static rtems_task +bdbuf_swapout_task(rtems_task_argument unused) +{ + rtems_status_code rc; + int result; + ISR_Level level; + bdbuf_buffer *bd_buf; + bdbuf_pool *bd_pool; + disk_device *dd; + blkdev_request1 req; + + while (1) + { + rc = rtems_semaphore_obtain(bd_ctx.flush_sema, RTEMS_WAIT, 0); + if (rc != RTEMS_SUCCESSFUL) + { + rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_SWAPOUT); + } + + bd_buf = (bdbuf_buffer *)Chain_Get(&bd_ctx.mod); + if (bd_buf == NULL) + { + /* It is possible that flush_sema semaphore will be released, but + * buffer to be removed from mod chain before swapout task start + * its processing. */ + continue; + } + + bd_buf->in_progress = TRUE; + bd_buf->use_count++; + bd_pool = bd_ctx.pool + bd_buf->pool; + dd = rtems_disk_lookup(bd_buf->dev); + + req.req.req = BLKDEV_REQ_WRITE; + req.req.req_done = bdbuf_write_transfer_done; + req.req.done_arg = bd_buf; + req.req.start = bd_buf->block + dd->start; + req.req.count = 1; + req.req.bufnum = 1; + req.req.bufs[0].length = dd->block_size; + req.req.bufs[0].buffer = bd_buf->buffer; + + /* transfer_sema initialized when bd_buf inserted in the mod chain + first time */ + result = dd->ioctl(dd->phys_dev->dev, BLKIO_REQUEST, &req); + + rtems_disk_release(dd); + + if (result == -1) + { + bd_buf->status = RTEMS_IO_ERROR; + bd_buf->error = errno; + /* Release tasks waiting on syncing this buffer */ + _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL, + CORE_MUTEX_STATUS_SUCCESSFUL); + } + else + { + if (bd_buf->in_progress) + { + _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, 0, level); + } + } + bd_buf->use_count--; + + /* Another task have chance to use this buffer, or even + * modify it. If buffer is not in use, insert it in appropriate chain + * and release semaphore */ + if (bd_buf->use_count == 0) + { + if (bd_buf->modified) + { + Chain_Append(&bd_ctx.mod, &bd_buf->link); + rc = rtems_semaphore_release(bd_ctx.flush_sema); + } + else + { + Chain_Append(&bd_pool->lru, &bd_buf->link); + rc = rtems_semaphore_release(bd_pool->bufget_sema); + } + } + } +} + +/* rtems_bdbuf_find_pool -- + * Find first appropriate buffer pool. This primitive returns the index + * of first buffer pool which block size is greater than or equal to + * specified size. + * + * PARAMETERS: + * block_size - requested block size + * pool - placeholder for result + * + * RETURNS: + * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully, + * RTEMS_INVALID_SIZE if specified block size is invalid (not a power + * of 2), RTEMS_NOT_DEFINED if buffer pool for this or greater block size + * is not configured. + */ +rtems_status_code +rtems_bdbuf_find_pool(int block_size, rtems_bdpool_id *pool) +{ + rtems_bdpool_id i; + bdbuf_pool *p; + int cursize = INT_MAX; + rtems_bdpool_id curid = -1; + rtems_boolean found = FALSE; + int j; + + for (j = block_size; (j != 0) && ((j & 1) == 0); j >>= 1); + if (j != 1) + return RTEMS_INVALID_SIZE; + + for (i = 0, p = bd_ctx.pool; i < bd_ctx.npools; i++, p++) + { + if ((p->blksize >= block_size) && + (p->blksize < cursize)) + { + curid = i; + cursize = p->blksize; + found = TRUE; + } + } + + if (found) + { + if (pool != NULL) + *pool = curid; + return RTEMS_SUCCESSFUL; + } + else + { + return RTEMS_NOT_DEFINED; + } +} + +/* rtems_bdbuf_get_pool_info -- + * Obtain characteristics of buffer pool with specified number. + * + * PARAMETERS: + * pool - buffer pool number + * block_size - block size for which buffer pool is configured returned + * there + * blocks - number of buffers in buffer pool returned there + * + * RETURNS: + * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully, + * RTEMS_INVALID_NUMBER if appropriate buffer pool is not configured. + * + * NOTE: + * Buffer pools enumerated contiguously starting from 0. + */ +rtems_status_code +rtems_bdbuf_get_pool_info(rtems_bdpool_id pool, int *block_size, + int *blocks) +{ + if (pool >= bd_ctx.npools) + return RTEMS_INVALID_NUMBER; + + if (block_size != NULL) + { + *block_size = bd_ctx.pool[pool].blksize; + } + + if (blocks != NULL) + { + *blocks = bd_ctx.pool[pool].nblks; + } + + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/libblock/src/blkdev.c b/c/src/libblock/src/blkdev.c new file mode 100644 index 0000000000..1004ec4de3 --- /dev/null +++ b/c/src/libblock/src/blkdev.c @@ -0,0 +1,243 @@ +/* + * blkdev.h - block device driver generic support + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#include +#include +#include + +#include "rtems/diskdevs.h" +#include "rtems/bdbuf.h" + +/* rtems_blkdev_generic_read -- + * Generic block device read primitive. Implemented using block device + * buffer management primitives. + */ +rtems_device_driver +rtems_blkdev_generic_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + rtems_libio_rw_args_t *args = arg; + int block_size_log2; + int block_size; + char *buf; + unsigned int count; + unsigned int block; + unsigned int blkofs; + dev_t dev; + disk_device *dd; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + block_size_log2 = dd->block_size_log2; + block_size = dd->block_size; + + buf = args->buffer; + count = args->count; + args->bytes_moved = 0; + + block = args->offset >> block_size_log2; + blkofs = args->offset & (block_size - 1); + + while (count > 0) + { + bdbuf_buffer *diskbuf; + int copy; + rtems_status_code rc; + + rc = rtems_bdbuf_read(dev, block, &diskbuf); + if (rc != RTEMS_SUCCESSFUL) + return rc; + copy = block_size - blkofs; + if (copy > count) + copy = count; + memcpy(buf, (char *)diskbuf->buffer + blkofs, copy); + rc = rtems_bdbuf_release(diskbuf); + args->bytes_moved += copy; + if (rc != RTEMS_SUCCESSFUL) + return rc; + count -= copy; + buf += copy; + blkofs = 0; + block++; + } + return RTEMS_SUCCESSFUL; +} + +/* rtems_blkdev_generic_write -- + * Generic block device write primitive. Implemented using block device + * buffer management primitives. + */ +rtems_device_driver +rtems_blkdev_generic_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + rtems_libio_rw_args_t *args = arg; + int block_size_log2; + int block_size; + char *buf; + unsigned int count; + unsigned int block; + unsigned int blkofs; + dev_t dev; + rtems_status_code rc; + disk_device *dd; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + block_size_log2 = dd->block_size_log2; + block_size = dd->block_size; + + buf = args->buffer; + count = args->count; + args->bytes_moved = 0; + + block = args->offset >> block_size_log2; + blkofs = args->offset & (block_size - 1); + + while (count > 0) + { + bdbuf_buffer *diskbuf; + int copy; + + if ((blkofs == 0) && (count > block_size)) + rc = rtems_bdbuf_get(dev, block, &diskbuf); + else + rc = rtems_bdbuf_read(dev, block, &diskbuf); + if (rc != RTEMS_SUCCESSFUL) + return rc; + + copy = block_size - blkofs; + if (copy > count) + copy = count; + memcpy((char *)diskbuf->buffer + blkofs, buf, copy); + args->bytes_moved += copy; + + rc = rtems_bdbuf_release_modified(diskbuf); + if (rc != RTEMS_SUCCESSFUL) + return rc; + + count -= copy; + buf += copy; + blkofs = 0; + block++; + } + return RTEMS_SUCCESSFUL; +} + +/* blkdev_generic_open -- + * Generic block device open primitive. + */ +rtems_device_driver +rtems_blkdev_generic_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + dev_t dev; + disk_device *dd; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + dd->uses++; + + rtems_disk_release(dd); + + return RTEMS_SUCCESSFUL; +} + + +/* blkdev_generic_close -- + * Generic block device close primitive. + */ +rtems_device_driver +rtems_blkdev_generic_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + dev_t dev; + disk_device *dd; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + dd->uses--; + + rtems_disk_release(dd); + + return RTEMS_SUCCESSFUL; +} + +/* blkdev_generic_ioctl -- + * Generic block device ioctl primitive. + */ +rtems_device_driver +rtems_blkdev_generic_ioctl( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + rtems_libio_ioctl_args_t *args = arg; + dev_t dev; + disk_device *dd; + int rc; + + dev = rtems_filesystem_make_dev_t(major, minor); + dd = rtems_disk_lookup(dev); + if (dd == NULL) + return RTEMS_INVALID_NUMBER; + + switch (args->command) + { + case BLKIO_GETBLKSIZE: + args->ioctl_return = dd->block_size; + break; + + case BLKIO_GETSIZE: + args->ioctl_return = dd->size; + break; + + case BLKIO_SYNCDEV: + rc = rtems_bdbuf_syncdev(dd->dev); + args->ioctl_return = (rc == RTEMS_SUCCESSFUL ? 0 : -1); + break; + + case BLKIO_REQUEST: + { + blkdev_request *req = args->buffer; + req->start += dd->start; + args->ioctl_return = dd->ioctl(dd->phys_dev->dev, args->command, + req); + break; + } + + default: + args->ioctl_return = dd->ioctl(dd->phys_dev->dev, args->command, + args->buffer); + break; + } + rtems_disk_release(dd); + + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/libblock/src/diskdevs.c b/c/src/libblock/src/diskdevs.c new file mode 100644 index 0000000000..a9d6035e8b --- /dev/null +++ b/c/src/libblock/src/diskdevs.c @@ -0,0 +1,631 @@ +/* + * diskdevs.c - Physical and logical block devices (disks) support + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + + +#include +#include +#include +#include + +#include "rtems/diskdevs.h" +#include "rtems/bdbuf.h" + +#define DISKTAB_INITIAL_SIZE 32 + +/* Table of disk devices having the same major number */ +struct disk_device_table { + disk_device **minor; /* minor-indexed disk device table */ + int size; /* Number of entries in the table */ +}; + +/* Pointer to [major].minor[minor] indexed array of disk devices */ +static struct disk_device_table *disktab; + +/* Number of allocated entries in disktab table */ +static int disktab_size; + +/* Mutual exclusion semaphore for disk devices table */ +static rtems_id diskdevs_mutex; + +/* Flag meaning that disk I/O, buffering etc. already has been initialized. */ +static boolean disk_io_initialized = FALSE; + +/* diskdevs data structures protection flag. + * Normally, only table lookup operations performed. It is quite fast, so + * it is possible to done lookups when interrupts are disabled, avoiding + * obtaining the semaphore. This flags sets immediately after entering in + * mutex-protected section and cleared before leaving this section in + * "big" primitives like add/delete new device etc. Lookup function first + * disable interrupts and check this flag. If it is set, lookup function + * will be blocked on semaphore and lookup operation will be performed in + * semaphore-protected code. If it is not set (very-very frequent case), + * we can do lookup safely, enable interrupts and return result. + */ +static volatile rtems_boolean diskdevs_protected; + +/* create_disk_entry -- + * Return pointer to the disk_entry structure for the specified device, or + * create one if it is not exists. + * + * PARAMETERS: + * dev - device id (major, minor) + * + * RETURNS: + * pointer to the disk device descirptor entry, or NULL if no memory + * available for its creation. + */ +static disk_device * +create_disk_entry(dev_t dev) +{ + rtems_device_major_number major; + rtems_device_minor_number minor; + struct disk_device **d; + + rtems_filesystem_split_dev_t (dev, major, minor); + + if (major >= disktab_size) + { + struct disk_device_table *p; + int newsize; + int i; + newsize = disktab_size * 2; + if (major >= newsize) + newsize = major + 1; + p = realloc(disktab, sizeof(struct disk_device_table) * newsize); + if (p == NULL) + return NULL; + p += disktab_size; + for (i = disktab_size; i < newsize; i++, p++) + { + p->minor = NULL; + p->size = 0; + } + disktab_size = newsize; + } + + if ((disktab[major].minor == NULL) || + (minor >= disktab[major].size)) + { + int newsize; + disk_device **p; + int i; + int s = disktab[major].size; + + if (s == 0) + newsize = DISKTAB_INITIAL_SIZE; + else + newsize = s * 2; + if (minor >= newsize) + newsize = minor + 1; + + p = realloc(disktab[major].minor, sizeof(disk_device *) * newsize); + if (p == NULL) + return NULL; + disktab[major].minor = p; + p += s; + for (i = s; i < newsize; i++, p++) + *p = NULL; + disktab[major].size = newsize; + } + + d = disktab[major].minor + minor; + if (*d == NULL) + { + *d = calloc(1, sizeof(disk_device)); + } + return *d; +} + +/* get_disk_entry -- + * Get disk device descriptor by device number. + * + * PARAMETERS: + * dev - block device number + * + * RETURNS: + * Pointer to the disk device descriptor corresponding to the specified + * device number, or NULL if disk device with such number not exists. + */ +static inline disk_device * +get_disk_entry(dev_t dev) +{ + rtems_device_major_number major; + rtems_device_minor_number minor; + struct disk_device_table *dtab; + + rtems_filesystem_split_dev_t (dev, major, minor); + + if ((major >= disktab_size) || (disktab == NULL)) + return NULL; + + dtab = disktab + major; + + if ((minor >= dtab->size) || (dtab->minor == NULL)) + return NULL; + + return dtab->minor[minor]; +} + +/* create_disk -- + * Check that disk entry for specified device number is not defined + * and create it. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * name - character name of device (e.g. /dev/hda) + * disdev - placeholder for pointer to created disk descriptor + * + * RETURNS: + * RTEMS_SUCCESSFUL if disk entry successfully created, or + * error code if error occured (device already registered, + * no memory available). + */ +static rtems_status_code +create_disk(dev_t dev, char *name, disk_device **diskdev) +{ + disk_device *dd; + char *n; + + dd = get_disk_entry(dev); + if (dd != NULL) + { + return RTEMS_RESOURCE_IN_USE; + } + + if (name == NULL) + { + n = NULL; + } + else + { + int nlen = strlen(name) + 1; + n = malloc(nlen); + if (n == NULL) + return RTEMS_NO_MEMORY; + strncpy(n, name, nlen); + } + + dd = create_disk_entry(dev); + if (dd == NULL) + { + free(n); + return RTEMS_NO_MEMORY; + } + + dd->dev = dev; + dd->name = n; + + *diskdev = dd; + + return RTEMS_SUCCESSFUL; +} + +/* rtems_disk_create_phys -- + * Create physical disk entry. This function usually invoked from + * block device driver initialization code when physical device + * detected in the system. Device driver should provide ioctl handler + * to allow block device access operations. This primitive will register + * device in rtems (invoke rtems_io_register_name). + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * block_size - size of disk block (minimum data transfer unit); must be + * power of 2 + * disk_size - number of blocks on device + * handler - IOCTL handler (function providing basic block input/output + * request handling BIOREQUEST and other device management + * operations) + * name - character name of device (e.g. /dev/hda) + * + * RETURNS: + * RTEMS_SUCCESSFUL if information about new physical disk added, or + * error code if error occured (device already registered, wrong block + * size value, no memory available). + */ +rtems_status_code +rtems_disk_create_phys(dev_t dev, int block_size, int disk_size, + block_device_ioctl handler, + char *name) +{ + int bs_log2; + int i; + disk_device *dd; + rtems_status_code rc; + rtems_bdpool_id pool; + rtems_device_major_number major; + rtems_device_minor_number minor; + + rtems_filesystem_split_dev_t (dev, major, minor); + + + for (bs_log2 = 0, i = block_size; (i & 1) == 0; i >>= 1, bs_log2++); + if ((bs_log2 < 9) || (i != 1)) /* block size < 512 or not power of 2 */ + return RTEMS_INVALID_NUMBER; + + rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + return rc; + diskdevs_protected = TRUE; + + rc = rtems_bdbuf_find_pool(block_size, &pool); + if (rc != RTEMS_SUCCESSFUL) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return rc; + } + + rc = create_disk(dev, name, &dd); + if (rc != RTEMS_SUCCESSFUL) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return rc; + } + + dd->phys_dev = dd; + dd->uses = 0; + dd->start = 0; + dd->size = disk_size; + dd->block_size = block_size; + dd->block_size_log2 = bs_log2; + dd->ioctl = handler; + dd->pool = pool; + + rc = rtems_io_register_name(name, major, minor); + + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + + return rc; +} + +/* rtems_disk_create_log -- + * Create logical disk entry. Logical disk is contiguous area on physical + * disk. Disk may be splitted to several logical disks in several ways: + * manually or using information stored in blocks on physical disk + * (DOS-like partition table, BSD disk label, etc). This function usually + * invoked from application when application-specific splitting are in use, + * or from generic code which handle different logical disk organizations. + * This primitive will register device in rtems (invoke + * rtems_io_register_name). + * + * PARAMETERS: + * dev - logical device identifier (major, minor numbers) + * phys - physical device (block device which holds this logical disk) + * identifier + * start - starting block number on the physical device + * size - logical disk size in blocks + * name - logical disk name + * + * RETURNS: + * RTEMS_SUCCESSFUL if logical device successfully added, or error code + * if error occured (device already registered, no physical device + * exists, logical disk is out of physical disk boundaries, no memory + * available). + */ +rtems_status_code +rtems_disk_create_log(dev_t dev, dev_t phys, int start, int size, char *name) +{ + disk_device *dd; + disk_device *pdd; + rtems_status_code rc; + rtems_device_major_number major; + rtems_device_minor_number minor; + + rtems_filesystem_split_dev_t (dev, major, minor); + + rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + return rc; + diskdevs_protected = TRUE; + + pdd = get_disk_entry(phys); + if (pdd == NULL) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return RTEMS_INVALID_NUMBER; + } + + rc = create_disk(dev, name, &dd); + if (rc != RTEMS_SUCCESSFUL) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return rc; + } + + dd->phys_dev = pdd; + dd->uses = 0; + dd->start = start; + dd->size = size; + dd->block_size = pdd->block_size; + dd->block_size_log2 = pdd->block_size_log2; + dd->ioctl = pdd->ioctl; + + rc = rtems_io_register_name(name, major, minor); + + diskdevs_protected = FALSE; + rc = rtems_semaphore_release(diskdevs_mutex); + + return rc; +} + +/* rtems_disk_delete -- + * Delete physical or logical disk device. Device may be deleted if its + * use counter (and use counters of all logical devices - if it is + * physical device) equal to 0. When physical device deleted, + * all logical devices deleted inherently. Appropriate devices removed + * from "/dev" filesystem. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * + * RETURNS: + * RTEMS_SUCCESSFUL if block device successfully deleted, or error code + * if error occured (device is not defined, device is in use). + */ +rtems_status_code +rtems_disk_delete(dev_t dev) +{ + rtems_status_code rc; + int used; + rtems_device_major_number maj; + rtems_device_minor_number min; + + rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + return rc; + diskdevs_protected = TRUE; + + /* Check if this device is in use -- calculate usage counter */ + used = 0; + for (maj = 0; maj < disktab_size; maj++) + { + struct disk_device_table *dtab = disktab + maj; + if (dtab != NULL) + { + for (min = 0; min < dtab->size; min++) + { + disk_device *dd = dtab->minor[min]; + if ((dd != NULL) && (dd->phys_dev->dev == dev)) + used += dd->uses; + } + } + } + + if (used != 0) + { + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return RTEMS_RESOURCE_IN_USE; + } + + /* Delete this device and all of its logical devices */ + for (maj = 0; maj < disktab_size; maj++) + { + struct disk_device_table *dtab = disktab +maj; + if (dtab != NULL) + { + for (min = 0; min < dtab->size; min++) + { + disk_device *dd = dtab->minor[min]; + if ((dd != NULL) && (dd->phys_dev->dev == dev)) + { + unlink(dd->name); + free(dd->name); + free(dd); + dtab->minor[min] = NULL; + } + } + } + } + + diskdevs_protected = FALSE; + rc = rtems_semaphore_release(diskdevs_mutex); + return rc; +} + +/* rtems_disk_lookup -- + * Find block device descriptor by its device identifier. + * + * PARAMETERS: + * dev - device identifier (major, minor numbers) + * + * RETURNS: + * pointer to the block device descriptor, or NULL if no such device + * exists. + */ +disk_device * +rtems_disk_lookup(dev_t dev) +{ + rtems_interrupt_level level; + disk_device *dd; + rtems_status_code rc; + + rtems_interrupt_disable(level); + if (diskdevs_protected) + { + rtems_interrupt_enable(level); + rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, + RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + return NULL; + diskdevs_protected = TRUE; + dd = get_disk_entry(dev); + dd->uses++; + diskdevs_protected = FALSE; + rtems_semaphore_release(diskdevs_mutex); + return dd; + } + else + { + /* Frequent and quickest case */ + dd = get_disk_entry(dev); + dd->uses++; + rtems_interrupt_enable(level); + return dd; + } +} + +/* rtems_disk_release -- + * Release disk_device structure (decrement usage counter to 1). + * + * PARAMETERS: + * dd - pointer to disk device structure + * + * RETURNS: + * RTEMS_SUCCESSFUL + */ +rtems_status_code +rtems_disk_release(disk_device *dd) +{ + rtems_interrupt_level level; + rtems_interrupt_disable(level); + dd->uses--; + rtems_interrupt_enable(level); + return RTEMS_SUCCESSFUL; +} + +/* rtems_disk_next -- + * Disk device enumerator. Looking for device having device number larger + * than dev and return disk device descriptor for it. If there are no + * such device, NULL value returned. + * + * PARAMETERS: + * dev - device number (use -1 to start search) + * + * RETURNS: + * Pointer to the disk descriptor for next disk device, or NULL if all + * devices enumerated. + */ +disk_device * +rtems_disk_next(dev_t dev) +{ + rtems_device_major_number major; + rtems_device_minor_number minor; + struct disk_device_table *dtab; + + dev++; + rtems_filesystem_split_dev_t (dev, major, minor); + + if (major >= disktab_size) + return NULL; + + dtab = disktab + major; + while (TRUE) + { + if ((dtab == NULL) || (minor > dtab->size)) + { + major++; minor = 0; + if (major >= disktab_size) + return NULL; + dtab = disktab + major; + } + else if (dtab->minor[minor] == NULL) + { + minor++; + } + else + return dtab->minor[minor]; + } +} + +/* rtems_disk_initialize -- + * Initialization of disk device library (initialize all data structures, + * etc.) + * + * PARAMETERS: + * none + * + * RETURNS: + * RTEMS_SUCCESSFUL if library initialized, or error code if error + * occured. + */ +rtems_status_code +rtems_disk_io_initialize(void) +{ + rtems_status_code rc; + + if (disk_io_initialized) + return RTEMS_SUCCESSFUL; + + disktab_size = DISKTAB_INITIAL_SIZE; + disktab = calloc(disktab_size, sizeof(struct disk_device_table)); + if (disktab == NULL) + return RTEMS_NO_MEMORY; + + diskdevs_protected = FALSE; + rc = rtems_semaphore_create( + rtems_build_name('D', 'D', 'E', 'V'), 1, + RTEMS_FIFO | RTEMS_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, &diskdevs_mutex); + + if (rc != RTEMS_SUCCESSFUL) + { + free(disktab); + return rc; + } + + rc = rtems_bdbuf_init(rtems_bdbuf_configuration, + rtems_bdbuf_configuration_size); + + if (rc != RTEMS_SUCCESSFUL) + { + rtems_semaphore_delete(diskdevs_mutex); + free(disktab); + return rc; + } + + disk_io_initialized = 1; + return RTEMS_SUCCESSFUL; +} + +/* rtems_disk_io_done -- + * Release all resources allocated for disk device interface. + * + * PARAMETERS: + * none + * + * RETURNS: + * RTEMS_SUCCESSFUL if all resources released, or error code if error + * occured. + */ +rtems_status_code +rtems_disk_io_done(void) +{ + rtems_device_major_number maj; + rtems_device_minor_number min; + rtems_status_code rc; + + /* Free data structures */ + for (maj = 0; maj < disktab_size; maj++) + { + struct disk_device_table *dtab = disktab + maj; + if (dtab != NULL) + { + for (min = 0; min < dtab->size; min++) + { + disk_device *dd = dtab->minor[min]; + unlink(dd->name); + free(dd->name); + free(dd); + } + free(dtab); + } + } + free(disktab); + + rc = rtems_semaphore_release(diskdevs_mutex); + + /* XXX bdbuf should be released too! */ + disk_io_initialized = 0; + return rc; +} diff --git a/c/src/libblock/src/ramdisk.c b/c/src/libblock/src/ramdisk.c new file mode 100644 index 0000000000..08f42846e0 --- /dev/null +++ b/c/src/libblock/src/ramdisk.c @@ -0,0 +1,224 @@ +/* ramdisk.c -- RAM disk block device implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov + * + * @(#) $Id$ + */ + +#include +#include +#include +#include +#include + +#include "rtems/blkdev.h" +#include "rtems/diskdevs.h" +#include "rtems/ramdisk.h" + +#define RAMDISK_DEVICE_BASE_NAME "/dev/ramdisk" + +/* Internal RAM disk descriptor */ +struct ramdisk { + int block_size; /* RAM disk block size */ + int block_num; /* Number of blocks on this RAM disk */ + void *area; /* RAM disk memory area */ + rtems_boolean initialized;/* RAM disk is initialized */ + rtems_boolean malloced; /* != 0, if memory allocated by malloc for this + RAM disk */ +}; + +static struct ramdisk *ramdisk; +static int nramdisks; + +/* ramdisk_read -- + * RAM disk READ request handler. This primitive copies data from RAM + * disk to supplied buffer and invoke the callout function to inform + * upper layer that reading is completed. + * + * PARAMETERS: + * req - pointer to the READ block device request info + * + * RETURNS: + * ioctl return value + */ +static int +ramdisk_read(struct ramdisk *rd, blkdev_request *req) +{ + char *from; + rtems_unsigned32 i; + blkdev_sg_buffer *sg; + rtems_unsigned32 remains; + + from = (char *)rd->area + (req->start * rd->block_size); + remains = rd->block_size * req->count; + sg = req->bufs; + for (i = 0; (remains > 0) && (i < req->bufnum); i++, sg++) + { + int count = sg->length; + if (count > remains) + count = remains; + memcpy(sg->buffer, from, count); + remains -= count; + } + req->req_done(req->done_arg, RTEMS_SUCCESSFUL, 0); + return 0; +} + +/* ramdisk_write -- + * RAM disk WRITE request handler. This primitive copies data from + * supplied buffer to RAM disk and invoke the callout function to inform + * upper layer that writing is completed. + * + * PARAMETERS: + * req - pointer to the WRITE block device request info + * + * RETURNS: + * ioctl return value + */ +static int +ramdisk_write(struct ramdisk *rd, blkdev_request *req) +{ + char *to; + rtems_unsigned32 i; + blkdev_sg_buffer *sg; + rtems_unsigned32 remains; + + to = (char *)rd->area + (req->start * rd->block_size); + remains = rd->block_size * req->count; + sg = req->bufs; + for (i = 0; (remains > 0) && (i < req->bufnum); i++, sg++) + { + int count = sg->length; + if (count > remains) + count = remains; + memcpy(to, sg->buffer, count); + remains -= count; + } + req->req_done(req->done_arg, RTEMS_SUCCESSFUL, 0); + return 0; +} + +/* ramdisk_ioctl -- + * IOCTL handler for RAM disk device. + * + * PARAMETERS: + * dev - device number (major, minor number) + * req - IOCTL request code + * argp - IOCTL argument + * + * RETURNS: + * IOCTL return value + */ +static int +ramdisk_ioctl(dev_t dev, int req, void *argp) +{ + switch (req) + { + case BLKIO_REQUEST: + { + rtems_device_minor_number minor; + blkdev_request *r = argp; + struct ramdisk *rd; + + minor = rtems_filesystem_dev_minor_t(dev); + if ((minor >= nramdisks) || !ramdisk[minor].initialized) + { + errno = ENODEV; + return -1; + } + + rd = ramdisk + minor; + + switch (r->req) + { + case BLKDEV_REQ_READ: + return ramdisk_read(rd, r); + + case BLKDEV_REQ_WRITE: + return ramdisk_write(rd, r); + + default: + errno = EBADRQC; + return -1; + } + break; + } + + default: + errno = EBADRQC; + return -1; + } +} + +/* ramdisk_initialize -- + * RAM disk device driver initialization. Run through RAM disk + * configuration information and configure appropriate RAM disks. + * + * PARAMETERS: + * major - RAM disk major device number + * minor - minor device number, not applicable + * arg - initialization argument, not applicable + * + * RETURNS: + * none + */ +rtems_device_driver +ramdisk_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + rtems_device_minor_number i; + rtems_ramdisk_config *c = rtems_ramdisk_configuration; + struct ramdisk *r; + rtems_status_code rc; + + rc = rtems_disk_io_initialize(); + if (rc != RTEMS_SUCCESSFUL) + return rc; + + r = ramdisk = calloc(rtems_ramdisk_configuration_size, + sizeof(struct ramdisk)); + + for (i = 0; i < rtems_ramdisk_configuration_size; i++, c++, r++) + { + dev_t dev = rtems_filesystem_make_dev_t(major, i); + char name[sizeof(RAMDISK_DEVICE_BASE_NAME "0123456789")]; + snprintf(name, sizeof(name), RAMDISK_DEVICE_BASE_NAME "%d", i); + r->block_size = c->block_size; + r->block_num = c->block_num; + if (c->location == NULL) + { + r->malloced = TRUE; + r->area = malloc(r->block_size * r->block_num); + if (r->area == NULL) /* No enough memory for this disk */ + { + r->initialized = FALSE; + continue; + } + else + { + r->initialized = TRUE; + } + } + else + { + r->malloced = FALSE; + r->initialized = TRUE; + r->area = c->location; + } + rc = rtems_disk_create_phys(dev, c->block_size, c->block_num, + ramdisk_ioctl, name); + if (rc != RTEMS_SUCCESSFUL) + { + if (r->malloced) + { + free(r->area); + } + r->initialized = FALSE; + } + } + nramdisks = rtems_ramdisk_configuration_size; + return RTEMS_SUCCESSFUL; +} -- cgit v1.2.3