OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rtems-20020807/] [cpukit/] [libblock/] [src/] [bdbuf.c] - Diff between revs 1026 and 1765

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 1026 Rev 1765
/*
/*
 * Disk I/O buffering
 * Disk I/O buffering
 * Buffer managment
 * Buffer managment
 *
 *
 * Copyright (C) 2001 OKTET Ltd., St.-Peterburg, Russia
 * Copyright (C) 2001 OKTET Ltd., St.-Peterburg, Russia
 * Author: Andrey G. Ivanov <Andrey.Ivanov@oktet.ru>
 * Author: Andrey G. Ivanov <Andrey.Ivanov@oktet.ru>
 *         Victor V. Vengerov <vvv@oktet.ru>
 *         Victor V. Vengerov <vvv@oktet.ru>
 *         Alexander Kukuta <kam@oktet.ru>
 *         Alexander Kukuta <kam@oktet.ru>
 *
 *
 * @(#) bdbuf.c,v 1.3 2002/07/01 22:37:58 joel Exp
 * @(#) bdbuf.c,v 1.3 2002/07/01 22:37:58 joel Exp
 */
 */
 
 
#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__
#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__
#include <rtems.h>
#include <rtems.h>
#include <limits.h>
#include <limits.h>
#include <errno.h>
#include <errno.h>
#include <assert.h>
#include <assert.h>
 
 
#include "rtems/bdbuf.h"
#include "rtems/bdbuf.h"
 
 
/* Fatal errors: */
/* Fatal errors: */
#define BLKDEV_FATAL_ERROR(n) (('B' << 24) | ((n) & 0x00FFFFFF))
#define BLKDEV_FATAL_ERROR(n) (('B' << 24) | ((n) & 0x00FFFFFF))
#define BLKDEV_FATAL_BDBUF_CONSISTENCY BLKDEV_FATAL_ERROR(1)
#define BLKDEV_FATAL_BDBUF_CONSISTENCY BLKDEV_FATAL_ERROR(1)
#define BLKDEV_FATAL_BDBUF_SWAPOUT     BLKDEV_FATAL_ERROR(2)
#define BLKDEV_FATAL_BDBUF_SWAPOUT     BLKDEV_FATAL_ERROR(2)
 
 
 
 
#define SWAPOUT_PRIORITY 15
#define SWAPOUT_PRIORITY 15
#define SWAPOUT_STACK_SIZE (RTEMS_MINIMUM_STACK_SIZE * 2)
#define SWAPOUT_STACK_SIZE (RTEMS_MINIMUM_STACK_SIZE * 2)
 
 
static rtems_task bdbuf_swapout_task(rtems_task_argument unused);
static rtems_task bdbuf_swapout_task(rtems_task_argument unused);
 
 
/*
/*
 * The groups of the blocks with the same size are collected in the
 * 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
 * bd_pool. Note that a several of the buffer's groups with the
 * same size can exists.
 * same size can exists.
 */
 */
typedef struct bdbuf_pool
typedef struct bdbuf_pool
{
{
    bdbuf_buffer *tree;         /* Buffer descriptor lookup AVL tree root */
    bdbuf_buffer *tree;         /* Buffer descriptor lookup AVL tree root */
 
 
    Chain_Control free;         /* Free buffers list */
    Chain_Control free;         /* Free buffers list */
    Chain_Control lru;          /* Last recently used list */
    Chain_Control lru;          /* Last recently used list */
 
 
    int           blksize;      /* The size of the blocks (in bytes) */
    int           blksize;      /* The size of the blocks (in bytes) */
    int           nblks;        /* Number of blocks in this pool */
    int           nblks;        /* Number of blocks in this pool */
    rtems_id      bufget_sema;  /* Buffer obtain counting semaphore */
    rtems_id      bufget_sema;  /* Buffer obtain counting semaphore */
    void         *mallocd_bufs; /* Pointer to the malloc'd buffer memory,
    void         *mallocd_bufs; /* Pointer to the malloc'd buffer memory,
                                   or NULL, if buffer memory provided in
                                   or NULL, if buffer memory provided in
                                   buffer configuration */
                                   buffer configuration */
    bdbuf_buffer *bdbufs;       /* Pointer to table of buffer descriptors
    bdbuf_buffer *bdbufs;       /* Pointer to table of buffer descriptors
                                   allocated for this buffer pool. */
                                   allocated for this buffer pool. */
} bdbuf_pool;
} bdbuf_pool;
 
 
/* Buffering layer context definition */
/* Buffering layer context definition */
struct bdbuf_context {
struct bdbuf_context {
    bdbuf_pool    *pool;         /* Table of buffer pools */
    bdbuf_pool    *pool;         /* Table of buffer pools */
    int            npools;       /* Number of entries in pool table */
    int            npools;       /* Number of entries in pool table */
 
 
    Chain_Control  mod;          /* Modified buffers list */
    Chain_Control  mod;          /* Modified buffers list */
    rtems_id       flush_sema;   /* Buffer flush semaphore; counting
    rtems_id       flush_sema;   /* Buffer flush semaphore; counting
                                    semaphore; incremented when buffer
                                    semaphore; incremented when buffer
                                    flushed to the disk; decremented when
                                    flushed to the disk; decremented when
                                    buffer modified */
                                    buffer modified */
    rtems_id       swapout_task; /* Swapout task ID */
    rtems_id       swapout_task; /* Swapout task ID */
};
};
 
 
/* Block device request with a single buffer provided */
/* Block device request with a single buffer provided */
typedef struct blkdev_request1 {
typedef struct blkdev_request1 {
    blkdev_request   req;
    blkdev_request   req;
    blkdev_sg_buffer sg[1];
    blkdev_sg_buffer sg[1];
} blkdev_request1;
} blkdev_request1;
 
 
/* The static context of buffering layer */
/* The static context of buffering layer */
static struct bdbuf_context bd_ctx;
static struct bdbuf_context bd_ctx;
 
 
#define SAFE
#define SAFE
#ifdef SAFE
#ifdef SAFE
typedef rtems_mode preemption_key;
typedef rtems_mode preemption_key;
 
 
#define DISABLE_PREEMPTION(key) \
#define DISABLE_PREEMPTION(key) \
    do {                                                               \
    do {                                                               \
        rtems_task_mode(RTEMS_PREEMPT_MASK, RTEMS_NO_PREEMPT, &(key)); \
        rtems_task_mode(RTEMS_PREEMPT_MASK, RTEMS_NO_PREEMPT, &(key)); \
    } while (0)
    } while (0)
 
 
#define ENABLE_PREEMPTION(key) \
#define ENABLE_PREEMPTION(key) \
    do {                                                        \
    do {                                                        \
        rtems_mode temp;                                        \
        rtems_mode temp;                                        \
        rtems_task_mode(RTEMS_PREEMPT_MASK, (key), &temp);      \
        rtems_task_mode(RTEMS_PREEMPT_MASK, (key), &temp);      \
    } while (0)
    } while (0)
 
 
#else
#else
 
 
typedef boolean preemption_key;
typedef boolean preemption_key;
 
 
#define DISABLE_PREEMPTION(key) \
#define DISABLE_PREEMPTION(key) \
    do {                                             \
    do {                                             \
        (key) = _Thread_Executing->is_preemptible;   \
        (key) = _Thread_Executing->is_preemptible;   \
        _Thread_Executing->is_preemptible = 0;       \
        _Thread_Executing->is_preemptible = 0;       \
    } while (0)
    } while (0)
 
 
#define ENABLE_PREEMPTION(key) \
#define ENABLE_PREEMPTION(key) \
    do {                                             \
    do {                                             \
        _Thread_Executing->is_preemptible = (key);   \
        _Thread_Executing->is_preemptible = (key);   \
        if (_Thread_Evaluate_mode())                 \
        if (_Thread_Evaluate_mode())                 \
            _Thread_Dispatch();                      \
            _Thread_Dispatch();                      \
    } while (0)
    } while (0)
 
 
#endif
#endif
 
 
 
 
/* The default maximum height of 32 allows for AVL trees having
/* 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
   between 5,704,880 and 4,294,967,295 nodes, depending on order of
   insertion.  You may change this compile-time constant as you
   insertion.  You may change this compile-time constant as you
   wish. */
   wish. */
#ifndef AVL_MAX_HEIGHT
#ifndef AVL_MAX_HEIGHT
#define AVL_MAX_HEIGHT  32
#define AVL_MAX_HEIGHT  32
#endif
#endif
 
 
/*
/*
 * avl_search --
 * avl_search --
 *     Searches for the node with specified dev/block.
 *     Searches for the node with specified dev/block.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     root - pointer to the root node of the AVL-Tree.
 *     root - pointer to the root node of the AVL-Tree.
 *     dev, block - search key
 *     dev, block - search key
 *
 *
 * RETURNS:
 * RETURNS:
 *     NULL if node with specified dev/block not found
 *     NULL if node with specified dev/block not found
 *     non-NULL - pointer to the node with specified dev/block
 *     non-NULL - pointer to the node with specified dev/block
 */
 */
static bdbuf_buffer *
static bdbuf_buffer *
avl_search(bdbuf_buffer **root, dev_t dev, blkdev_bnum block)
avl_search(bdbuf_buffer **root, dev_t dev, blkdev_bnum block)
{
{
    bdbuf_buffer *p = *root;
    bdbuf_buffer *p = *root;
 
 
    while ((p != NULL) && ((p->dev != dev) || (p->block != block)))
    while ((p != NULL) && ((p->dev != dev) || (p->block != block)))
    {
    {
        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
        {
        {
            p = p->avl.right;
            p = p->avl.right;
        }
        }
        else
        else
        {
        {
            p = p->avl.left;
            p = p->avl.left;
        }
        }
    }
    }
 
 
    return p;
    return p;
}
}
 
 
 
 
/* avl_search_for_sync --
/* avl_search_for_sync --
 *     Search in AVL tree for first modified buffer belongs to specified
 *     Search in AVL tree for first modified buffer belongs to specified
 *     disk device.
 *     disk device.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     root - pointer to tree root
 *     root - pointer to tree root
 *     dd - disk device descriptor
 *     dd - disk device descriptor
 *
 *
 * RETURNS:
 * RETURNS:
 *     Block buffer, or NULL if no modified blocks on specified device
 *     Block buffer, or NULL if no modified blocks on specified device
 *     exists.
 *     exists.
 */
 */
static bdbuf_buffer *
static bdbuf_buffer *
avl_search_for_sync(bdbuf_buffer **root, disk_device *dd)
avl_search_for_sync(bdbuf_buffer **root, disk_device *dd)
{
{
    dev_t dev = dd->phys_dev->dev;
    dev_t dev = dd->phys_dev->dev;
    blkdev_bnum block_start = dd->start;
    blkdev_bnum block_start = dd->start;
    blkdev_bnum block_end = dd->start + dd->size - 1;
    blkdev_bnum block_end = dd->start + dd->size - 1;
 
 
    bdbuf_buffer *buf_stack[AVL_MAX_HEIGHT];
    bdbuf_buffer *buf_stack[AVL_MAX_HEIGHT];
    bdbuf_buffer **buf_prev = buf_stack;
    bdbuf_buffer **buf_prev = buf_stack;
    bdbuf_buffer *p = *root;
    bdbuf_buffer *p = *root;
 
 
    while (p != NULL)
    while (p != NULL)
    {
    {
        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block_start)))
        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block_start)))
        {
        {
            p = p->avl.right;
            p = p->avl.right;
        }
        }
        else if ((p->dev > dev) || ((p->dev == dev) && (p->block > block_end)))
        else if ((p->dev > dev) || ((p->dev == dev) && (p->block > block_end)))
        {
        {
            p = p->avl.left;
            p = p->avl.left;
        }
        }
        else if (p->modified)
        else if (p->modified)
        {
        {
            return p;
            return p;
        }
        }
        else
        else
        {
        {
            if (p->avl.right != NULL)
            if (p->avl.right != NULL)
            {
            {
                *buf_prev++ = p->avl.right;
                *buf_prev++ = p->avl.right;
            }
            }
            p = p->avl.left;
            p = p->avl.left;
        }
        }
 
 
        if ((p == NULL) && (buf_prev > buf_stack))
        if ((p == NULL) && (buf_prev > buf_stack))
        {
        {
            p = *--buf_prev;
            p = *--buf_prev;
        }
        }
    }
    }
 
 
    return p;
    return p;
}
}
 
 
 
 
/*
/*
 * avl_insert --
 * avl_insert --
 *     Inserts the specified node to the AVl-Tree.
 *     Inserts the specified node to the AVl-Tree.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     root - Pointer to pointer to the root node
 *     root - Pointer to pointer to the root node
 *     node - Pointer to the node to add.
 *     node - Pointer to the node to add.
 *
 *
 * RETURNS:
 * RETURNS:
 *     0 - The node added successfully
 *     0 - The node added successfully
 *    -1 - An error occured
 *    -1 - An error occured
 */
 */
static int
static int
avl_insert(bdbuf_buffer **root, bdbuf_buffer *node)
avl_insert(bdbuf_buffer **root, bdbuf_buffer *node)
{
{
    dev_t dev = node->dev;
    dev_t dev = node->dev;
    blkdev_bnum block = node->block;
    blkdev_bnum block = node->block;
 
 
    bdbuf_buffer *p = *root;
    bdbuf_buffer *p = *root;
    bdbuf_buffer *q, *p1, *p2;
    bdbuf_buffer *q, *p1, *p2;
    bdbuf_buffer *buf_stack[AVL_MAX_HEIGHT];
    bdbuf_buffer *buf_stack[AVL_MAX_HEIGHT];
    bdbuf_buffer **buf_prev = buf_stack;
    bdbuf_buffer **buf_prev = buf_stack;
 
 
    boolean modified = FALSE;
    boolean modified = FALSE;
 
 
    if (p == NULL)
    if (p == NULL)
    {
    {
        *root = node;
        *root = node;
        node->avl.left = NULL;
        node->avl.left = NULL;
        node->avl.right = NULL;
        node->avl.right = NULL;
        node->avl.bal = 0;
        node->avl.bal = 0;
        return 0;
        return 0;
    }
    }
 
 
    while (p != NULL)
    while (p != NULL)
    {
    {
        *buf_prev++ = p;
        *buf_prev++ = p;
 
 
        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
        {
        {
            p->avl.cache = 1;
            p->avl.cache = 1;
            q = p->avl.right;
            q = p->avl.right;
            if (q == NULL)
            if (q == NULL)
            {
            {
                q = node;
                q = node;
                p->avl.right = q = node;
                p->avl.right = q = node;
                break;
                break;
            }
            }
        }
        }
        else if ((p->dev != dev) || (p->block != block))
        else if ((p->dev != dev) || (p->block != block))
        {
        {
            p->avl.cache = -1;
            p->avl.cache = -1;
            q = p->avl.left;
            q = p->avl.left;
            if (q == NULL)
            if (q == NULL)
            {
            {
                q = node;
                q = node;
                p->avl.left = q;
                p->avl.left = q;
                break;
                break;
            }
            }
        }
        }
        else
        else
        {
        {
            return -1;
            return -1;
        }
        }
 
 
        p = q;
        p = q;
    }
    }
 
 
    q->avl.left = q->avl.right = NULL;
    q->avl.left = q->avl.right = NULL;
    q->avl.bal = 0;
    q->avl.bal = 0;
    modified = TRUE;
    modified = TRUE;
    buf_prev--;
    buf_prev--;
 
 
    while (modified)
    while (modified)
    {
    {
        if (p->avl.cache == -1)
        if (p->avl.cache == -1)
        {
        {
            switch (p->avl.bal)
            switch (p->avl.bal)
            {
            {
                case 1:
                case 1:
                    p->avl.bal = 0;
                    p->avl.bal = 0;
                    modified = FALSE;
                    modified = FALSE;
                    break;
                    break;
 
 
                case 0:
                case 0:
                    p->avl.bal = -1;
                    p->avl.bal = -1;
                    break;
                    break;
 
 
                case -1:
                case -1:
                    p1 = p->avl.left;
                    p1 = p->avl.left;
                    if (p1->avl.bal == -1) /* simple LL-turn */
                    if (p1->avl.bal == -1) /* simple LL-turn */
                    {
                    {
                        p->avl.left = p1->avl.right;
                        p->avl.left = p1->avl.right;
                        p1->avl.right = p;
                        p1->avl.right = p;
                        p->avl.bal = 0;
                        p->avl.bal = 0;
                        p = p1;
                        p = p1;
                    }
                    }
                    else /* double LR-turn */
                    else /* double LR-turn */
                    {
                    {
                        p2 = p1->avl.right;
                        p2 = p1->avl.right;
                        p1->avl.right = p2->avl.left;
                        p1->avl.right = p2->avl.left;
                        p2->avl.left = p1;
                        p2->avl.left = p1;
                        p->avl.left = p2->avl.right;
                        p->avl.left = p2->avl.right;
                        p2->avl.right = p;
                        p2->avl.right = p;
                        if (p2->avl.bal == -1) p->avl.bal = +1; else p->avl.bal = 0;
                        if (p2->avl.bal == -1) p->avl.bal = +1; else p->avl.bal = 0;
                        if (p2->avl.bal == +1) p1->avl.bal = -1; else p1->avl.bal = 0;
                        if (p2->avl.bal == +1) p1->avl.bal = -1; else p1->avl.bal = 0;
                        p = p2;
                        p = p2;
                    }
                    }
                    p->avl.bal = 0;
                    p->avl.bal = 0;
                    modified = FALSE;
                    modified = FALSE;
                    break;
                    break;
 
 
                default:
                default:
                    break;
                    break;
            }
            }
        }
        }
        else
        else
        {
        {
            switch (p->avl.bal)
            switch (p->avl.bal)
            {
            {
                case -1:
                case -1:
                    p->avl.bal = 0;
                    p->avl.bal = 0;
                    modified = FALSE;
                    modified = FALSE;
                    break;
                    break;
 
 
                case 0:
                case 0:
                    p->avl.bal = 1;
                    p->avl.bal = 1;
                    break;
                    break;
 
 
                case 1:
                case 1:
                    p1 = p->avl.right;
                    p1 = p->avl.right;
                    if (p1->avl.bal == 1) /* simple RR-turn */
                    if (p1->avl.bal == 1) /* simple RR-turn */
                    {
                    {
                        p->avl.right = p1->avl.left;
                        p->avl.right = p1->avl.left;
                        p1->avl.left = p;
                        p1->avl.left = p;
                        p->avl.bal = 0;
                        p->avl.bal = 0;
                        p = p1;
                        p = p1;
                    }
                    }
                    else /* double RL-turn */
                    else /* double RL-turn */
                    {
                    {
                        p2 = p1->avl.left;
                        p2 = p1->avl.left;
                        p1->avl.left = p2->avl.right;
                        p1->avl.left = p2->avl.right;
                        p2->avl.right = p1;
                        p2->avl.right = p1;
                        p->avl.right = p2->avl.left;
                        p->avl.right = p2->avl.left;
                        p2->avl.left = p;
                        p2->avl.left = p;
                        if (p2->avl.bal == +1) p->avl.bal = -1; else p->avl.bal = 0;
                        if (p2->avl.bal == +1) p->avl.bal = -1; else p->avl.bal = 0;
                        if (p2->avl.bal == -1) p1->avl.bal = +1; else p1->avl.bal = 0;
                        if (p2->avl.bal == -1) p1->avl.bal = +1; else p1->avl.bal = 0;
                        p = p2;
                        p = p2;
                    }
                    }
                    p->avl.bal = 0;
                    p->avl.bal = 0;
                    modified = FALSE;
                    modified = FALSE;
                    break;
                    break;
 
 
                default:
                default:
                    break;
                    break;
            }
            }
        }
        }
        q = p;
        q = p;
        if (buf_prev > buf_stack)
        if (buf_prev > buf_stack)
        {
        {
            p = *--buf_prev;
            p = *--buf_prev;
 
 
            if (p->avl.cache == -1)
            if (p->avl.cache == -1)
            {
            {
                p->avl.left = q;
                p->avl.left = q;
            }
            }
            else
            else
            {
            {
                p->avl.right = q;
                p->avl.right = q;
            }
            }
        }
        }
        else
        else
        {
        {
            *root = p;
            *root = p;
            break;
            break;
        }
        }
    };
    };
 
 
    return 0;
    return 0;
}
}
 
 
 
 
/* avl_remove --
/* avl_remove --
 *     removes the node from the tree.
 *     removes the node from the tree.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     root_addr - Pointer to pointer to the root node
 *     root_addr - Pointer to pointer to the root node
 *     node      - Pointer to the node to remove
 *     node      - Pointer to the node to remove
 *
 *
 * RETURNS:
 * RETURNS:
 *     0 - Item removed
 *     0 - Item removed
 *    -1 - No such item found
 *    -1 - No such item found
 */
 */
static int
static int
avl_remove(bdbuf_buffer **root, const bdbuf_buffer *node)
avl_remove(bdbuf_buffer **root, const bdbuf_buffer *node)
{
{
    dev_t dev = node->dev;
    dev_t dev = node->dev;
    blkdev_bnum block = node->block;
    blkdev_bnum block = node->block;
 
 
    bdbuf_buffer *p = *root;
    bdbuf_buffer *p = *root;
    bdbuf_buffer *q, *r, *s, *p1, *p2;
    bdbuf_buffer *q, *r, *s, *p1, *p2;
    bdbuf_buffer *buf_stack[AVL_MAX_HEIGHT];
    bdbuf_buffer *buf_stack[AVL_MAX_HEIGHT];
    bdbuf_buffer **buf_prev = buf_stack;
    bdbuf_buffer **buf_prev = buf_stack;
 
 
    boolean modified = FALSE;
    boolean modified = FALSE;
 
 
    memset(buf_stack, 0, sizeof(buf_stack));
    memset(buf_stack, 0, sizeof(buf_stack));
 
 
    while (p != NULL)
    while (p != NULL)
    {
    {
        *buf_prev++ = p;
        *buf_prev++ = p;
 
 
        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
        {
        {
            p->avl.cache = 1;
            p->avl.cache = 1;
            p = p->avl.right;
            p = p->avl.right;
        }
        }
        else if ((p->dev != dev) || (p->block != block))
        else if ((p->dev != dev) || (p->block != block))
        {
        {
            p->avl.cache = -1;
            p->avl.cache = -1;
            p = p->avl.left;
            p = p->avl.left;
        }
        }
        else
        else
        {
        {
            /* node found */
            /* node found */
            break;
            break;
        }
        }
    }
    }
 
 
    if (p == NULL)
    if (p == NULL)
    {
    {
        /* there is no such node */
        /* there is no such node */
        return -1;
        return -1;
    }
    }
 
 
    q = p;
    q = p;
 
 
    buf_prev--;
    buf_prev--;
    if (buf_prev > buf_stack)
    if (buf_prev > buf_stack)
    {
    {
        p = *(buf_prev - 1);
        p = *(buf_prev - 1);
    }
    }
    else
    else
    {
    {
        p = NULL;
        p = NULL;
    }
    }
 
 
    /* at this moment q - is a node to delete, p is q's parent */
    /* at this moment q - is a node to delete, p is q's parent */
    if (q->avl.right == NULL)
    if (q->avl.right == NULL)
    {
    {
        r = q->avl.left;
        r = q->avl.left;
        if (r != NULL)
        if (r != NULL)
        {
        {
            r->avl.bal = 0;
            r->avl.bal = 0;
        }
        }
        q = r;
        q = r;
    }
    }
    else
    else
    {
    {
        bdbuf_buffer **t;
        bdbuf_buffer **t;
 
 
        r = q->avl.right;
        r = q->avl.right;
 
 
        if (r->avl.left == NULL)
        if (r->avl.left == NULL)
        {
        {
            r->avl.left = q->avl.left;
            r->avl.left = q->avl.left;
            r->avl.bal = q->avl.bal;
            r->avl.bal = q->avl.bal;
            r->avl.cache = 1;
            r->avl.cache = 1;
            *buf_prev++ = q = r;
            *buf_prev++ = q = r;
        }
        }
        else
        else
        {
        {
            t = buf_prev++;
            t = buf_prev++;
            s = r;
            s = r;
 
 
            while (s->avl.left != NULL)
            while (s->avl.left != NULL)
            {
            {
                *buf_prev++ = r = s;
                *buf_prev++ = r = s;
                s = r->avl.left;
                s = r->avl.left;
                r->avl.cache = -1;
                r->avl.cache = -1;
            }
            }
 
 
            s->avl.left = q->avl.left;
            s->avl.left = q->avl.left;
            r->avl.left = s->avl.right;
            r->avl.left = s->avl.right;
            s->avl.right = q->avl.right;
            s->avl.right = q->avl.right;
            s->avl.bal = q->avl.bal;
            s->avl.bal = q->avl.bal;
            s->avl.cache = 1;
            s->avl.cache = 1;
 
 
            *t = q = s;
            *t = q = s;
        }
        }
    }
    }
 
 
    if (p != NULL)
    if (p != NULL)
    {
    {
        if (p->avl.cache == -1)
        if (p->avl.cache == -1)
        {
        {
            p->avl.left = q;
            p->avl.left = q;
        }
        }
        else
        else
        {
        {
            p->avl.right = q;
            p->avl.right = q;
        }
        }
    }
    }
    else
    else
    {
    {
        *root = q;
        *root = q;
    }
    }
 
 
    modified = TRUE;
    modified = TRUE;
 
 
    while (modified)
    while (modified)
    {
    {
        if (buf_prev > buf_stack)
        if (buf_prev > buf_stack)
        {
        {
            p = *--buf_prev;
            p = *--buf_prev;
        }
        }
        else
        else
        {
        {
            break;
            break;
        }
        }
 
 
        if (p->avl.cache == -1)
        if (p->avl.cache == -1)
        {
        {
            /* rebalance left branch */
            /* rebalance left branch */
            switch (p->avl.bal)
            switch (p->avl.bal)
            {
            {
                case -1:
                case -1:
                    p->avl.bal = 0;
                    p->avl.bal = 0;
                    break;
                    break;
                case  0:
                case  0:
                    p->avl.bal = 1;
                    p->avl.bal = 1;
                    modified = FALSE;
                    modified = FALSE;
                    break;
                    break;
 
 
                case +1:
                case +1:
                    p1 = p->avl.right;
                    p1 = p->avl.right;
 
 
                    if (p1->avl.bal >= 0) /* simple RR-turn */
                    if (p1->avl.bal >= 0) /* simple RR-turn */
                    {
                    {
                        p->avl.right = p1->avl.left;
                        p->avl.right = p1->avl.left;
                        p1->avl.left = p;
                        p1->avl.left = p;
 
 
                        if (p1->avl.bal == 0)
                        if (p1->avl.bal == 0)
                        {
                        {
                            p1->avl.bal = -1;
                            p1->avl.bal = -1;
                            modified = FALSE;
                            modified = FALSE;
                        }
                        }
                        else
                        else
                        {
                        {
                            p->avl.bal = 0;
                            p->avl.bal = 0;
                            p1->avl.bal = 0;
                            p1->avl.bal = 0;
                        }
                        }
                        p = p1;
                        p = p1;
                    }
                    }
                    else /* double RL-turn */
                    else /* double RL-turn */
                    {
                    {
                        p2 = p1->avl.left;
                        p2 = p1->avl.left;
 
 
                        p1->avl.left = p2->avl.right;
                        p1->avl.left = p2->avl.right;
                        p2->avl.right = p1;
                        p2->avl.right = p1;
                        p->avl.right = p2->avl.left;
                        p->avl.right = p2->avl.left;
                        p2->avl.left = p;
                        p2->avl.left = p;
 
 
                        if (p2->avl.bal == +1) p->avl.bal = -1; else p->avl.bal = 0;
                        if (p2->avl.bal == +1) p->avl.bal = -1; else p->avl.bal = 0;
                        if (p2->avl.bal == -1) p1->avl.bal = 1; else p1->avl.bal = 0;
                        if (p2->avl.bal == -1) p1->avl.bal = 1; else p1->avl.bal = 0;
 
 
                        p = p2;
                        p = p2;
                        p2->avl.bal = 0;
                        p2->avl.bal = 0;
                    }
                    }
                    break;
                    break;
 
 
                default:
                default:
                    break;
                    break;
            }
            }
        }
        }
        else
        else
        {
        {
            /* rebalance right branch */
            /* rebalance right branch */
            switch (p->avl.bal)
            switch (p->avl.bal)
            {
            {
                case +1:
                case +1:
                    p->avl.bal = 0;
                    p->avl.bal = 0;
                    break;
                    break;
 
 
                case  0:
                case  0:
                    p->avl.bal = -1;
                    p->avl.bal = -1;
                    modified = FALSE;
                    modified = FALSE;
                    break;
                    break;
 
 
                case -1:
                case -1:
                    p1 = p->avl.left;
                    p1 = p->avl.left;
 
 
                    if (p1->avl.bal <= 0) /* simple LL-turn */
                    if (p1->avl.bal <= 0) /* simple LL-turn */
                    {
                    {
                        p->avl.left = p1->avl.right;
                        p->avl.left = p1->avl.right;
                        p1->avl.right = p;
                        p1->avl.right = p;
                        if (p1->avl.bal == 0)
                        if (p1->avl.bal == 0)
                        {
                        {
                            p1->avl.bal = 1;
                            p1->avl.bal = 1;
                            modified = FALSE;
                            modified = FALSE;
                        }
                        }
                        else
                        else
                        {
                        {
                            p->avl.bal = 0;
                            p->avl.bal = 0;
                            p1->avl.bal = 0;
                            p1->avl.bal = 0;
                        }
                        }
                        p = p1;
                        p = p1;
                    }
                    }
                    else /* double LR-turn */
                    else /* double LR-turn */
                    {
                    {
                        p2 = p1->avl.right;
                        p2 = p1->avl.right;
 
 
                        p1->avl.right = p2->avl.left;
                        p1->avl.right = p2->avl.left;
                        p2->avl.left = p1;
                        p2->avl.left = p1;
                        p->avl.left = p2->avl.right;
                        p->avl.left = p2->avl.right;
                        p2->avl.right = p;
                        p2->avl.right = p;
 
 
                        if (p2->avl.bal == -1) p->avl.bal = 1; else p->avl.bal = 0;
                        if (p2->avl.bal == -1) p->avl.bal = 1; else p->avl.bal = 0;
                        if (p2->avl.bal == +1) p1->avl.bal = -1; else p1->avl.bal = 0;
                        if (p2->avl.bal == +1) p1->avl.bal = -1; else p1->avl.bal = 0;
 
 
                        p = p2;
                        p = p2;
                        p2->avl.bal = 0;
                        p2->avl.bal = 0;
                    }
                    }
                    break;
                    break;
 
 
                default:
                default:
                    break;
                    break;
            }
            }
        }
        }
 
 
        if (buf_prev > buf_stack)
        if (buf_prev > buf_stack)
        {
        {
            q = *(buf_prev - 1);
            q = *(buf_prev - 1);
 
 
            if (q->avl.cache == -1)
            if (q->avl.cache == -1)
            {
            {
                q->avl.left = p;
                q->avl.left = p;
            }
            }
            else
            else
            {
            {
                q->avl.right = p;
                q->avl.right = p;
            }
            }
        }
        }
        else
        else
        {
        {
            *root = p;
            *root = p;
            break;
            break;
        }
        }
 
 
    }
    }
 
 
    return 0;
    return 0;
}
}
 
 
/* bdbuf_initialize_pool --
/* bdbuf_initialize_pool --
 *      Initialize single buffer pool.
 *      Initialize single buffer pool.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     config - buffer pool configuration
 *     config - buffer pool configuration
 *     pool   - pool number
 *     pool   - pool number
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS_SUCCESSFUL, if buffer pool initialized successfully, or error
 *     RTEMS_SUCCESSFUL, if buffer pool initialized successfully, or error
 *     code if error occured.
 *     code if error occured.
 */
 */
static rtems_status_code
static rtems_status_code
bdbuf_initialize_pool(rtems_bdbuf_config *config, int pool)
bdbuf_initialize_pool(rtems_bdbuf_config *config, int pool)
{
{
    bdbuf_pool *p = bd_ctx.pool + pool;
    bdbuf_pool *p = bd_ctx.pool + pool;
    unsigned char *bufs;
    unsigned char *bufs;
    bdbuf_buffer *b;
    bdbuf_buffer *b;
    rtems_status_code rc;
    rtems_status_code rc;
    int i;
    int i;
 
 
    p->blksize = config->size;
    p->blksize = config->size;
    p->nblks = config->num;
    p->nblks = config->num;
    p->tree = NULL;
    p->tree = NULL;
 
 
    Chain_Initialize_empty(&p->free);
    Chain_Initialize_empty(&p->free);
    Chain_Initialize_empty(&p->lru);
    Chain_Initialize_empty(&p->lru);
 
 
    /* Allocate memory for buffer descriptors */
    /* Allocate memory for buffer descriptors */
    p->bdbufs = calloc(config->num, sizeof(bdbuf_buffer));
    p->bdbufs = calloc(config->num, sizeof(bdbuf_buffer));
    if (p->bdbufs == NULL)
    if (p->bdbufs == NULL)
    {
    {
        return RTEMS_NO_MEMORY;
        return RTEMS_NO_MEMORY;
    }
    }
 
 
    /* Allocate memory for buffers if required */
    /* Allocate memory for buffers if required */
    if (config->mem_area == NULL)
    if (config->mem_area == NULL)
    {
    {
        bufs = p->mallocd_bufs = malloc(config->num * config->size);
        bufs = p->mallocd_bufs = malloc(config->num * config->size);
        if (bufs == NULL)
        if (bufs == NULL)
        {
        {
            free(p->bdbufs);
            free(p->bdbufs);
            return RTEMS_NO_MEMORY;
            return RTEMS_NO_MEMORY;
        }
        }
    }
    }
    else
    else
    {
    {
        bufs = config->mem_area;
        bufs = config->mem_area;
        p->mallocd_bufs = NULL;
        p->mallocd_bufs = NULL;
    }
    }
 
 
    for (i = 0, b = p->bdbufs; i < p->nblks; i++, b++, bufs += p->blksize)
    for (i = 0, b = p->bdbufs; i < p->nblks; i++, b++, bufs += p->blksize)
    {
    {
        b->dev = -1; b->block = 0;
        b->dev = -1; b->block = 0;
        b->buffer = bufs;
        b->buffer = bufs;
        b->actual = b->modified = b->in_progress = FALSE;
        b->actual = b->modified = b->in_progress = FALSE;
        b->use_count = 0;
        b->use_count = 0;
        b->pool = pool;
        b->pool = pool;
        _Chain_Append(&p->free, &b->link);
        _Chain_Append(&p->free, &b->link);
    }
    }
 
 
    rc = rtems_semaphore_create(
    rc = rtems_semaphore_create(
        rtems_build_name('B', 'U', 'F', 'G'),
        rtems_build_name('B', 'U', 'F', 'G'),
        p->nblks,
        p->nblks,
        RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
        RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
        RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
        RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
        0,
        0,
        &p->bufget_sema);
        &p->bufget_sema);
 
 
    if (rc != RTEMS_SUCCESSFUL)
    if (rc != RTEMS_SUCCESSFUL)
    {
    {
        free(p->bdbufs);
        free(p->bdbufs);
        free(p->mallocd_bufs);
        free(p->mallocd_bufs);
        return rc;
        return rc;
    }
    }
 
 
    return RTEMS_SUCCESSFUL;
    return RTEMS_SUCCESSFUL;
}
}
 
 
/* bdbuf_release_pool --
/* bdbuf_release_pool --
 *     Free resources allocated for buffer pool with specified number.
 *     Free resources allocated for buffer pool with specified number.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     pool - buffer pool number
 *     pool - buffer pool number
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS_SUCCESSFUL
 *     RTEMS_SUCCESSFUL
 */
 */
static rtems_status_code
static rtems_status_code
bdbuf_release_pool(rtems_bdpool_id pool)
bdbuf_release_pool(rtems_bdpool_id pool)
{
{
    bdbuf_pool *p = bd_ctx.pool + pool;
    bdbuf_pool *p = bd_ctx.pool + pool;
    rtems_semaphore_delete(p->bufget_sema);
    rtems_semaphore_delete(p->bufget_sema);
    free(p->bdbufs);
    free(p->bdbufs);
    free(p->mallocd_bufs);
    free(p->mallocd_bufs);
    return RTEMS_SUCCESSFUL;
    return RTEMS_SUCCESSFUL;
}
}
 
 
/* rtems_bdbuf_init --
/* rtems_bdbuf_init --
 *     Prepare buffering layer to work - initialize buffer descritors
 *     Prepare buffering layer to work - initialize buffer descritors
 *     and (if it is neccessary)buffers. Buffers will be allocated accoriding
 *     and (if it is neccessary)buffers. Buffers will be allocated accoriding
 *     to the configuration table, each entry describes kind of block and
 *     to the configuration table, each entry describes kind of block and
 *     amount requested. After initialization all blocks is placed into
 *     amount requested. After initialization all blocks is placed into
 *     free elements lists.
 *     free elements lists.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     conf_table - pointer to the buffers configuration table
 *     conf_table - pointer to the buffers configuration table
 *     size       - number of entries in configuration table
 *     size       - number of entries in configuration table
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     or error code if error is occured)
 *     or error code if error is occured)
 */
 */
rtems_status_code
rtems_status_code
rtems_bdbuf_init(rtems_bdbuf_config *conf_table, int size)
rtems_bdbuf_init(rtems_bdbuf_config *conf_table, int size)
{
{
    rtems_bdpool_id i;
    rtems_bdpool_id i;
    rtems_status_code rc;
    rtems_status_code rc;
 
 
    if (size <= 0)
    if (size <= 0)
        return RTEMS_INVALID_SIZE;
        return RTEMS_INVALID_SIZE;
 
 
    bd_ctx.npools = size;
    bd_ctx.npools = size;
 
 
    /*
    /*
     * Allocate memory for buffer pool descriptors
     * Allocate memory for buffer pool descriptors
     */
     */
    bd_ctx.pool = calloc(size, sizeof(bdbuf_pool));
    bd_ctx.pool = calloc(size, sizeof(bdbuf_pool));
    if (bd_ctx.pool == NULL)
    if (bd_ctx.pool == NULL)
    {
    {
        return RTEMS_NO_MEMORY;
        return RTEMS_NO_MEMORY;
    }
    }
 
 
    Chain_Initialize_empty(&bd_ctx.mod);
    Chain_Initialize_empty(&bd_ctx.mod);
 
 
    /* Initialize buffer pools and roll out if something failed */
    /* Initialize buffer pools and roll out if something failed */
    for (i = 0; i < size; i++)
    for (i = 0; i < size; i++)
    {
    {
        rc = bdbuf_initialize_pool(conf_table + i, i);
        rc = bdbuf_initialize_pool(conf_table + i, i);
        if (rc != RTEMS_SUCCESSFUL)
        if (rc != RTEMS_SUCCESSFUL)
        {
        {
             rtems_bdpool_id j;
             rtems_bdpool_id j;
             for (j = 0; j < i - 1; j++)
             for (j = 0; j < i - 1; j++)
             {
             {
                 bdbuf_release_pool(j);
                 bdbuf_release_pool(j);
             }
             }
             return rc;
             return rc;
        }
        }
    }
    }
 
 
    /* Create buffer flush semaphore */
    /* Create buffer flush semaphore */
    rc = rtems_semaphore_create(
    rc = rtems_semaphore_create(
        rtems_build_name('B', 'F', 'L', 'U'), 0,
        rtems_build_name('B', 'F', 'L', 'U'), 0,
        RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
        RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
        RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0,
        RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0,
        &bd_ctx.flush_sema);
        &bd_ctx.flush_sema);
    if (rc != RTEMS_SUCCESSFUL)
    if (rc != RTEMS_SUCCESSFUL)
    {
    {
        for (i = 0; i < size; i++)
        for (i = 0; i < size; i++)
            bdbuf_release_pool(i);
            bdbuf_release_pool(i);
        free(bd_ctx.pool);
        free(bd_ctx.pool);
        return rc;
        return rc;
    }
    }
 
 
    /* Create and start swapout task */
    /* Create and start swapout task */
    rc = rtems_task_create(
    rc = rtems_task_create(
        rtems_build_name('B', 'S', 'W', 'P'),
        rtems_build_name('B', 'S', 'W', 'P'),
        SWAPOUT_PRIORITY,
        SWAPOUT_PRIORITY,
        SWAPOUT_STACK_SIZE,
        SWAPOUT_STACK_SIZE,
        RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT,
        RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT,
        RTEMS_DEFAULT_ATTRIBUTES,
        RTEMS_DEFAULT_ATTRIBUTES,
        &bd_ctx.swapout_task);
        &bd_ctx.swapout_task);
    if (rc != RTEMS_SUCCESSFUL)
    if (rc != RTEMS_SUCCESSFUL)
    {
    {
        rtems_semaphore_delete(bd_ctx.flush_sema);
        rtems_semaphore_delete(bd_ctx.flush_sema);
        for (i = 0; i < size; i++)
        for (i = 0; i < size; i++)
            bdbuf_release_pool(i);
            bdbuf_release_pool(i);
        free(bd_ctx.pool);
        free(bd_ctx.pool);
        return rc;
        return rc;
    }
    }
 
 
    rc = rtems_task_start(bd_ctx.swapout_task, bdbuf_swapout_task, 0);
    rc = rtems_task_start(bd_ctx.swapout_task, bdbuf_swapout_task, 0);
    if (rc != RTEMS_SUCCESSFUL)
    if (rc != RTEMS_SUCCESSFUL)
    {
    {
        rtems_task_delete(bd_ctx.swapout_task);
        rtems_task_delete(bd_ctx.swapout_task);
        rtems_semaphore_delete(bd_ctx.flush_sema);
        rtems_semaphore_delete(bd_ctx.flush_sema);
        for (i = 0; i < size; i++)
        for (i = 0; i < size; i++)
            bdbuf_release_pool(i);
            bdbuf_release_pool(i);
        free(bd_ctx.pool);
        free(bd_ctx.pool);
        return rc;
        return rc;
    }
    }
 
 
    return RTEMS_SUCCESSFUL;
    return RTEMS_SUCCESSFUL;
}
}
 
 
/* find_or_assign_buffer --
/* find_or_assign_buffer --
 *     Looks for buffer already assigned for this dev/block. If one is found
 *     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
 *     obtain block buffer. If specified block already cached (i.e. there's
 *     block in the _modified_, or _recently_used_), return address
 *     block in the _modified_, or _recently_used_), return address
 *     of appropriate buffer descriptor and increment reference counter to 1.
 *     of appropriate buffer descriptor and increment reference counter to 1.
 *     If block is not cached, allocate new buffer and return it. Data
 *     If block is not cached, allocate new buffer and return it. Data
 *     shouldn't be read to the buffer from media; buffer contains arbitrary
 *     shouldn't be read to the buffer from media; buffer contains arbitrary
 *     data. This primitive may be blocked if there are no free buffer
 *     data. This primitive may be blocked if there are no free buffer
 *     descriptors available and there are no unused non-modified (or
 *     descriptors available and there are no unused non-modified (or
 *     synchronized with media) buffers available.
 *     synchronized with media) buffers available.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     device - device number (constructed of major and minor device number
 *     device - device number (constructed of major and minor device number
 *     block  - linear media block number
 *     block  - linear media block number
 *     ret_buf - address of the variable to store address of found descriptor
 *     ret_buf - address of the variable to store address of found descriptor
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     or error code if error is occured)
 *     or error code if error is occured)
 *
 *
 * SIDE EFFEECTS:
 * SIDE EFFEECTS:
 *     bufget_sema may be obtained by this primitive
 *     bufget_sema may be obtained by this primitive
 *
 *
 * NOTE:
 * NOTE:
 *     It is assumed that primitive invoked when thread preemption is disabled.
 *     It is assumed that primitive invoked when thread preemption is disabled.
 */
 */
static rtems_status_code
static rtems_status_code
find_or_assign_buffer(disk_device *dd,
find_or_assign_buffer(disk_device *dd,
                      blkdev_bnum block,
                      blkdev_bnum block,
                      bdbuf_buffer **ret_buf)
                      bdbuf_buffer **ret_buf)
{
{
    bdbuf_buffer *bd_buf;
    bdbuf_buffer *bd_buf;
    bdbuf_pool   *bd_pool;
    bdbuf_pool   *bd_pool;
    rtems_status_code rc;
    rtems_status_code rc;
    dev_t         device;
    dev_t         device;
    ISR_Level     level;
    ISR_Level     level;
 
 
    int blksize;
    int blksize;
 
 
    device = dd->dev;
    device = dd->dev;
    bd_pool = bd_ctx.pool + dd->pool;
    bd_pool = bd_ctx.pool + dd->pool;
    blksize = dd->block_size;
    blksize = dd->block_size;
 
 
again:
again:
    /* Looking for buffer descriptor used for this dev/block. */
    /* Looking for buffer descriptor used for this dev/block. */
    bd_buf = avl_search(&bd_pool->tree, device, block);
    bd_buf = avl_search(&bd_pool->tree, device, block);
 
 
    if (bd_buf == NULL)
    if (bd_buf == NULL)
    {
    {
        /* Try to obtain semaphore without waiting first. It is the most
        /* Try to obtain semaphore without waiting first. It is the most
           frequent case when reasonable number of buffers configured. If
           frequent case when reasonable number of buffers configured. If
           it is failed, obtain semaphore blocking on it. In this case
           it is failed, obtain semaphore blocking on it. In this case
           it should be checked that appropriate buffer hasn't been loaded
           it should be checked that appropriate buffer hasn't been loaded
           by another thread, because this thread is preempted */
           by another thread, because this thread is preempted */
        rc = rtems_semaphore_obtain(bd_pool->bufget_sema, RTEMS_NO_WAIT, 0);
        rc = rtems_semaphore_obtain(bd_pool->bufget_sema, RTEMS_NO_WAIT, 0);
        if (rc == RTEMS_UNSATISFIED)
        if (rc == RTEMS_UNSATISFIED)
        {
        {
            rc = rtems_semaphore_obtain(bd_pool->bufget_sema,
            rc = rtems_semaphore_obtain(bd_pool->bufget_sema,
                                        RTEMS_WAIT, RTEMS_NO_TIMEOUT);
                                        RTEMS_WAIT, RTEMS_NO_TIMEOUT);
            bd_buf = avl_search(&bd_pool->tree, device, block);
            bd_buf = avl_search(&bd_pool->tree, device, block);
            if (bd_buf != NULL)
            if (bd_buf != NULL)
                rtems_semaphore_release(bd_pool->bufget_sema);
                rtems_semaphore_release(bd_pool->bufget_sema);
        }
        }
    }
    }
 
 
    if (bd_buf == NULL)
    if (bd_buf == NULL)
    {
    {
        /* Assign new buffer descriptor */
        /* Assign new buffer descriptor */
        if (_Chain_Is_empty(&bd_pool->free))
        if (_Chain_Is_empty(&bd_pool->free))
        {
        {
            bd_buf = (bdbuf_buffer *)Chain_Get(&bd_pool->lru);
            bd_buf = (bdbuf_buffer *)Chain_Get(&bd_pool->lru);
            if (bd_buf != NULL)
            if (bd_buf != NULL)
            {
            {
                int avl_result;
                int avl_result;
                avl_result = avl_remove(&bd_pool->tree, bd_buf);
                avl_result = avl_remove(&bd_pool->tree, bd_buf);
                if (avl_result != 0)
                if (avl_result != 0)
                {
                {
                    rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
                    rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
                    return RTEMS_INTERNAL_ERROR;
                    return RTEMS_INTERNAL_ERROR;
                }
                }
            }
            }
        }
        }
        else
        else
        {
        {
            bd_buf = (bdbuf_buffer *)Chain_Get(&(bd_pool->free));
            bd_buf = (bdbuf_buffer *)Chain_Get(&(bd_pool->free));
        }
        }
 
 
        if (bd_buf == NULL)
        if (bd_buf == NULL)
        {
        {
            goto again;
            goto again;
        }
        }
        else
        else
        {
        {
            bd_buf->dev = device;
            bd_buf->dev = device;
            bd_buf->block = block;
            bd_buf->block = block;
#ifdef AVL_GPL
#ifdef AVL_GPL
            bd_buf->avl.link[0] = NULL;
            bd_buf->avl.link[0] = NULL;
            bd_buf->avl.link[1] = NULL;
            bd_buf->avl.link[1] = NULL;
#else
#else
            bd_buf->avl.left = NULL;
            bd_buf->avl.left = NULL;
            bd_buf->avl.right = NULL;
            bd_buf->avl.right = NULL;
#endif
#endif
            bd_buf->use_count = 1;
            bd_buf->use_count = 1;
            bd_buf->modified = bd_buf->actual = bd_buf->in_progress = FALSE;
            bd_buf->modified = bd_buf->actual = bd_buf->in_progress = FALSE;
            bd_buf->status = RTEMS_SUCCESSFUL;
            bd_buf->status = RTEMS_SUCCESSFUL;
 
 
            if (avl_insert(&bd_pool->tree, bd_buf) != 0)
            if (avl_insert(&bd_pool->tree, bd_buf) != 0)
            {
            {
                rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
                rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
                return RTEMS_INTERNAL_ERROR;
                return RTEMS_INTERNAL_ERROR;
            }
            }
 
 
            *ret_buf = bd_buf;
            *ret_buf = bd_buf;
 
 
            return RTEMS_SUCCESSFUL;
            return RTEMS_SUCCESSFUL;
        }
        }
    }
    }
    else
    else
    {
    {
        /* Buffer descriptor already assigned for this dev/block */
        /* Buffer descriptor already assigned for this dev/block */
        if (bd_buf->use_count == 0)
        if (bd_buf->use_count == 0)
        {
        {
            /* If we are removing from lru list, obtain the bufget_sema
            /* If we are removing from lru list, obtain the bufget_sema
             * first. If we are removing from mod list, obtain flush sema.
             * first. If we are removing from mod list, obtain flush sema.
             * It should be obtained without blocking because we know
             * It should be obtained without blocking because we know
             * that our buffer descriptor is in the list. */
             * that our buffer descriptor is in the list. */
            if (bd_buf->modified)
            if (bd_buf->modified)
            {
            {
                rc = rtems_semaphore_obtain(bd_ctx.flush_sema,
                rc = rtems_semaphore_obtain(bd_ctx.flush_sema,
                                            RTEMS_NO_WAIT, 0);
                                            RTEMS_NO_WAIT, 0);
            }
            }
            else
            else
            {
            {
                rc = rtems_semaphore_obtain(bd_pool->bufget_sema,
                rc = rtems_semaphore_obtain(bd_pool->bufget_sema,
                                            RTEMS_NO_WAIT, 0);
                                            RTEMS_NO_WAIT, 0);
            }
            }
            /* It is possible that we couldn't obtain flush or bufget sema
            /* It is possible that we couldn't obtain flush or bufget sema
             * although buffer in the appropriate chain is available:
             * although buffer in the appropriate chain is available:
             * semaphore may be released to swapout task, but this task
             * semaphore may be released to swapout task, but this task
             * actually did not start to process it. */
             * actually did not start to process it. */
            if (rc == RTEMS_UNSATISFIED)
            if (rc == RTEMS_UNSATISFIED)
                rc = RTEMS_SUCCESSFUL;
                rc = RTEMS_SUCCESSFUL;
            if (rc != RTEMS_SUCCESSFUL)
            if (rc != RTEMS_SUCCESSFUL)
            {
            {
                rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
                rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
                return RTEMS_INTERNAL_ERROR;
                return RTEMS_INTERNAL_ERROR;
            }
            }
 
 
            /* Buffer descriptor is linked to the lru or mod chain. Remove
            /* Buffer descriptor is linked to the lru or mod chain. Remove
               it from there. */
               it from there. */
            Chain_Extract(&bd_buf->link);
            Chain_Extract(&bd_buf->link);
        }
        }
        bd_buf->use_count++;
        bd_buf->use_count++;
        while (bd_buf->in_progress != 0)
        while (bd_buf->in_progress != 0)
        {
        {
            rtems_interrupt_disable(level);
            rtems_interrupt_disable(level);
            _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
            _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
                              WATCHDOG_NO_TIMEOUT, level);
                              WATCHDOG_NO_TIMEOUT, level);
        }
        }
 
 
        *ret_buf = bd_buf;
        *ret_buf = bd_buf;
        return RTEMS_SUCCESSFUL;
        return RTEMS_SUCCESSFUL;
    }
    }
}
}
 
 
/* rtems_bdbuf_get --
/* rtems_bdbuf_get --
 *     Obtain block buffer. If specified block already cached (i.e. there's
 *     Obtain block buffer. If specified block already cached (i.e. there's
 *     block in the _modified_, or _recently_used_), return address
 *     block in the _modified_, or _recently_used_), return address
 *     of appropriate buffer descriptor and increment reference counter to 1.
 *     of appropriate buffer descriptor and increment reference counter to 1.
 *     If block is not cached, allocate new buffer and return it. Data
 *     If block is not cached, allocate new buffer and return it. Data
 *     shouldn't be read to the buffer from media; buffer may contains
 *     shouldn't be read to the buffer from media; buffer may contains
 *     arbitrary data. This primitive may be blocked if there are no free
 *     arbitrary data. This primitive may be blocked if there are no free
 *     buffer descriptors available and there are no unused non-modified
 *     buffer descriptors available and there are no unused non-modified
 *     (or synchronized with media) buffers available.
 *     (or synchronized with media) buffers available.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     device - device number (constructed of major and minor device number)
 *     device - device number (constructed of major and minor device number)
 *     block  - linear media block number
 *     block  - linear media block number
 *     bd     - address of variable to store pointer to the buffer descriptor
 *     bd     - address of variable to store pointer to the buffer descriptor
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     or error code if error is occured)
 *     or error code if error is occured)
 *
 *
 * SIDE EFFECTS:
 * SIDE EFFECTS:
 *     bufget_sema semaphore obtained by this primitive.
 *     bufget_sema semaphore obtained by this primitive.
 */
 */
rtems_status_code
rtems_status_code
rtems_bdbuf_get(dev_t device, blkdev_bnum block, bdbuf_buffer **bd)
rtems_bdbuf_get(dev_t device, blkdev_bnum block, bdbuf_buffer **bd)
{
{
    rtems_status_code rc;
    rtems_status_code rc;
    disk_device *dd;
    disk_device *dd;
    disk_device *pdd;
    disk_device *pdd;
    preemption_key key;
    preemption_key key;
 
 
    /*
    /*
     * Convert logical dev/block to physical one
     * Convert logical dev/block to physical one
     */
     */
    dd = rtems_disk_lookup(device);
    dd = rtems_disk_lookup(device);
    if (dd == NULL)
    if (dd == NULL)
        return RTEMS_INVALID_ID;
        return RTEMS_INVALID_ID;
 
 
    if (block >= dd->size)
    if (block >= dd->size)
    {
    {
        rtems_disk_release(dd);
        rtems_disk_release(dd);
        return RTEMS_INVALID_NUMBER;
        return RTEMS_INVALID_NUMBER;
    }
    }
 
 
    pdd = dd->phys_dev;
    pdd = dd->phys_dev;
    block += dd->start;
    block += dd->start;
    rtems_disk_release(dd);
    rtems_disk_release(dd);
 
 
    DISABLE_PREEMPTION(key);
    DISABLE_PREEMPTION(key);
    rc = find_or_assign_buffer(pdd, block, bd);
    rc = find_or_assign_buffer(pdd, block, bd);
    ENABLE_PREEMPTION(key);
    ENABLE_PREEMPTION(key);
 
 
    if (rc != RTEMS_SUCCESSFUL)
    if (rc != RTEMS_SUCCESSFUL)
        return rc;
        return rc;
 
 
    return RTEMS_SUCCESSFUL;
    return RTEMS_SUCCESSFUL;
}
}
 
 
/* bdbuf_initialize_transfer_sema --
/* bdbuf_initialize_transfer_sema --
 *     Initialize transfer_sema mutex semaphore associated with buffer
 *     Initialize transfer_sema mutex semaphore associated with buffer
 *     descriptor.
 *     descriptor.
 */
 */
static inline void
static inline void
bdbuf_initialize_transfer_sema(bdbuf_buffer *bd_buf)
bdbuf_initialize_transfer_sema(bdbuf_buffer *bd_buf)
{
{
    CORE_mutex_Attributes mutex_attr;
    CORE_mutex_Attributes mutex_attr;
    mutex_attr.lock_nesting_behavior = CORE_MUTEX_NESTING_BLOCKS;
    mutex_attr.lock_nesting_behavior = CORE_MUTEX_NESTING_BLOCKS;
    mutex_attr.only_owner_release = FALSE;
    mutex_attr.only_owner_release = FALSE;
    mutex_attr.discipline = CORE_MUTEX_DISCIPLINES_FIFO;
    mutex_attr.discipline = CORE_MUTEX_DISCIPLINES_FIFO;
    mutex_attr.priority_ceiling = 0;
    mutex_attr.priority_ceiling = 0;
 
 
    _CORE_mutex_Initialize(&bd_buf->transfer_sema,
    _CORE_mutex_Initialize(&bd_buf->transfer_sema,
                           &mutex_attr, CORE_MUTEX_LOCKED);
                           &mutex_attr, CORE_MUTEX_LOCKED);
}
}
 
 
/* bdbuf_write_transfer_done --
/* bdbuf_write_transfer_done --
 *     Callout function. Invoked by block device driver when data transfer
 *     Callout function. Invoked by block device driver when data transfer
 *     to device (write) is completed. This function may be invoked from
 *     to device (write) is completed. This function may be invoked from
 *     interrupt handler.
 *     interrupt handler.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     arg    - arbitrary argument specified in block device request
 *     arg    - arbitrary argument specified in block device request
 *              structure (in this case - pointer to the appropriate
 *              structure (in this case - pointer to the appropriate
 *              bdbuf_buffer buffer descriptor structure).
 *              bdbuf_buffer buffer descriptor structure).
 *     status - I/O completion status
 *     status - I/O completion status
 *     error  - errno error code if status != RTEMS_SUCCESSFUL
 *     error  - errno error code if status != RTEMS_SUCCESSFUL
 *
 *
 * RETURNS:
 * RETURNS:
 *     none
 *     none
 */
 */
static void
static void
bdbuf_write_transfer_done(void *arg, rtems_status_code status, int error)
bdbuf_write_transfer_done(void *arg, rtems_status_code status, int error)
{
{
    bdbuf_buffer *bd_buf = arg;
    bdbuf_buffer *bd_buf = arg;
    bd_buf->status = status;
    bd_buf->status = status;
    bd_buf->error = error;
    bd_buf->error = error;
    bd_buf->in_progress = bd_buf->modified = FALSE;
    bd_buf->in_progress = bd_buf->modified = FALSE;
    _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL);
    _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL);
    _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
    _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
                      CORE_MUTEX_STATUS_SUCCESSFUL);
                      CORE_MUTEX_STATUS_SUCCESSFUL);
}
}
 
 
/* bdbuf_read_transfer_done --
/* bdbuf_read_transfer_done --
 *     Callout function. Invoked by block device driver when data transfer
 *     Callout function. Invoked by block device driver when data transfer
 *     from device (read) is completed. This function may be invoked from
 *     from device (read) is completed. This function may be invoked from
 *     interrupt handler.
 *     interrupt handler.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     arg    - arbitrary argument specified in block device request
 *     arg    - arbitrary argument specified in block device request
 *              structure (in this case - pointer to the appropriate
 *              structure (in this case - pointer to the appropriate
 *              bdbuf_buffer buffer descriptor structure).
 *              bdbuf_buffer buffer descriptor structure).
 *     status - I/O completion status
 *     status - I/O completion status
 *     error  - errno error code if status != RTEMS_SUCCESSFUL
 *     error  - errno error code if status != RTEMS_SUCCESSFUL
 *
 *
 * RETURNS:
 * RETURNS:
 *     none
 *     none
 */
 */
static void
static void
bdbuf_read_transfer_done(void *arg, rtems_status_code status, int error)
bdbuf_read_transfer_done(void *arg, rtems_status_code status, int error)
{
{
    bdbuf_buffer *bd_buf = arg;
    bdbuf_buffer *bd_buf = arg;
    bd_buf->status = status;
    bd_buf->status = status;
    bd_buf->error = error;
    bd_buf->error = error;
    _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL);
    _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL);
    _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
    _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
                      CORE_MUTEX_STATUS_SUCCESSFUL);
                      CORE_MUTEX_STATUS_SUCCESSFUL);
}
}
 
 
/* rtems_bdbuf_read --
/* rtems_bdbuf_read --
 *     (Similar to the rtems_bdbuf_get, except reading data from media)
 *     (Similar to the rtems_bdbuf_get, except reading data from media)
 *     Obtain block buffer. If specified block already cached, return address
 *     Obtain block buffer. If specified block already cached, return address
 *     of appropriate buffer and increment reference counter to 1. If block is
 *     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.
 *     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
 *     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
 *     media, if there are no free buffer descriptors available and there are
 *     no unused non-modified (or synchronized with media) buffers available.
 *     no unused non-modified (or synchronized with media) buffers available.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     device - device number (consists of major and minor device number)
 *     device - device number (consists of major and minor device number)
 *     block  - linear media block number
 *     block  - linear media block number
 *     bd     - address of variable to store pointer to the buffer descriptor
 *     bd     - address of variable to store pointer to the buffer descriptor
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     or error code if error is occured)
 *     or error code if error is occured)
 *
 *
 * SIDE EFFECTS:
 * SIDE EFFECTS:
 *     bufget_sema and transfer_sema semaphores obtained by this primitive.
 *     bufget_sema and transfer_sema semaphores obtained by this primitive.
 */
 */
rtems_status_code
rtems_status_code
rtems_bdbuf_read(dev_t device,
rtems_bdbuf_read(dev_t device,
                 blkdev_bnum block,
                 blkdev_bnum block,
                 bdbuf_buffer **bd)
                 bdbuf_buffer **bd)
{
{
    preemption_key key;
    preemption_key key;
    ISR_Level level;
    ISR_Level level;
 
 
    bdbuf_buffer *bd_buf;
    bdbuf_buffer *bd_buf;
    rtems_status_code rc;
    rtems_status_code rc;
    int result;
    int result;
    disk_device *dd;
    disk_device *dd;
    disk_device *pdd;
    disk_device *pdd;
    blkdev_request1 req;
    blkdev_request1 req;
 
 
    dd = rtems_disk_lookup(device);
    dd = rtems_disk_lookup(device);
    if (dd == NULL)
    if (dd == NULL)
        return RTEMS_INVALID_ID;
        return RTEMS_INVALID_ID;
 
 
    if (block >= dd->size)
    if (block >= dd->size)
    {
    {
        rtems_disk_release(dd);
        rtems_disk_release(dd);
        return RTEMS_INVALID_NUMBER;
        return RTEMS_INVALID_NUMBER;
    }
    }
 
 
    pdd = dd->phys_dev;
    pdd = dd->phys_dev;
    block += dd->start;
    block += dd->start;
 
 
    DISABLE_PREEMPTION(key);
    DISABLE_PREEMPTION(key);
    rc = find_or_assign_buffer(pdd, block, &bd_buf);
    rc = find_or_assign_buffer(pdd, block, &bd_buf);
 
 
    if (rc != RTEMS_SUCCESSFUL)
    if (rc != RTEMS_SUCCESSFUL)
    {
    {
        ENABLE_PREEMPTION(key);
        ENABLE_PREEMPTION(key);
        rtems_disk_release(dd);
        rtems_disk_release(dd);
        return rc;
        return rc;
    }
    }
 
 
    if (!bd_buf->actual)
    if (!bd_buf->actual)
    {
    {
        bd_buf->in_progress = 1;
        bd_buf->in_progress = 1;
 
 
        req.req.req = BLKDEV_REQ_READ;
        req.req.req = BLKDEV_REQ_READ;
        req.req.req_done = bdbuf_read_transfer_done;
        req.req.req_done = bdbuf_read_transfer_done;
        req.req.done_arg = bd_buf;
        req.req.done_arg = bd_buf;
        req.req.start = block;
        req.req.start = block;
        req.req.count = 1;
        req.req.count = 1;
        req.req.bufnum = 1;
        req.req.bufnum = 1;
        req.req.bufs[0].length = dd->block_size;
        req.req.bufs[0].length = dd->block_size;
        req.req.bufs[0].buffer = bd_buf->buffer;
        req.req.bufs[0].buffer = bd_buf->buffer;
 
 
        bdbuf_initialize_transfer_sema(bd_buf);
        bdbuf_initialize_transfer_sema(bd_buf);
        result = dd->ioctl(device, BLKIO_REQUEST, &req);
        result = dd->ioctl(device, BLKIO_REQUEST, &req);
        if (result == -1)
        if (result == -1)
        {
        {
            bd_buf->status = RTEMS_IO_ERROR;
            bd_buf->status = RTEMS_IO_ERROR;
            bd_buf->error = errno;
            bd_buf->error = errno;
            bd_buf->actual = FALSE;
            bd_buf->actual = FALSE;
        }
        }
        else
        else
        {
        {
            rtems_interrupt_disable(level);
            rtems_interrupt_disable(level);
            _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
            _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
                              WATCHDOG_NO_TIMEOUT, level);
                              WATCHDOG_NO_TIMEOUT, level);
            bd_buf->actual = TRUE;
            bd_buf->actual = TRUE;
        }
        }
        bd_buf->in_progress = FALSE;
        bd_buf->in_progress = FALSE;
    }
    }
    rtems_disk_release(dd);
    rtems_disk_release(dd);
 
 
    ENABLE_PREEMPTION(key);
    ENABLE_PREEMPTION(key);
 
 
    *bd = bd_buf;
    *bd = bd_buf;
 
 
    return RTEMS_SUCCESSFUL;
    return RTEMS_SUCCESSFUL;
}
}
 
 
 
 
/* bdbuf_release --
/* bdbuf_release --
 *     Release buffer. Decrease buffer usage counter. If it is zero, further
 *     Release buffer. Decrease buffer usage counter. If it is zero, further
 *     processing depends on modified attribute. If buffer was modified, it
 *     processing depends on modified attribute. If buffer was modified, it
 *     is inserted into mod chain and swapout task waken up. If buffer was
 *     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
 *     not modified, it is returned to the end of lru chain making it available
 *     for further use.
 *     for further use.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     bd_buf - pointer to the released buffer descriptor.
 *     bd_buf - pointer to the released buffer descriptor.
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS_SUCCESSFUL if buffer released successfully, or error code if
 *     RTEMS_SUCCESSFUL if buffer released successfully, or error code if
 *     error occured.
 *     error occured.
 *
 *
 * NOTE:
 * NOTE:
 *     This is internal function. It is assumed that task made non-preemptive
 *     This is internal function. It is assumed that task made non-preemptive
 *     before its invocation.
 *     before its invocation.
 */
 */
static rtems_status_code
static rtems_status_code
bdbuf_release(bdbuf_buffer *bd_buf)
bdbuf_release(bdbuf_buffer *bd_buf)
{
{
    bdbuf_pool *bd_pool;
    bdbuf_pool *bd_pool;
    rtems_status_code rc = RTEMS_SUCCESSFUL;
    rtems_status_code rc = RTEMS_SUCCESSFUL;
 
 
    if (bd_buf->use_count <= 0)
    if (bd_buf->use_count <= 0)
        return RTEMS_INTERNAL_ERROR;
        return RTEMS_INTERNAL_ERROR;
 
 
    bd_pool = bd_ctx.pool + bd_buf->pool;
    bd_pool = bd_ctx.pool + bd_buf->pool;
 
 
    bd_buf->use_count--;
    bd_buf->use_count--;
 
 
    if (bd_buf->use_count == 0)
    if (bd_buf->use_count == 0)
    {
    {
        if (bd_buf->modified)
        if (bd_buf->modified)
        {
        {
 
 
            /* Buffer was modified. Insert buffer to the modified buffers
            /* Buffer was modified. Insert buffer to the modified buffers
             * list and initiate flushing. */
             * list and initiate flushing. */
            Chain_Append(&bd_ctx.mod, &bd_buf->link);
            Chain_Append(&bd_ctx.mod, &bd_buf->link);
 
 
            /* Release the flush_sema */
            /* Release the flush_sema */
            rc = rtems_semaphore_release(bd_ctx.flush_sema);
            rc = rtems_semaphore_release(bd_ctx.flush_sema);
        }
        }
        else
        else
        {
        {
            /* Buffer was not modified. Add this descriptor to the
            /* Buffer was not modified. Add this descriptor to the
             * end of lru chain and make it available for reuse. */
             * end of lru chain and make it available for reuse. */
            Chain_Append(&bd_pool->lru, &bd_buf->link);
            Chain_Append(&bd_pool->lru, &bd_buf->link);
            rc = rtems_semaphore_release(bd_pool->bufget_sema);
            rc = rtems_semaphore_release(bd_pool->bufget_sema);
        }
        }
    }
    }
    return rc;
    return rc;
}
}
 
 
 
 
/* rtems_bdbuf_release --
/* rtems_bdbuf_release --
 *     Release buffer allocated before. This primitive decrease the
 *     Release buffer allocated before. This primitive decrease the
 *     usage counter. If it is zero, further destiny of buffer depends on
 *     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
 *     '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,
 *     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
 *     it is placed to the end of lru list, and bufget_sema released, allowing
 *     to reuse this buffer.
 *     to reuse this buffer.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     bd_buf - pointer to the bdbuf_buffer structure previously obtained using
 *     bd_buf - pointer to the bdbuf_buffer structure previously obtained using
 *              get/read primitive.
 *              get/read primitive.
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     or error code if error is occured)
 *     or error code if error is occured)
 *
 *
 * SIDE EFFECTS:
 * SIDE EFFECTS:
 *     flush_sema and bufget_sema semaphores may be released by this primitive.
 *     flush_sema and bufget_sema semaphores may be released by this primitive.
 */
 */
rtems_status_code
rtems_status_code
rtems_bdbuf_release(bdbuf_buffer *bd_buf)
rtems_bdbuf_release(bdbuf_buffer *bd_buf)
{
{
    preemption_key key;
    preemption_key key;
    rtems_status_code rc = RTEMS_SUCCESSFUL;
    rtems_status_code rc = RTEMS_SUCCESSFUL;
 
 
    if (bd_buf == NULL)
    if (bd_buf == NULL)
        return RTEMS_INVALID_ADDRESS;
        return RTEMS_INVALID_ADDRESS;
 
 
    DISABLE_PREEMPTION(key);
    DISABLE_PREEMPTION(key);
 
 
    rc = bdbuf_release(bd_buf);
    rc = bdbuf_release(bd_buf);
 
 
    ENABLE_PREEMPTION(key);
    ENABLE_PREEMPTION(key);
 
 
    return rc;
    return rc;
}
}
 
 
/* rtems_bdbuf_release_modified --
/* rtems_bdbuf_release_modified --
 *     Release buffer allocated before, assuming that it is _modified_ by
 *     Release buffer allocated before, assuming that it is _modified_ by
 *     it's owner. This primitive decrease usage counter for buffer, mark
 *     it's owner. This primitive decrease usage counter for buffer, mark
 *     buffer descriptor as modified. If usage counter is 0, insert it at
 *     buffer descriptor as modified. If usage counter is 0, insert it at
 *     end of mod chain and release flush_sema semaphore to activate the
 *     end of mod chain and release flush_sema semaphore to activate the
 *     flush task.
 *     flush task.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     bd_buf - pointer to the bdbuf_buffer structure previously obtained using
 *     bd_buf - pointer to the bdbuf_buffer structure previously obtained using
 *              get/read primitive.
 *              get/read primitive.
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     or error code if error is occured)
 *     or error code if error is occured)
 *
 *
 * SIDE EFFECTS:
 * SIDE EFFECTS:
 *     flush_sema semaphore may be released by this primitive.
 *     flush_sema semaphore may be released by this primitive.
 */
 */
rtems_status_code
rtems_status_code
rtems_bdbuf_release_modified(bdbuf_buffer *bd_buf)
rtems_bdbuf_release_modified(bdbuf_buffer *bd_buf)
{
{
    preemption_key key;
    preemption_key key;
    rtems_status_code rc = RTEMS_SUCCESSFUL;
    rtems_status_code rc = RTEMS_SUCCESSFUL;
 
 
    if (bd_buf == NULL)
    if (bd_buf == NULL)
        return RTEMS_INVALID_ADDRESS;
        return RTEMS_INVALID_ADDRESS;
 
 
    DISABLE_PREEMPTION(key);
    DISABLE_PREEMPTION(key);
 
 
    if (!bd_buf->modified)
    if (!bd_buf->modified)
    {
    {
        bdbuf_initialize_transfer_sema(bd_buf);
        bdbuf_initialize_transfer_sema(bd_buf);
    }
    }
    bd_buf->modified = TRUE;
    bd_buf->modified = TRUE;
    bd_buf->actual = TRUE;
    bd_buf->actual = TRUE;
    rc = bdbuf_release(bd_buf);
    rc = bdbuf_release(bd_buf);
 
 
    ENABLE_PREEMPTION(key);
    ENABLE_PREEMPTION(key);
 
 
    return rc;
    return rc;
}
}
 
 
/* rtems_bdbuf_sync --
/* rtems_bdbuf_sync --
 *     Wait until specified buffer synchronized with disk. Invoked on exchanges
 *     Wait until specified buffer synchronized with disk. Invoked on exchanges
 *     critical for data consistency on the media. This primitive mark owned
 *     critical for data consistency on the media. This primitive mark owned
 *     block as modified, decrease usage counter. If usage counter is 0,
 *     block as modified, decrease usage counter. If usage counter is 0,
 *     block inserted to the mod chain and flush_sema semaphore released.
 *     block inserted to the mod chain and flush_sema semaphore released.
 *     Finally, primitives blocked on transfer_sema semaphore.
 *     Finally, primitives blocked on transfer_sema semaphore.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     bd_buf - pointer to the bdbuf_buffer structure previously obtained using
 *     bd_buf - pointer to the bdbuf_buffer structure previously obtained using
 *              get/read primitive.
 *              get/read primitive.
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     or error code if error is occured)
 *     or error code if error is occured)
 *
 *
 * SIDE EFFECTS:
 * SIDE EFFECTS:
 *     Primitive may be blocked on transfer_sema semaphore.
 *     Primitive may be blocked on transfer_sema semaphore.
 */
 */
rtems_status_code
rtems_status_code
rtems_bdbuf_sync(bdbuf_buffer *bd_buf)
rtems_bdbuf_sync(bdbuf_buffer *bd_buf)
{
{
    preemption_key key;
    preemption_key key;
    ISR_Level level;
    ISR_Level level;
    rtems_status_code rc = RTEMS_SUCCESSFUL;
    rtems_status_code rc = RTEMS_SUCCESSFUL;
 
 
    if (bd_buf == NULL)
    if (bd_buf == NULL)
        return RTEMS_INVALID_ADDRESS;
        return RTEMS_INVALID_ADDRESS;
 
 
    DISABLE_PREEMPTION(key);
    DISABLE_PREEMPTION(key);
 
 
    if (!bd_buf->modified)
    if (!bd_buf->modified)
    {
    {
        bdbuf_initialize_transfer_sema(bd_buf);
        bdbuf_initialize_transfer_sema(bd_buf);
    }
    }
    bd_buf->modified = TRUE;
    bd_buf->modified = TRUE;
    bd_buf->actual = TRUE;
    bd_buf->actual = TRUE;
 
 
    rc = bdbuf_release(bd_buf);
    rc = bdbuf_release(bd_buf);
 
 
    if (rc == RTEMS_SUCCESSFUL)
    if (rc == RTEMS_SUCCESSFUL)
    {
    {
        rtems_interrupt_disable(level);
        rtems_interrupt_disable(level);
        _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
        _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
                          WATCHDOG_NO_TIMEOUT, level);
                          WATCHDOG_NO_TIMEOUT, level);
    }
    }
 
 
    ENABLE_PREEMPTION(key);
    ENABLE_PREEMPTION(key);
 
 
    return rc;
    return rc;
}
}
 
 
/* rtems_bdbuf_syncdev --
/* rtems_bdbuf_syncdev --
 *     Synchronize with disk all buffers containing the blocks belonging to
 *     Synchronize with disk all buffers containing the blocks belonging to
 *     specified device.
 *     specified device.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     dev - block device number
 *     dev - block device number
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
 *     or error code if error is occured)
 *     or error code if error is occured)
 */
 */
rtems_status_code
rtems_status_code
rtems_bdbuf_syncdev(dev_t dev)
rtems_bdbuf_syncdev(dev_t dev)
{
{
    preemption_key key;
    preemption_key key;
    ISR_Level level;
    ISR_Level level;
 
 
    bdbuf_buffer *bd_buf;
    bdbuf_buffer *bd_buf;
    disk_device *dd;
    disk_device *dd;
    bdbuf_pool  *pool;
    bdbuf_pool  *pool;
 
 
    dd = rtems_disk_lookup(dev);
    dd = rtems_disk_lookup(dev);
    if (dd == NULL)
    if (dd == NULL)
        return RTEMS_INVALID_ID;
        return RTEMS_INVALID_ID;
 
 
    pool = bd_ctx.pool + dd->pool;
    pool = bd_ctx.pool + dd->pool;
 
 
    DISABLE_PREEMPTION(key);
    DISABLE_PREEMPTION(key);
    do {
    do {
        bd_buf = avl_search_for_sync(&pool->tree, dd);
        bd_buf = avl_search_for_sync(&pool->tree, dd);
        if (bd_buf != NULL /* && bd_buf->modified */)
        if (bd_buf != NULL /* && bd_buf->modified */)
        {
        {
            rtems_interrupt_disable(level);
            rtems_interrupt_disable(level);
            _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
            _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
                              WATCHDOG_NO_TIMEOUT, level);
                              WATCHDOG_NO_TIMEOUT, level);
        }
        }
    } while (bd_buf != NULL);
    } while (bd_buf != NULL);
    ENABLE_PREEMPTION(key);
    ENABLE_PREEMPTION(key);
    return rtems_disk_release(dd);
    return rtems_disk_release(dd);
}
}
 
 
/* bdbuf_swapout_task --
/* bdbuf_swapout_task --
 *     Body of task which take care on flushing modified buffers to the
 *     Body of task which take care on flushing modified buffers to the
 *     disk.
 *     disk.
 */
 */
static rtems_task
static rtems_task
bdbuf_swapout_task(rtems_task_argument unused)
bdbuf_swapout_task(rtems_task_argument unused)
{
{
    rtems_status_code rc;
    rtems_status_code rc;
    int result;
    int result;
    ISR_Level level;
    ISR_Level level;
    bdbuf_buffer *bd_buf;
    bdbuf_buffer *bd_buf;
    bdbuf_pool *bd_pool;
    bdbuf_pool *bd_pool;
    disk_device *dd;
    disk_device *dd;
    blkdev_request1 req;
    blkdev_request1 req;
 
 
    while (1)
    while (1)
    {
    {
        rc = rtems_semaphore_obtain(bd_ctx.flush_sema, RTEMS_WAIT, 0);
        rc = rtems_semaphore_obtain(bd_ctx.flush_sema, RTEMS_WAIT, 0);
        if (rc != RTEMS_SUCCESSFUL)
        if (rc != RTEMS_SUCCESSFUL)
        {
        {
            rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_SWAPOUT);
            rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_SWAPOUT);
        }
        }
 
 
        bd_buf = (bdbuf_buffer *)Chain_Get(&bd_ctx.mod);
        bd_buf = (bdbuf_buffer *)Chain_Get(&bd_ctx.mod);
        if (bd_buf == NULL)
        if (bd_buf == NULL)
        {
        {
            /* It is possible that flush_sema semaphore will be released, but
            /* It is possible that flush_sema semaphore will be released, but
             * buffer to be removed from mod chain before swapout task start
             * buffer to be removed from mod chain before swapout task start
             * its processing. */
             * its processing. */
            continue;
            continue;
        }
        }
 
 
        bd_buf->in_progress = TRUE;
        bd_buf->in_progress = TRUE;
        bd_buf->use_count++;
        bd_buf->use_count++;
        bd_pool = bd_ctx.pool + bd_buf->pool;
        bd_pool = bd_ctx.pool + bd_buf->pool;
        dd = rtems_disk_lookup(bd_buf->dev);
        dd = rtems_disk_lookup(bd_buf->dev);
 
 
        req.req.req = BLKDEV_REQ_WRITE;
        req.req.req = BLKDEV_REQ_WRITE;
        req.req.req_done = bdbuf_write_transfer_done;
        req.req.req_done = bdbuf_write_transfer_done;
        req.req.done_arg = bd_buf;
        req.req.done_arg = bd_buf;
        req.req.start = bd_buf->block + dd->start;
        req.req.start = bd_buf->block + dd->start;
        req.req.count = 1;
        req.req.count = 1;
        req.req.bufnum = 1;
        req.req.bufnum = 1;
        req.req.bufs[0].length = dd->block_size;
        req.req.bufs[0].length = dd->block_size;
        req.req.bufs[0].buffer = bd_buf->buffer;
        req.req.bufs[0].buffer = bd_buf->buffer;
 
 
        /* transfer_sema initialized when bd_buf inserted in the mod chain
        /* transfer_sema initialized when bd_buf inserted in the mod chain
           first time */
           first time */
        result = dd->ioctl(dd->phys_dev->dev, BLKIO_REQUEST, &req);
        result = dd->ioctl(dd->phys_dev->dev, BLKIO_REQUEST, &req);
 
 
        rtems_disk_release(dd);
        rtems_disk_release(dd);
 
 
        if (result == -1)
        if (result == -1)
        {
        {
            bd_buf->status = RTEMS_IO_ERROR;
            bd_buf->status = RTEMS_IO_ERROR;
            bd_buf->error = errno;
            bd_buf->error = errno;
            /* Release tasks waiting on syncing this buffer */
            /* Release tasks waiting on syncing this buffer */
            _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
            _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
                              CORE_MUTEX_STATUS_SUCCESSFUL);
                              CORE_MUTEX_STATUS_SUCCESSFUL);
        }
        }
        else
        else
        {
        {
            if (bd_buf->in_progress)
            if (bd_buf->in_progress)
            {
            {
                rtems_interrupt_disable(level);
                rtems_interrupt_disable(level);
                _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, 0, level);
                _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, 0, level);
            }
            }
        }
        }
        bd_buf->use_count--;
        bd_buf->use_count--;
 
 
        /* Another task have chance to use this buffer, or even
        /* Another task have chance to use this buffer, or even
         * modify it. If buffer is not in use, insert it in appropriate chain
         * modify it. If buffer is not in use, insert it in appropriate chain
         * and release semaphore */
         * and release semaphore */
        if (bd_buf->use_count == 0)
        if (bd_buf->use_count == 0)
        {
        {
            if (bd_buf->modified)
            if (bd_buf->modified)
            {
            {
                Chain_Append(&bd_ctx.mod, &bd_buf->link);
                Chain_Append(&bd_ctx.mod, &bd_buf->link);
                rc = rtems_semaphore_release(bd_ctx.flush_sema);
                rc = rtems_semaphore_release(bd_ctx.flush_sema);
            }
            }
            else
            else
            {
            {
                Chain_Append(&bd_pool->lru, &bd_buf->link);
                Chain_Append(&bd_pool->lru, &bd_buf->link);
                rc = rtems_semaphore_release(bd_pool->bufget_sema);
                rc = rtems_semaphore_release(bd_pool->bufget_sema);
            }
            }
        }
        }
    }
    }
}
}
 
 
/* rtems_bdbuf_find_pool --
/* rtems_bdbuf_find_pool --
 *     Find first appropriate buffer pool. This primitive returns the index
 *     Find first appropriate buffer pool. This primitive returns the index
 *     of first buffer pool which block size is greater than or equal to
 *     of first buffer pool which block size is greater than or equal to
 *     specified size.
 *     specified size.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     block_size - requested block size
 *     block_size - requested block size
 *     pool       - placeholder for result
 *     pool       - placeholder for result
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
 *     RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
 *     RTEMS_INVALID_SIZE if specified block size is invalid (not a power
 *     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
 *     of 2), RTEMS_NOT_DEFINED if buffer pool for this or greater block size
 *     is not configured.
 *     is not configured.
 */
 */
rtems_status_code
rtems_status_code
rtems_bdbuf_find_pool(int block_size, rtems_bdpool_id *pool)
rtems_bdbuf_find_pool(int block_size, rtems_bdpool_id *pool)
{
{
    rtems_bdpool_id i;
    rtems_bdpool_id i;
    bdbuf_pool *p;
    bdbuf_pool *p;
    int cursize = INT_MAX;
    int cursize = INT_MAX;
    rtems_bdpool_id curid = -1;
    rtems_bdpool_id curid = -1;
    rtems_boolean found = FALSE;
    rtems_boolean found = FALSE;
    int j;
    int j;
 
 
    for (j = block_size; (j != 0) && ((j & 1) == 0); j >>= 1);
    for (j = block_size; (j != 0) && ((j & 1) == 0); j >>= 1);
    if (j != 1)
    if (j != 1)
        return RTEMS_INVALID_SIZE;
        return RTEMS_INVALID_SIZE;
 
 
    for (i = 0, p = bd_ctx.pool; i < bd_ctx.npools; i++, p++)
    for (i = 0, p = bd_ctx.pool; i < bd_ctx.npools; i++, p++)
    {
    {
        if ((p->blksize >= block_size) &&
        if ((p->blksize >= block_size) &&
            (p->blksize < cursize))
            (p->blksize < cursize))
        {
        {
            curid = i;
            curid = i;
            cursize = p->blksize;
            cursize = p->blksize;
            found = TRUE;
            found = TRUE;
        }
        }
    }
    }
 
 
    if (found)
    if (found)
    {
    {
        if (pool != NULL)
        if (pool != NULL)
            *pool = curid;
            *pool = curid;
        return RTEMS_SUCCESSFUL;
        return RTEMS_SUCCESSFUL;
    }
    }
    else
    else
    {
    {
        return RTEMS_NOT_DEFINED;
        return RTEMS_NOT_DEFINED;
    }
    }
}
}
 
 
/* rtems_bdbuf_get_pool_info --
/* rtems_bdbuf_get_pool_info --
 *     Obtain characteristics of buffer pool with specified number.
 *     Obtain characteristics of buffer pool with specified number.
 *
 *
 * PARAMETERS:
 * PARAMETERS:
 *     pool       - buffer pool number
 *     pool       - buffer pool number
 *     block_size - block size for which buffer pool is configured returned
 *     block_size - block size for which buffer pool is configured returned
 *                  there
 *                  there
 *     blocks     - number of buffers in buffer pool returned there
 *     blocks     - number of buffers in buffer pool returned there
 *
 *
 * RETURNS:
 * RETURNS:
 *     RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
 *     RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
 *     RTEMS_INVALID_NUMBER if appropriate buffer pool is not configured.
 *     RTEMS_INVALID_NUMBER if appropriate buffer pool is not configured.
 *
 *
 * NOTE:
 * NOTE:
 *     Buffer pools enumerated contiguously starting from 0.
 *     Buffer pools enumerated contiguously starting from 0.
 */
 */
rtems_status_code
rtems_status_code
rtems_bdbuf_get_pool_info(rtems_bdpool_id pool, int *block_size,
rtems_bdbuf_get_pool_info(rtems_bdpool_id pool, int *block_size,
                          int *blocks)
                          int *blocks)
{
{
    if (pool >= bd_ctx.npools)
    if (pool >= bd_ctx.npools)
        return RTEMS_INVALID_NUMBER;
        return RTEMS_INVALID_NUMBER;
 
 
    if (block_size != NULL)
    if (block_size != NULL)
    {
    {
        *block_size = bd_ctx.pool[pool].blksize;
        *block_size = bd_ctx.pool[pool].blksize;
    }
    }
 
 
    if (blocks != NULL)
    if (blocks != NULL)
    {
    {
        *blocks = bd_ctx.pool[pool].nblks;
        *blocks = bd_ctx.pool[pool].nblks;
    }
    }
 
 
    return RTEMS_SUCCESSFUL;
    return RTEMS_SUCCESSFUL;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.