source: rtems/cpukit/libblock/src/bdbuf.c @ bc2083c1

4.104.114.84.95
Last change on this file since bc2083c1 was c9b005a9, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 07/09/06 at 10:05:27

applied patches for PR1117/1118/1119/1120

  • Property mode set to 100644
File size: 52.0 KB
Line 
1/*
2 * Disk I/O buffering
3 * Buffer managment
4 *
5 * Copyright (C) 2001 OKTET Ltd., St.-Peterburg, Russia
6 * Author: Andrey G. Ivanov <Andrey.Ivanov@oktet.ru>
7 *         Victor V. Vengerov <vvv@oktet.ru>
8 *         Alexander Kukuta <kam@oktet.ru>
9 *
10 * @(#) $Id$
11 */
12
13#if HAVE_CONFIG_H
14#include "config.h"
15#endif
16
17#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__
18#include <rtems.h>
19#include <limits.h>
20#include <errno.h>
21#include <assert.h>
22
23#include "rtems/bdbuf.h"
24
25/* Fatal errors: */
26#define BLKDEV_FATAL_ERROR(n) (((uint32_t)'B' << 24) | ((uint32_t)(n) & (uint32_t)0x00FFFFFF))
27#define BLKDEV_FATAL_BDBUF_CONSISTENCY BLKDEV_FATAL_ERROR(1)
28#define BLKDEV_FATAL_BDBUF_SWAPOUT     BLKDEV_FATAL_ERROR(2)
29
30
31#define SWAPOUT_TASK_STACK_SIZE (RTEMS_MINIMUM_STACK_SIZE * 2)
32
33#define READ_MULTIPLE
34
35#if defined(READ_MULTIPLE)
36#define READ_AHEAD_MAX_BLK_CNT 32
37typedef struct {
38  blkdev_request   req;
39  blkdev_sg_buffer sg[READ_AHEAD_MAX_BLK_CNT];
40} blkdev_request_read_ahead;
41/*
42 * list of bd_bufs related to one transfer request
43 */
44typedef struct {
45  int cnt;
46  bdbuf_buffer *bd_bufs[READ_AHEAD_MAX_BLK_CNT];
47} read_ahead_bd_buf_group;
48#endif
49
50typedef struct {
51  blkdev_request *req;
52  bdbuf_buffer   **write_store;
53} write_tfer_done_arg_t;
54
55static rtems_task bdbuf_swapout_task(rtems_task_argument unused);
56
57static rtems_status_code bdbuf_release(bdbuf_buffer *bd_buf);
58/*
59 * The groups of the blocks with the same size are collected in the
60 * bd_pool. Note that a several of the buffer's groups with the
61 * same size can exists.
62 */
63typedef struct bdbuf_pool
64{
65    bdbuf_buffer *tree;         /* Buffer descriptor lookup AVL tree root */
66
67    Chain_Control free;         /* Free buffers list */
68    Chain_Control lru;          /* Last recently used list */
69
70    int           blksize;      /* The size of the blocks (in bytes) */
71    int           nblks;        /* Number of blocks in this pool */
72    rtems_id      bufget_sema;  /* Buffer obtain counting semaphore */
73    void         *mallocd_bufs; /* Pointer to the malloc'd buffer memory,
74                                   or NULL, if buffer memory provided in
75                                   buffer configuration */
76    bdbuf_buffer *bdbufs;       /* Pointer to table of buffer descriptors
77                                   allocated for this buffer pool. */
78} bdbuf_pool;
79
80/* Buffering layer context definition */
81struct bdbuf_context {
82    bdbuf_pool    *pool;         /* Table of buffer pools */
83    int            npools;       /* Number of entries in pool table */
84
85    Chain_Control  mod;          /* Modified buffers list */
86    rtems_id       flush_sema;   /* Buffer flush semaphore; counting
87                                    semaphore; incremented when buffer
88                                    flushed to the disk; decremented when
89                                    buffer modified */
90    rtems_id       swapout_task; /* Swapout task ID */
91};
92/*
93 * maximum number of blocks that might be chained together to one
94 * write driver call
95 */
96#define SWAP_OUT_MAX_BLK_CNT 32
97/*#define SWAP_OUT_MAX_BLK_CNT 1 */
98/*
99 * XXX: this is a global buffer. It is used in the swapout task
100 * and currently will be reused only after it is no longer in use
101 *
102 */
103static bdbuf_buffer *bd_buf_write_store[SWAP_OUT_MAX_BLK_CNT];
104
105/* Block device request with a single buffer provided */
106typedef struct blkdev_request1 {
107    blkdev_request   req;
108    blkdev_sg_buffer sg[1];
109} blkdev_request1;
110
111/* The static context of buffering layer */
112static struct bdbuf_context bd_ctx;
113
114#define SAFE
115#ifdef SAFE
116typedef rtems_mode preemption_key;
117
118#define DISABLE_PREEMPTION(key) \
119    do {                                                               \
120        rtems_task_mode(RTEMS_NO_PREEMPT, RTEMS_PREEMPT_MASK, &(key)); \
121    } while (0)
122
123#define ENABLE_PREEMPTION(key) \
124    do {                                                        \
125        rtems_mode temp;                                        \
126        rtems_task_mode((key), RTEMS_PREEMPT_MASK, &temp);      \
127    } while (0)
128
129#else
130
131typedef boolean preemption_key;
132
133#define DISABLE_PREEMPTION(key) \
134    do {                                             \
135        (key) = _Thread_Executing->is_preemptible;   \
136        _Thread_Executing->is_preemptible = 0;       \
137    } while (0)
138
139#define ENABLE_PREEMPTION(key) \
140    do {                                             \
141        _Thread_Executing->is_preemptible = (key);   \
142        if (_Thread_Evaluate_mode())                 \
143            _Thread_Dispatch();                      \
144    } while (0)
145
146#endif
147
148
149/* The default maximum height of 32 allows for AVL trees having
150   between 5,704,880 and 4,294,967,295 nodes, depending on order of
151   insertion.  You may change this compile-time constant as you
152   wish. */
153#ifndef AVL_MAX_HEIGHT
154#define AVL_MAX_HEIGHT  32
155#endif
156
157/*
158 * avl_search --
159 *     Searches for the node with specified dev/block.
160 *
161 * PARAMETERS:
162 *     root - pointer to the root node of the AVL-Tree.
163 *     dev, block - search key
164 *
165 * RETURNS:
166 *     NULL if node with specified dev/block not found
167 *     non-NULL - pointer to the node with specified dev/block
168 */
169static bdbuf_buffer *
170avl_search(bdbuf_buffer **root, dev_t dev, blkdev_bnum block)
171{
172    bdbuf_buffer *p = *root;
173
174    while ((p != NULL) && ((p->dev != dev) || (p->block != block)))
175    {
176        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
177        {
178            p = p->avl.right;
179        }
180        else
181        {
182            p = p->avl.left;
183        }
184    }
185
186    return p;
187}
188
189
190/* avl_search_for_sync --
191 *     Search in AVL tree for first modified buffer belongs to specified
192 *     disk device.
193 *
194 * PARAMETERS:
195 *     root - pointer to tree root
196 *     dd - disk device descriptor
197 *
198 * RETURNS:
199 *     Block buffer, or NULL if no modified blocks on specified device
200 *     exists.
201 */
202static bdbuf_buffer *
203avl_search_for_sync(bdbuf_buffer **root, disk_device *dd)
204{
205    dev_t dev = dd->phys_dev->dev;
206    blkdev_bnum block_start = dd->start;
207    blkdev_bnum block_end = dd->start + dd->size - 1;
208
209    bdbuf_buffer *buf_stack[AVL_MAX_HEIGHT];
210    bdbuf_buffer **buf_prev = buf_stack;
211    bdbuf_buffer *p = *root;
212
213    while (p != NULL)
214    {
215        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block_start)))
216        {
217            p = p->avl.right;
218        }
219        else if ((p->dev > dev) || ((p->dev == dev) && (p->block > block_end)))
220        {
221            p = p->avl.left;
222        }
223        else if (p->modified)
224        {
225            return p;
226        }
227        else
228        {
229            if (p->avl.right != NULL)
230            {
231                *buf_prev++ = p->avl.right;
232            }
233            p = p->avl.left;
234        }
235
236        if ((p == NULL) && (buf_prev > buf_stack))
237        {
238            p = *--buf_prev;
239        }
240    }
241
242    return p;
243}
244
245
246/*
247 * avl_insert --
248 *     Inserts the specified node to the AVl-Tree.
249 *
250 * PARAMETERS:
251 *     root - Pointer to pointer to the root node
252 *     node - Pointer to the node to add.
253 *
254 * RETURNS:
255 *     0 - The node added successfully
256 *    -1 - An error occured
257 */
258static int
259avl_insert(bdbuf_buffer **root, bdbuf_buffer *node)
260{
261    dev_t dev = node->dev;
262    blkdev_bnum block = node->block;
263
264    bdbuf_buffer *p = *root;
265    bdbuf_buffer *q, *p1, *p2;
266    bdbuf_buffer *buf_stack[AVL_MAX_HEIGHT];
267    bdbuf_buffer **buf_prev = buf_stack;
268
269    boolean modified = FALSE;
270
271    if (p == NULL)
272    {
273        *root = node;
274        node->avl.left = NULL;
275        node->avl.right = NULL;
276        node->avl.bal = 0;
277        return 0;
278    }
279
280    while (p != NULL)
281    {
282        *buf_prev++ = p;
283
284        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
285        {
286            p->avl.cache = 1;
287            q = p->avl.right;
288            if (q == NULL)
289            {
290                q = node;
291                p->avl.right = q = node;
292                break;
293            }
294        }
295        else if ((p->dev != dev) || (p->block != block))
296        {
297            p->avl.cache = -1;
298            q = p->avl.left;
299            if (q == NULL)
300            {
301                q = node;
302                p->avl.left = q;
303                break;
304            }
305        }
306        else
307        {
308            return -1;
309        }
310
311        p = q;
312    }
313
314    q->avl.left = q->avl.right = NULL;
315    q->avl.bal = 0;
316    modified = TRUE;
317    buf_prev--;
318
319    while (modified)
320    {
321        if (p->avl.cache == -1)
322        {
323            switch (p->avl.bal)
324            {
325                case 1:
326                    p->avl.bal = 0;
327                    modified = FALSE;
328                    break;
329
330                case 0:
331                    p->avl.bal = -1;
332                    break;
333
334                case -1:
335                    p1 = p->avl.left;
336                    if (p1->avl.bal == -1) /* simple LL-turn */
337                    {
338                        p->avl.left = p1->avl.right;
339                        p1->avl.right = p;
340                        p->avl.bal = 0;
341                        p = p1;
342                    }
343                    else /* double LR-turn */
344                    {
345                        p2 = p1->avl.right;
346                        p1->avl.right = p2->avl.left;
347                        p2->avl.left = p1;
348                        p->avl.left = p2->avl.right;
349                        p2->avl.right = p;
350                        if (p2->avl.bal == -1) p->avl.bal = +1; else p->avl.bal = 0;
351                        if (p2->avl.bal == +1) p1->avl.bal = -1; else p1->avl.bal = 0;
352                        p = p2;
353                    }
354                    p->avl.bal = 0;
355                    modified = FALSE;
356                    break;
357
358                default:
359                    break;
360            }
361        }
362        else
363        {
364            switch (p->avl.bal)
365            {
366                case -1:
367                    p->avl.bal = 0;
368                    modified = FALSE;
369                    break;
370
371                case 0:
372                    p->avl.bal = 1;
373                    break;
374
375                case 1:
376                    p1 = p->avl.right;
377                    if (p1->avl.bal == 1) /* simple RR-turn */
378                    {
379                        p->avl.right = p1->avl.left;
380                        p1->avl.left = p;
381                        p->avl.bal = 0;
382                        p = p1;
383                    }
384                    else /* double RL-turn */
385                    {
386                        p2 = p1->avl.left;
387                        p1->avl.left = p2->avl.right;
388                        p2->avl.right = p1;
389                        p->avl.right = p2->avl.left;
390                        p2->avl.left = p;
391                        if (p2->avl.bal == +1) p->avl.bal = -1; else p->avl.bal = 0;
392                        if (p2->avl.bal == -1) p1->avl.bal = +1; else p1->avl.bal = 0;
393                        p = p2;
394                    }
395                    p->avl.bal = 0;
396                    modified = FALSE;
397                    break;
398
399                default:
400                    break;
401            }
402        }
403        q = p;
404        if (buf_prev > buf_stack)
405        {
406            p = *--buf_prev;
407
408            if (p->avl.cache == -1)
409            {
410                p->avl.left = q;
411            }
412            else
413            {
414                p->avl.right = q;
415            }
416        }
417        else
418        {
419            *root = p;
420            break;
421        }
422    };
423
424    return 0;
425}
426
427
428/* avl_remove --
429 *     removes the node from the tree.
430 *
431 * PARAMETERS:
432 *     root_addr - Pointer to pointer to the root node
433 *     node      - Pointer to the node to remove
434 *
435 * RETURNS:
436 *     0 - Item removed
437 *    -1 - No such item found
438 */
439static int
440avl_remove(bdbuf_buffer **root, const bdbuf_buffer *node)
441{
442    dev_t dev = node->dev;
443    blkdev_bnum block = node->block;
444
445    bdbuf_buffer *p = *root;
446    bdbuf_buffer *q, *r, *s, *p1, *p2;
447    bdbuf_buffer *buf_stack[AVL_MAX_HEIGHT];
448    bdbuf_buffer **buf_prev = buf_stack;
449
450    boolean modified = FALSE;
451
452    memset(buf_stack, 0, sizeof(buf_stack));
453
454    while (p != NULL)
455    {
456        *buf_prev++ = p;
457
458        if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
459        {
460            p->avl.cache = 1;
461            p = p->avl.right;
462        }
463        else if ((p->dev != dev) || (p->block != block))
464        {
465            p->avl.cache = -1;
466            p = p->avl.left;
467        }
468        else
469        {
470            /* node found */
471            break;
472        }
473    }
474
475    if (p == NULL)
476    {
477        /* there is no such node */
478        return -1;
479    }
480
481    q = p;
482
483    buf_prev--;
484    if (buf_prev > buf_stack)
485    {
486        p = *(buf_prev - 1);
487    }
488    else
489    {
490        p = NULL;
491    }
492
493    /* at this moment q - is a node to delete, p is q's parent */
494    if (q->avl.right == NULL)
495    {
496        r = q->avl.left;
497        if (r != NULL)
498        {
499            r->avl.bal = 0;
500        }
501        q = r;
502    }
503    else
504    {
505        bdbuf_buffer **t;
506
507        r = q->avl.right;
508
509        if (r->avl.left == NULL)
510        {
511            r->avl.left = q->avl.left;
512            r->avl.bal = q->avl.bal;
513            r->avl.cache = 1;
514            *buf_prev++ = q = r;
515        }
516        else
517        {
518            t = buf_prev++;
519            s = r;
520
521            while (s->avl.left != NULL)
522            {
523                *buf_prev++ = r = s;
524                s = r->avl.left;
525                r->avl.cache = -1;
526            }
527
528            s->avl.left = q->avl.left;
529            r->avl.left = s->avl.right;
530            s->avl.right = q->avl.right;
531            s->avl.bal = q->avl.bal;
532            s->avl.cache = 1;
533
534            *t = q = s;
535        }
536    }
537
538    if (p != NULL)
539    {
540        if (p->avl.cache == -1)
541        {
542            p->avl.left = q;
543        }
544        else
545        {
546            p->avl.right = q;
547        }
548    }
549    else
550    {
551        *root = q;
552    }
553
554    modified = TRUE;
555
556    while (modified)
557    {
558        if (buf_prev > buf_stack)
559        {
560            p = *--buf_prev;
561        }
562        else
563        {
564            break;
565        }
566
567        if (p->avl.cache == -1)
568        {
569            /* rebalance left branch */
570            switch (p->avl.bal)
571            {
572                case -1:
573                    p->avl.bal = 0;
574                    break;
575                case  0:
576                    p->avl.bal = 1;
577                    modified = FALSE;
578                    break;
579
580                case +1:
581                    p1 = p->avl.right;
582
583                    if (p1->avl.bal >= 0) /* simple RR-turn */
584                    {
585                        p->avl.right = p1->avl.left;
586                        p1->avl.left = p;
587
588                        if (p1->avl.bal == 0)
589                        {
590                            p1->avl.bal = -1;
591                            modified = FALSE;
592                        }
593                        else
594                        {
595                            p->avl.bal = 0;
596                            p1->avl.bal = 0;
597                        }
598                        p = p1;
599                    }
600                    else /* double RL-turn */
601                    {
602                        p2 = p1->avl.left;
603
604                        p1->avl.left = p2->avl.right;
605                        p2->avl.right = p1;
606                        p->avl.right = p2->avl.left;
607                        p2->avl.left = p;
608
609                        if (p2->avl.bal == +1) p->avl.bal = -1; else p->avl.bal = 0;
610                        if (p2->avl.bal == -1) p1->avl.bal = 1; else p1->avl.bal = 0;
611
612                        p = p2;
613                        p2->avl.bal = 0;
614                    }
615                    break;
616
617                default:
618                    break;
619            }
620        }
621        else
622        {
623            /* rebalance right branch */
624            switch (p->avl.bal)
625            {
626                case +1:
627                    p->avl.bal = 0;
628                    break;
629
630                case  0:
631                    p->avl.bal = -1;
632                    modified = FALSE;
633                    break;
634
635                case -1:
636                    p1 = p->avl.left;
637
638                    if (p1->avl.bal <= 0) /* simple LL-turn */
639                    {
640                        p->avl.left = p1->avl.right;
641                        p1->avl.right = p;
642                        if (p1->avl.bal == 0)
643                        {
644                            p1->avl.bal = 1;
645                            modified = FALSE;
646                        }
647                        else
648                        {
649                            p->avl.bal = 0;
650                            p1->avl.bal = 0;
651                        }
652                        p = p1;
653                    }
654                    else /* double LR-turn */
655                    {
656                        p2 = p1->avl.right;
657
658                        p1->avl.right = p2->avl.left;
659                        p2->avl.left = p1;
660                        p->avl.left = p2->avl.right;
661                        p2->avl.right = p;
662
663                        if (p2->avl.bal == -1) p->avl.bal = 1; else p->avl.bal = 0;
664                        if (p2->avl.bal == +1) p1->avl.bal = -1; else p1->avl.bal = 0;
665
666                        p = p2;
667                        p2->avl.bal = 0;
668                    }
669                    break;
670
671                default:
672                    break;
673            }
674        }
675
676        if (buf_prev > buf_stack)
677        {
678            q = *(buf_prev - 1);
679
680            if (q->avl.cache == -1)
681            {
682                q->avl.left = p;
683            }
684            else
685            {
686                q->avl.right = p;
687            }
688        }
689        else
690        {
691            *root = p;
692            break;
693        }
694
695    }
696
697    return 0;
698}
699
700/* bdbuf_initialize_pool --
701 *      Initialize single buffer pool.
702 *
703 * PARAMETERS:
704 *     config - buffer pool configuration
705 *     pool   - pool number
706 *
707 * RETURNS:
708 *     RTEMS_SUCCESSFUL, if buffer pool initialized successfully, or error
709 *     code if error occured.
710 */
711static rtems_status_code
712bdbuf_initialize_pool(rtems_bdbuf_config *config, int pool)
713{
714    bdbuf_pool *p = bd_ctx.pool + pool;
715    unsigned char *bufs;
716    bdbuf_buffer *b;
717    rtems_status_code rc;
718    int i;
719
720    p->blksize = config->size;
721    p->nblks = config->num;
722    p->tree = NULL;
723
724    Chain_Initialize_empty(&p->free);
725    Chain_Initialize_empty(&p->lru);
726
727    /* Allocate memory for buffer descriptors */
728    p->bdbufs = calloc(config->num, sizeof(bdbuf_buffer));
729    if (p->bdbufs == NULL)
730    {
731        return RTEMS_NO_MEMORY;
732    }
733
734    /* Allocate memory for buffers if required */
735    if (config->mem_area == NULL)
736    {
737        bufs = p->mallocd_bufs = malloc(config->num * config->size);
738        if (bufs == NULL)
739        {
740            free(p->bdbufs);
741            return RTEMS_NO_MEMORY;
742        }
743    }
744    else
745    {
746        bufs = config->mem_area;
747        p->mallocd_bufs = NULL;
748    }
749
750    for (i = 0, b = p->bdbufs; i < p->nblks; i++, b++, bufs += p->blksize)
751    {
752        b->dev = -1; b->block = 0;
753        b->buffer = bufs;
754        b->actual = b->modified = b->in_progress = FALSE;
755        b->use_count = 0;
756        b->pool = pool;
757        _Chain_Append(&p->free, &b->link);
758    }
759
760    rc = rtems_semaphore_create(
761        rtems_build_name('B', 'U', 'F', 'G'),
762        p->nblks,
763        RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
764        RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
765        0,
766        &p->bufget_sema);
767
768    if (rc != RTEMS_SUCCESSFUL)
769    {
770        free(p->bdbufs);
771        free(p->mallocd_bufs);
772        return rc;
773    }
774
775    return RTEMS_SUCCESSFUL;
776}
777
778/* bdbuf_release_pool --
779 *     Free resources allocated for buffer pool with specified number.
780 *
781 * PARAMETERS:
782 *     pool - buffer pool number
783 *
784 * RETURNS:
785 *     RTEMS_SUCCESSFUL
786 */
787static rtems_status_code
788bdbuf_release_pool(rtems_bdpool_id pool)
789{
790    bdbuf_pool *p = bd_ctx.pool + pool;
791    rtems_semaphore_delete(p->bufget_sema);
792    free(p->bdbufs);
793    free(p->mallocd_bufs);
794    return RTEMS_SUCCESSFUL;
795}
796
797/* rtems_bdbuf_init --
798 *     Prepare buffering layer to work - initialize buffer descritors
799 *     and (if it is neccessary)buffers. Buffers will be allocated accoriding
800 *     to the configuration table, each entry describes kind of block and
801 *     amount requested. After initialization all blocks is placed into
802 *     free elements lists.
803 *
804 * PARAMETERS:
805 *     conf_table - pointer to the buffers configuration table
806 *     size       - number of entries in configuration table
807 *
808 * RETURNS:
809 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
810 *     or error code if error is occured)
811 */
812rtems_status_code
813rtems_bdbuf_init(rtems_bdbuf_config *conf_table, int size)
814{
815    rtems_bdpool_id i;
816    rtems_status_code rc;
817
818    if (size <= 0)
819        return RTEMS_INVALID_SIZE;
820
821    bd_ctx.npools = size;
822
823    /*
824     * Allocate memory for buffer pool descriptors
825     */
826    bd_ctx.pool = calloc(size, sizeof(bdbuf_pool));
827    if (bd_ctx.pool == NULL)
828    {
829        return RTEMS_NO_MEMORY;
830    }
831
832    Chain_Initialize_empty(&bd_ctx.mod);
833
834    /* Initialize buffer pools and roll out if something failed */
835    for (i = 0; i < size; i++)
836    {
837        rc = bdbuf_initialize_pool(conf_table + i, i);
838        if (rc != RTEMS_SUCCESSFUL)
839        {
840             rtems_bdpool_id j;
841             for (j = 0; j < i - 1; j++)
842             {
843                 bdbuf_release_pool(j);
844             }
845             return rc;
846        }
847    }
848
849    /* Create buffer flush semaphore */
850    rc = rtems_semaphore_create(
851        rtems_build_name('B', 'F', 'L', 'U'), 0,
852        RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
853        RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0,
854        &bd_ctx.flush_sema);
855    if (rc != RTEMS_SUCCESSFUL)
856    {
857        for (i = 0; i < size; i++)
858            bdbuf_release_pool(i);
859        free(bd_ctx.pool);
860        return rc;
861    }
862
863    /* Create and start swapout task */
864    rc = rtems_task_create(
865        rtems_build_name('B', 'S', 'W', 'P'),   
866        ((swapout_task_priority > 0)
867         ? swapout_task_priority
868         : SWAPOUT_TASK_DEFAULT_PRIORITY),
869        SWAPOUT_TASK_STACK_SIZE,
870        RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT,
871        RTEMS_DEFAULT_ATTRIBUTES,
872        &bd_ctx.swapout_task);
873    if (rc != RTEMS_SUCCESSFUL)
874    {
875        rtems_semaphore_delete(bd_ctx.flush_sema);
876        for (i = 0; i < size; i++)
877            bdbuf_release_pool(i);
878        free(bd_ctx.pool);
879        return rc;
880    }
881
882    rc = rtems_task_start(bd_ctx.swapout_task, bdbuf_swapout_task, 0);
883    if (rc != RTEMS_SUCCESSFUL)
884    {
885        rtems_task_delete(bd_ctx.swapout_task);
886        rtems_semaphore_delete(bd_ctx.flush_sema);
887        for (i = 0; i < size; i++)
888            bdbuf_release_pool(i);
889        free(bd_ctx.pool);
890        return rc;
891    }
892
893    return RTEMS_SUCCESSFUL;
894}
895
896/* find_or_assign_buffer --
897 *     Looks for buffer already assigned for this dev/block. If one is found
898 *     obtain block buffer. If specified block already cached (i.e. there's
899 *     block in the _modified_, or _recently_used_), return address
900 *     of appropriate buffer descriptor and increment reference counter to 1.
901 *     If block is not cached, allocate new buffer and return it. Data
902 *     shouldn't be read to the buffer from media; buffer contains arbitrary
903 *     data. This primitive may be blocked if there are no free buffer
904 *     descriptors available and there are no unused non-modified (or
905 *     synchronized with media) buffers available.
906 *
907 * PARAMETERS:
908 *     device - device number (constructed of major and minor device number
909 *     block  - linear media block number
910 *     ret_buf - address of the variable to store address of found descriptor
911 *
912 * RETURNS:
913 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
914 *     or error code if error is occured)
915 *
916 * SIDE EFFEECTS:
917 *     bufget_sema may be obtained by this primitive
918 *
919 * NOTE:
920 *     It is assumed that primitive invoked when thread preemption is disabled.
921 */
922static rtems_status_code
923find_or_assign_buffer(disk_device *dd,
924                      blkdev_bnum block,
925                      bdbuf_buffer **ret_buf)
926{
927    bdbuf_buffer *bd_buf;
928    bdbuf_pool   *bd_pool;
929    rtems_status_code rc;
930    dev_t         device;
931    ISR_Level     level;
932
933    int blksize;
934
935    device = dd->dev;
936    bd_pool = bd_ctx.pool + dd->pool;
937    blksize = dd->block_size;
938
939again:
940    /* Looking for buffer descriptor used for this dev/block. */
941    bd_buf = avl_search(&bd_pool->tree, device, block);
942
943    if (bd_buf == NULL)
944    {
945        /* Try to obtain semaphore without waiting first. It is the most
946           frequent case when reasonable number of buffers configured. If
947           it is failed, obtain semaphore blocking on it. In this case
948           it should be checked that appropriate buffer hasn't been loaded
949           by another thread, because this thread is preempted */
950        rc = rtems_semaphore_obtain(bd_pool->bufget_sema, RTEMS_NO_WAIT, 0);
951        if (rc == RTEMS_UNSATISFIED)
952        {
953            rc = rtems_semaphore_obtain(bd_pool->bufget_sema,
954                                        RTEMS_WAIT, RTEMS_NO_TIMEOUT);
955            bd_buf = avl_search(&bd_pool->tree, device, block);
956            if (bd_buf != NULL)
957                rtems_semaphore_release(bd_pool->bufget_sema);
958        }
959    }
960
961    if (bd_buf == NULL)
962    {
963        /* Assign new buffer descriptor */
964        if (_Chain_Is_empty(&bd_pool->free))
965        {
966            bd_buf = (bdbuf_buffer *)Chain_Get(&bd_pool->lru);
967            if (bd_buf != NULL)
968            {
969                int avl_result;
970                avl_result = avl_remove(&bd_pool->tree, bd_buf);
971                if (avl_result != 0)
972                {
973                    rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
974                    return RTEMS_INTERNAL_ERROR;
975                }
976            }
977        }
978        else
979        {
980            bd_buf = (bdbuf_buffer *)Chain_Get(&(bd_pool->free));
981        }
982
983        if (bd_buf == NULL)
984        {
985            goto again;
986        }
987        else
988        {
989            bd_buf->dev = device;
990            bd_buf->block = block;
991#ifdef AVL_GPL
992            bd_buf->avl.link[0] = NULL;
993            bd_buf->avl.link[1] = NULL;
994#else
995            bd_buf->avl.left = NULL;
996            bd_buf->avl.right = NULL;
997#endif
998            bd_buf->use_count = 1;
999            bd_buf->modified = bd_buf->actual = bd_buf->in_progress = FALSE;
1000            bd_buf->status = RTEMS_SUCCESSFUL;
1001
1002            if (avl_insert(&bd_pool->tree, bd_buf) != 0)
1003            {
1004                rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
1005                return RTEMS_INTERNAL_ERROR;
1006            }
1007
1008            *ret_buf = bd_buf;
1009
1010            return RTEMS_SUCCESSFUL;
1011        }
1012    }
1013    else
1014    {
1015        /* Buffer descriptor already assigned for this dev/block */
1016        if (bd_buf->use_count == 0)
1017        {
1018            /* If we are removing from lru list, obtain the bufget_sema
1019             * first. If we are removing from mod list, obtain flush sema.
1020             * It should be obtained without blocking because we know
1021             * that our buffer descriptor is in the list. */
1022            if (bd_buf->modified)
1023            {
1024                rc = rtems_semaphore_obtain(bd_ctx.flush_sema,
1025                                            RTEMS_NO_WAIT, 0);
1026            }
1027            else
1028            {
1029                rc = rtems_semaphore_obtain(bd_pool->bufget_sema,
1030                                            RTEMS_NO_WAIT, 0);
1031            }
1032            /* It is possible that we couldn't obtain flush or bufget sema
1033             * although buffer in the appropriate chain is available:
1034             * semaphore may be released to swapout task, but this task
1035             * actually did not start to process it. */
1036            if (rc == RTEMS_UNSATISFIED)
1037                rc = RTEMS_SUCCESSFUL;
1038            if (rc != RTEMS_SUCCESSFUL)
1039            {
1040                rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
1041                return RTEMS_INTERNAL_ERROR;
1042            }
1043
1044            /* Buffer descriptor is linked to the lru or mod chain. Remove
1045               it from there. */
1046            Chain_Extract(&bd_buf->link);
1047        }
1048        bd_buf->use_count++;
1049        while (bd_buf->in_progress != 0)
1050        {
1051            rtems_interrupt_disable(level);
1052            _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
1053                              WATCHDOG_NO_TIMEOUT, level);
1054        }
1055
1056        *ret_buf = bd_buf;
1057        return RTEMS_SUCCESSFUL;
1058    }
1059}
1060
1061/* rtems_bdbuf_get --
1062 *     Obtain block buffer. If specified block already cached (i.e. there's
1063 *     block in the _modified_, or _recently_used_), return address
1064 *     of appropriate buffer descriptor and increment reference counter to 1.
1065 *     If block is not cached, allocate new buffer and return it. Data
1066 *     shouldn't be read to the buffer from media; buffer may contains
1067 *     arbitrary data. This primitive may be blocked if there are no free
1068 *     buffer descriptors available and there are no unused non-modified
1069 *     (or synchronized with media) buffers available.
1070 *
1071 * PARAMETERS:
1072 *     device - device number (constructed of major and minor device number)
1073 *     block  - linear media block number
1074 *     bd     - address of variable to store pointer to the buffer descriptor
1075 *
1076 * RETURNS:
1077 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
1078 *     or error code if error is occured)
1079 *
1080 * SIDE EFFECTS:
1081 *     bufget_sema semaphore obtained by this primitive.
1082 */
1083rtems_status_code
1084rtems_bdbuf_get(dev_t device, blkdev_bnum block, bdbuf_buffer **bd)
1085{
1086    rtems_status_code rc;
1087    disk_device *dd;
1088    disk_device *pdd;
1089    preemption_key key;
1090
1091    /*
1092     * Convert logical dev/block to physical one
1093     */
1094    dd = rtems_disk_lookup(device);
1095    if (dd == NULL)
1096        return RTEMS_INVALID_ID;
1097
1098    if (block >= dd->size)
1099    {
1100        rtems_disk_release(dd);
1101        return RTEMS_INVALID_NUMBER;
1102    }
1103
1104    pdd = dd->phys_dev;
1105    block += dd->start;
1106    rtems_disk_release(dd);
1107
1108    DISABLE_PREEMPTION(key);
1109    rc = find_or_assign_buffer(pdd, block, bd);
1110    ENABLE_PREEMPTION(key);
1111
1112    if (rc != RTEMS_SUCCESSFUL)
1113        return rc;
1114
1115    return RTEMS_SUCCESSFUL;
1116}
1117
1118/* bdbuf_initialize_transfer_sema --
1119 *     Initialize transfer_sema mutex semaphore associated with buffer
1120 *     descriptor.
1121 */
1122static inline void
1123bdbuf_initialize_transfer_sema(bdbuf_buffer *bd_buf)
1124{
1125    CORE_mutex_Attributes mutex_attr;
1126    mutex_attr.lock_nesting_behavior = CORE_MUTEX_NESTING_BLOCKS;
1127    mutex_attr.only_owner_release = FALSE;
1128    mutex_attr.discipline = CORE_MUTEX_DISCIPLINES_FIFO;
1129    mutex_attr.priority_ceiling = 0;
1130
1131    _CORE_mutex_Initialize(&bd_buf->transfer_sema,
1132                           &mutex_attr, CORE_MUTEX_LOCKED);
1133}
1134
1135/* bdbuf_write_transfer_done --
1136 *     Callout function. Invoked by block device driver when data transfer
1137 *     to device (write) is completed. This function may be invoked from
1138 *     interrupt handler.
1139 *
1140 * PARAMETERS:
1141 *     arg    - arbitrary argument specified in block device request
1142 *              structure (in this case - pointer to the appropriate
1143 *              bdbuf_buffer buffer descriptor structure).
1144 *     status - I/O completion status
1145 *     error  - errno error code if status != RTEMS_SUCCESSFUL
1146 *
1147 * RETURNS:
1148 *     none
1149 */
1150static void
1151bdbuf_write_transfer_done(void *arg, rtems_status_code status, int error)
1152{
1153    int i;
1154    write_tfer_done_arg_t *wtd_arg = arg;
1155    blkdev_request *req = wtd_arg->req;
1156    bdbuf_buffer **bd_buf_write_store = wtd_arg->write_store;
1157    bdbuf_buffer *bd_buf;
1158    for (i = 0;i < req->count;i++) {
1159      bd_buf = bd_buf_write_store[i];
1160      bd_buf->status = status;
1161      bd_buf->error = RTEMS_IO_ERROR;
1162     
1163      bd_buf->in_progress = FALSE;
1164      _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL);
1165      _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
1166                        CORE_MUTEX_STATUS_SUCCESSFUL);
1167    }
1168}
1169
1170/* bdbuf_read_transfer_done --
1171 *     Callout function. Invoked by block device driver when data transfer
1172 *     from device (read) is completed. This function may be invoked from
1173 *     interrupt handler.
1174 *
1175 * PARAMETERS:
1176 *     arg    - arbitrary argument specified in block device request
1177 *              structure (in this case - pointer to the appropriate
1178 *              bdbuf_buffer buffer descriptor structure).
1179 *     status - I/O completion status
1180 *     error  - errno error code if status != RTEMS_SUCCESSFUL
1181 *
1182 * RETURNS:
1183 *     none
1184 */
1185static void
1186bdbuf_read_transfer_done(void *arg, rtems_status_code status, int error)
1187{
1188#if defined(READ_MULTIPLE) 
1189 
1190  read_ahead_bd_buf_group *bd_buf_group = arg;
1191  bdbuf_buffer *bd_buf;
1192  int i;
1193  for (i = 0;i < bd_buf_group->cnt;i++) {
1194    bd_buf = bd_buf_group->bd_bufs[i];
1195
1196    bd_buf->status = status;
1197    bd_buf->error = RTEMS_IO_ERROR;
1198    _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL);
1199    _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
1200                      CORE_MUTEX_STATUS_SUCCESSFUL);
1201  }
1202#else
1203    bdbuf_buffer *bd_buf = arg;
1204    bd_buf->status = status;
1205    bd_buf->error = RTEMS_IO_ERROR;
1206    _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL);
1207    _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
1208                      CORE_MUTEX_STATUS_SUCCESSFUL);
1209#endif
1210}
1211
1212/* rtems_bdbuf_read --
1213 *     (Similar to the rtems_bdbuf_get, except reading data from media)
1214 *     Obtain block buffer. If specified block already cached, return address
1215 *     of appropriate buffer and increment reference counter to 1. If block is
1216 *     not cached, allocate new buffer and read data to it from the media.
1217 *     This primitive may be blocked on waiting until data to be read from
1218 *     media, if there are no free buffer descriptors available and there are
1219 *     no unused non-modified (or synchronized with media) buffers available.
1220 *
1221 * PARAMETERS:
1222 *     device - device number (consists of major and minor device number)
1223 *     block  - linear media block number
1224 *     bd     - address of variable to store pointer to the buffer descriptor
1225 *
1226 * RETURNS:
1227 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
1228 *     or error code if error is occured)
1229 *
1230 * SIDE EFFECTS:
1231 *     bufget_sema and transfer_sema semaphores obtained by this primitive.
1232 */
1233#if !defined(READ_MULTIPLE)
1234rtems_status_code
1235rtems_bdbuf_read(dev_t device,
1236                 blkdev_bnum block,
1237                 bdbuf_buffer **bd)
1238{
1239    preemption_key key;
1240    ISR_Level level;
1241
1242    bdbuf_buffer *bd_buf;
1243    rtems_status_code rc;
1244    int result;
1245    disk_device *dd;
1246    disk_device *pdd;
1247    blkdev_request1 req;
1248
1249    dd = rtems_disk_lookup(device);
1250    if (dd == NULL)
1251        return RTEMS_INVALID_ID;
1252
1253    if (block >= dd->size)
1254    {
1255        rtems_disk_release(dd);
1256        return RTEMS_INVALID_NUMBER;
1257    }
1258
1259    pdd = dd->phys_dev;
1260    block += dd->start;
1261
1262    DISABLE_PREEMPTION(key);
1263    rc = find_or_assign_buffer(pdd, block, &bd_buf);
1264
1265    if (rc != RTEMS_SUCCESSFUL)
1266    {
1267        ENABLE_PREEMPTION(key);
1268        rtems_disk_release(dd);
1269        return rc;
1270    }
1271
1272    if (!bd_buf->actual)
1273    {
1274        bd_buf->in_progress = 1;
1275
1276        req.req.req = BLKDEV_REQ_READ;
1277        req.req.req_done = bdbuf_read_transfer_done;
1278        req.req.done_arg = bd_buf;
1279        req.req.start = block;
1280        req.req.count = 1;
1281        req.req.bufnum = 1;
1282        req.req.bufs[0].length = dd->block_size;
1283        req.req.bufs[0].buffer = bd_buf->buffer;
1284
1285        bdbuf_initialize_transfer_sema(bd_buf);
1286        result = dd->ioctl(pdd->dev, BLKIO_REQUEST, &req);
1287        if (result == -1)
1288        {
1289            bd_buf->status = RTEMS_IO_ERROR;
1290            bd_buf->error = errno;
1291            bd_buf->actual = FALSE;
1292        }
1293        else
1294        {
1295            rtems_interrupt_disable(level);
1296            _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
1297                              WATCHDOG_NO_TIMEOUT, level);
1298            bd_buf->actual = TRUE;
1299        }
1300        bd_buf->in_progress = FALSE;
1301    }
1302    rtems_disk_release(dd);
1303
1304    ENABLE_PREEMPTION(key);
1305
1306    *bd = bd_buf;
1307           
1308    return RTEMS_SUCCESSFUL;
1309}
1310#else /* READ_MULTIPLE */
1311rtems_status_code
1312rtems_bdbuf_read(dev_t device,
1313                 blkdev_bnum block,
1314                 bdbuf_buffer **bd)
1315{
1316    preemption_key key;
1317    ISR_Level level;
1318
1319    bdbuf_buffer *bd_buf,*first_bd_buf;
1320    rtems_status_code rc;
1321    int result;
1322    disk_device *dd;
1323    disk_device *pdd;
1324    blkdev_request_read_ahead req;
1325    read_ahead_bd_buf_group bd_buf_group;
1326    boolean find_more_buffers;
1327    int i;
1328
1329    dd = rtems_disk_lookup(device);
1330    if (dd == NULL)
1331        return RTEMS_INVALID_ID;
1332     
1333    if (block >= dd->size)
1334    {
1335        rtems_disk_release(dd);
1336        return RTEMS_INVALID_NUMBER;
1337    }
1338   
1339    pdd = dd->phys_dev;
1340    block += dd->start;
1341
1342    DISABLE_PREEMPTION(key);
1343    rc = find_or_assign_buffer(pdd, block, &first_bd_buf);
1344
1345    if (rc != RTEMS_SUCCESSFUL)
1346    {
1347        ENABLE_PREEMPTION(key);
1348        rtems_disk_release(dd);
1349        return rc;
1350    }
1351    if (!first_bd_buf->actual)
1352    {
1353
1354        bd_buf_group.bd_bufs[0] = first_bd_buf;
1355        bd_buf_group.cnt = 1;
1356
1357        first_bd_buf->in_progress = TRUE;
1358
1359        req.req.req = BLKDEV_REQ_READ;
1360        req.req.req_done = bdbuf_read_transfer_done;
1361        req.req.done_arg = &bd_buf_group;
1362        req.req.start = block;
1363        req.req.count = 1;
1364        req.req.bufnum = 1;
1365        req.req.bufs[0].length = dd->block_size;
1366        req.req.bufs[0].buffer = first_bd_buf->buffer;
1367       
1368        bdbuf_initialize_transfer_sema(first_bd_buf);
1369        /*
1370         * FIXME: check for following blocks to be:
1371         *   - still in range of partition size
1372         *   - not yet assigned
1373         *   - buffer available
1374         * allocate for read call, if possible
1375         */
1376        find_more_buffers = TRUE;
1377        while (find_more_buffers) {       
1378          block++;
1379          /*
1380           * still bd_buf_group entries free and
1381           * still in range of this disk?
1382           */
1383          if ((bd_buf_group.cnt >= READ_AHEAD_MAX_BLK_CNT) ||
1384              (block >= dd->size)) {
1385            find_more_buffers = FALSE;
1386          }
1387          if (find_more_buffers) {
1388            rc = find_or_assign_buffer(pdd, block, &bd_buf);
1389            if (rc != RTEMS_SUCCESSFUL) {
1390              find_more_buffers = FALSE;
1391            }     
1392            else if (bd_buf->actual) {
1393              find_more_buffers = FALSE;
1394          bdbuf_release(bd_buf);
1395            }     
1396          }
1397          if (find_more_buffers) {
1398            bdbuf_initialize_transfer_sema(bd_buf);
1399            bd_buf->in_progress = TRUE;
1400
1401            req.req.bufs[req.req.count].length = dd->block_size;
1402            req.req.bufs[req.req.count].buffer = bd_buf->buffer;
1403            req.req.count++;
1404            req.req.bufnum++;
1405            bd_buf_group.bd_bufs[bd_buf_group.cnt] = bd_buf;
1406            bd_buf_group.cnt++;
1407          }
1408        }           
1409
1410        /* do the actual read call here
1411         */
1412        result = dd->ioctl(pdd->dev, BLKIO_REQUEST, &req);
1413
1414        /*
1415         * cleanup:
1416         * wait, until all bd_bufs are processed
1417         * set status in all bd_bufs
1418         */
1419        for (i = 0;i < bd_buf_group.cnt;i++) {
1420          bd_buf = bd_buf_group.bd_bufs[i];
1421          if (result == -1)
1422            {
1423              bd_buf->status = RTEMS_IO_ERROR;
1424              bd_buf->error = errno;
1425              bd_buf->actual = FALSE;
1426            }
1427          else
1428            {
1429              rtems_interrupt_disable(level);
1430              _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
1431                                WATCHDOG_NO_TIMEOUT, level);
1432              bd_buf->actual = TRUE;
1433            }   
1434          bd_buf->in_progress = FALSE;
1435          /* release any pre-read buffers */
1436          if (i > 0) {
1437            bdbuf_release(bd_buf);
1438          }
1439        }
1440    }
1441    rtems_disk_release(dd);
1442   
1443    ENABLE_PREEMPTION(key);
1444
1445    *bd = first_bd_buf;
1446           
1447    return RTEMS_SUCCESSFUL;
1448}
1449#endif /* READ_MULTIPLE */
1450
1451
1452/* bdbuf_release --
1453 *     Release buffer. Decrease buffer usage counter. If it is zero, further
1454 *     processing depends on modified attribute. If buffer was modified, it
1455 *     is inserted into mod chain and swapout task waken up. If buffer was
1456 *     not modified, it is returned to the end of lru chain making it available
1457 *     for further use.
1458 *
1459 * PARAMETERS:
1460 *     bd_buf - pointer to the released buffer descriptor.
1461 *
1462 * RETURNS:
1463 *     RTEMS_SUCCESSFUL if buffer released successfully, or error code if
1464 *     error occured.
1465 *
1466 * NOTE:
1467 *     This is internal function. It is assumed that task made non-preemptive
1468 *     before its invocation.
1469 */
1470static rtems_status_code
1471bdbuf_release(bdbuf_buffer *bd_buf)
1472{
1473    bdbuf_pool *bd_pool;
1474    rtems_status_code rc = RTEMS_SUCCESSFUL;
1475
1476    if (bd_buf->use_count <= 0)
1477        return RTEMS_INTERNAL_ERROR;
1478
1479    bd_pool = bd_ctx.pool + bd_buf->pool;
1480
1481    bd_buf->use_count--;
1482
1483    if (bd_buf->use_count == 0)
1484    {
1485        if (bd_buf->modified)
1486        {
1487
1488            /* Buffer was modified. Insert buffer to the modified buffers
1489             * list and initiate flushing. */
1490            Chain_Append(&bd_ctx.mod, &bd_buf->link);
1491
1492            /* Release the flush_sema */
1493            rc = rtems_semaphore_release(bd_ctx.flush_sema);
1494        }
1495        else
1496        {
1497            /* Buffer was not modified. Add this descriptor to the
1498             * end of lru chain and make it available for reuse. */
1499            Chain_Append(&bd_pool->lru, &bd_buf->link);
1500            rc = rtems_semaphore_release(bd_pool->bufget_sema);
1501        }
1502    }
1503    return rc;
1504}
1505
1506
1507/* rtems_bdbuf_release --
1508 *     Release buffer allocated before. This primitive decrease the
1509 *     usage counter. If it is zero, further destiny of buffer depends on
1510 *     'modified' status. If buffer was modified, it is placed to the end of
1511 *     mod list and flush task waken up. If buffer was not modified,
1512 *     it is placed to the end of lru list, and bufget_sema released, allowing
1513 *     to reuse this buffer.
1514 *
1515 * PARAMETERS:
1516 *     bd_buf - pointer to the bdbuf_buffer structure previously obtained using
1517 *              get/read primitive.
1518 *
1519 * RETURNS:
1520 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
1521 *     or error code if error is occured)
1522 *
1523 * SIDE EFFECTS:
1524 *     flush_sema and bufget_sema semaphores may be released by this primitive.
1525 */
1526rtems_status_code
1527rtems_bdbuf_release(bdbuf_buffer *bd_buf)
1528{
1529    preemption_key key;
1530    rtems_status_code rc = RTEMS_SUCCESSFUL;
1531
1532    if (bd_buf == NULL)
1533        return RTEMS_INVALID_ADDRESS;
1534
1535    DISABLE_PREEMPTION(key);
1536
1537    rc = bdbuf_release(bd_buf);
1538
1539    ENABLE_PREEMPTION(key);
1540
1541    return rc;
1542}
1543
1544/* rtems_bdbuf_release_modified --
1545 *     Release buffer allocated before, assuming that it is _modified_ by
1546 *     it's owner. This primitive decrease usage counter for buffer, mark
1547 *     buffer descriptor as modified. If usage counter is 0, insert it at
1548 *     end of mod chain and release flush_sema semaphore to activate the
1549 *     flush task.
1550 *
1551 * PARAMETERS:
1552 *     bd_buf - pointer to the bdbuf_buffer structure previously obtained using
1553 *              get/read primitive.
1554 *
1555 * RETURNS:
1556 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
1557 *     or error code if error is occured)
1558 *
1559 * SIDE EFFECTS:
1560 *     flush_sema semaphore may be released by this primitive.
1561 */
1562rtems_status_code
1563rtems_bdbuf_release_modified(bdbuf_buffer *bd_buf)
1564{
1565    preemption_key key;
1566    rtems_status_code rc = RTEMS_SUCCESSFUL;
1567
1568    if (bd_buf == NULL)
1569        return RTEMS_INVALID_ADDRESS;
1570
1571    DISABLE_PREEMPTION(key);
1572
1573    if (!bd_buf->modified)
1574    {
1575        bdbuf_initialize_transfer_sema(bd_buf);
1576    }
1577    bd_buf->modified = TRUE;
1578    bd_buf->actual = TRUE;
1579    rc = bdbuf_release(bd_buf);
1580
1581    ENABLE_PREEMPTION(key);
1582
1583    return rc;
1584}
1585
1586/* rtems_bdbuf_sync --
1587 *     Wait until specified buffer synchronized with disk. Invoked on exchanges
1588 *     critical for data consistency on the media. This primitive mark owned
1589 *     block as modified, decrease usage counter. If usage counter is 0,
1590 *     block inserted to the mod chain and flush_sema semaphore released.
1591 *     Finally, primitives blocked on transfer_sema semaphore.
1592 *
1593 * PARAMETERS:
1594 *     bd_buf - pointer to the bdbuf_buffer structure previously obtained using
1595 *              get/read primitive.
1596 *
1597 * RETURNS:
1598 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
1599 *     or error code if error is occured)
1600 *
1601 * SIDE EFFECTS:
1602 *     Primitive may be blocked on transfer_sema semaphore.
1603 */
1604rtems_status_code
1605rtems_bdbuf_sync(bdbuf_buffer *bd_buf)
1606{
1607    preemption_key key;
1608    ISR_Level level;
1609    rtems_status_code rc = RTEMS_SUCCESSFUL;
1610
1611    if (bd_buf == NULL)
1612        return RTEMS_INVALID_ADDRESS;
1613
1614    DISABLE_PREEMPTION(key);
1615
1616    if (!bd_buf->modified)
1617    {
1618        bdbuf_initialize_transfer_sema(bd_buf);
1619    }
1620    bd_buf->modified = TRUE;
1621    bd_buf->actual = TRUE;
1622
1623    rc = bdbuf_release(bd_buf);
1624
1625    if (rc == RTEMS_SUCCESSFUL)
1626    {
1627        rtems_interrupt_disable(level);
1628        _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
1629                          WATCHDOG_NO_TIMEOUT, level);
1630    }
1631
1632    ENABLE_PREEMPTION(key);
1633
1634    return rc;
1635}
1636
1637/* rtems_bdbuf_syncdev --
1638 *     Synchronize with disk all buffers containing the blocks belonging to
1639 *     specified device.
1640 *
1641 * PARAMETERS:
1642 *     dev - block device number
1643 *
1644 * RETURNS:
1645 *     RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
1646 *     or error code if error is occured)
1647 */
1648rtems_status_code
1649rtems_bdbuf_syncdev(dev_t dev)
1650{
1651    preemption_key key;
1652    ISR_Level level;
1653
1654    bdbuf_buffer *bd_buf;
1655    disk_device *dd;
1656    bdbuf_pool  *pool;
1657
1658    dd = rtems_disk_lookup(dev);
1659    if (dd == NULL)
1660        return RTEMS_INVALID_ID;
1661
1662    pool = bd_ctx.pool + dd->pool;
1663
1664    DISABLE_PREEMPTION(key);
1665    do {
1666        bd_buf = avl_search_for_sync(&pool->tree, dd);
1667        if (bd_buf != NULL /* && bd_buf->modified */)
1668        {
1669            rtems_interrupt_disable(level);
1670            _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
1671                              WATCHDOG_NO_TIMEOUT, level);
1672        }
1673    } while (bd_buf != NULL);
1674    ENABLE_PREEMPTION(key);
1675    return rtems_disk_release(dd);
1676}
1677
1678/* bdbuf_swapout_task --
1679 *     Body of task which take care on flushing modified buffers to the
1680 *     disk.
1681 */
1682static rtems_task
1683bdbuf_swapout_task(rtems_task_argument unused)
1684{
1685    rtems_status_code rc;
1686    int result;
1687    int i;
1688    ISR_Level level;
1689    bdbuf_buffer *bd_buf;
1690    bdbuf_buffer *nxt_bd_buf;
1691    bdbuf_pool *bd_pool = NULL;
1692    disk_device *dd = NULL;
1693    struct {
1694      blkdev_request   req;
1695      blkdev_sg_buffer sg[SWAP_OUT_MAX_BLK_CNT];
1696    } req;
1697    write_tfer_done_arg_t write_tfer_done_arg;
1698
1699    /*
1700     * provide info needed for write_transfer_done function
1701     */
1702    write_tfer_done_arg.req         = (blkdev_request *)&req.req;
1703    write_tfer_done_arg.write_store =  bd_buf_write_store;
1704    nxt_bd_buf = NULL;
1705    while (1)
1706    {
1707        req.req.req = BLKDEV_REQ_WRITE;
1708        req.req.req_done = bdbuf_write_transfer_done;
1709        req.req.done_arg = &write_tfer_done_arg;
1710        req.req.count = 0;
1711        req.req.bufnum = 0;
1712        bd_buf = NULL;
1713        do {
1714          /*
1715           * if a buffer was left over from last loop, then use this buffer
1716           * otherwise fetch new buffer from chain.
1717           * Wait for buffer, if this is the first one of the request,
1718           * otherwise do not wait, if no buffer available
1719           */
1720          if (nxt_bd_buf == NULL) {
1721            rc = rtems_semaphore_obtain(bd_ctx.flush_sema,
1722                                        (req.req.count == 0)
1723                                        ? RTEMS_WAIT
1724                                        : RTEMS_NO_WAIT,
1725                                        0);
1726            if (rc == RTEMS_SUCCESSFUL) {
1727              nxt_bd_buf = (bdbuf_buffer *)Chain_Get(&bd_ctx.mod);
1728          if (nxt_bd_buf != NULL) {
1729                nxt_bd_buf->in_progress = TRUE;
1730            /* IMD try: clear "modified" bit early             */
1731            /* (and not in bdbuf_write_transfer_done) to allow */
1732            /* another modification during write processing    */
1733            nxt_bd_buf->modified    = FALSE;
1734
1735                nxt_bd_buf->use_count++;
1736                  }
1737            }
1738            else if ((rc != RTEMS_UNSATISFIED) &&
1739                     (rc != RTEMS_TIMEOUT)) {
1740              rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_SWAPOUT);
1741            }
1742          }
1743          /*
1744           * It is possible that flush_sema semaphore will be released, but
1745           * buffer to be removed from mod chain before swapout task start
1746           * its processing.
1747           */
1748          if ((req.req.count == 0)  || /* first bd_buf for this request */
1749              ((nxt_bd_buf        != NULL)            &&
1750               (nxt_bd_buf->dev   == bd_buf->dev)     && /* same device */
1751               (nxt_bd_buf->block == bd_buf->block+1))) {/* next block  */
1752            bd_buf     = nxt_bd_buf;
1753            nxt_bd_buf = NULL;
1754          }
1755          else {
1756            bd_buf = NULL;
1757          }
1758          /*
1759           * here we have three possible states:
1760           * bd_buf == NULL, nxt_bd_buf == NULL: no further block available
1761           * bd_buf != NULL, nxt_bd_buf == NULL: append bd_buf to request
1762           * bd_buf == NULL, nxt_bd_buf != NULL: nxt_bd_buf canot be appended
1763           *                                     to current request, keep it
1764           *                                     for next main loop
1765           */
1766          if (bd_buf != NULL) {
1767            bd_pool = bd_ctx.pool + bd_buf->pool;
1768            if (req.req.count == 0) {
1769              /*
1770               * this is the first block, so use its address
1771               */
1772              dd = rtems_disk_lookup(bd_buf->dev);
1773              req.req.start = bd_buf->block + dd->start;
1774            }
1775            req.req.bufs[req.req.bufnum].length = dd->block_size;
1776            req.req.bufs[req.req.bufnum].buffer = bd_buf->buffer;
1777            /*
1778             * keep bd_buf for postprocessing
1779             */
1780            bd_buf_write_store[req.req.bufnum] = bd_buf;
1781            req.req.count++;
1782            req.req.bufnum++;
1783          }
1784        } while ((bd_buf != NULL) &&
1785                 (req.req.count < SWAP_OUT_MAX_BLK_CNT));
1786                 
1787        /* transfer_sema initialized when bd_buf inserted in the mod chain
1788           first time */
1789        result = dd->ioctl(dd->phys_dev->dev, BLKIO_REQUEST, &req);
1790
1791        rtems_disk_release(dd);
1792       
1793        for (i = 0;i < req.req.count;i++) {
1794          bd_buf = bd_buf_write_store[i];
1795          if (result == -1)
1796            {
1797             
1798              bd_buf->status = RTEMS_IO_ERROR;
1799              bd_buf->error = errno;
1800              /* Release tasks waiting on syncing this buffer */
1801              _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
1802                                CORE_MUTEX_STATUS_SUCCESSFUL);
1803            }
1804          else
1805            {
1806              if (bd_buf->in_progress)
1807                {
1808                  rtems_interrupt_disable(level);
1809                  _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, 0, level);
1810                }
1811            }
1812          bd_buf->use_count--;
1813
1814          /* Another task have chance to use this buffer, or even
1815           * modify it. If buffer is not in use, insert it in appropriate chain
1816           * and release semaphore */
1817          if (bd_buf->use_count == 0)
1818            {
1819              if (bd_buf->modified)
1820                {
1821                  Chain_Append(&bd_ctx.mod, &bd_buf->link);
1822                  rc = rtems_semaphore_release(bd_ctx.flush_sema);
1823                }
1824              else
1825                {
1826                  Chain_Append(&bd_pool->lru, &bd_buf->link);
1827                  rc = rtems_semaphore_release(bd_pool->bufget_sema);
1828                }
1829            }
1830        }
1831    }
1832}
1833
1834/* rtems_bdbuf_find_pool --
1835 *     Find first appropriate buffer pool. This primitive returns the index
1836 *     of first buffer pool which block size is greater than or equal to
1837 *     specified size.
1838 *
1839 * PARAMETERS:
1840 *     block_size - requested block size
1841 *     pool       - placeholder for result
1842 *
1843 * RETURNS:
1844 *     RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
1845 *     RTEMS_INVALID_SIZE if specified block size is invalid (not a power
1846 *     of 2), RTEMS_NOT_DEFINED if buffer pool for this or greater block size
1847 *     is not configured.
1848 */
1849rtems_status_code
1850rtems_bdbuf_find_pool(int block_size, rtems_bdpool_id *pool)
1851{
1852    rtems_bdpool_id i;
1853    bdbuf_pool *p;
1854    int cursize = INT_MAX;
1855    rtems_bdpool_id curid = -1;
1856    rtems_boolean found = FALSE;
1857    int j;
1858
1859    for (j = block_size; (j != 0) && ((j & 1) == 0); j >>= 1);
1860    if (j != 1)
1861        return RTEMS_INVALID_SIZE;
1862
1863    for (i = 0, p = bd_ctx.pool; i < bd_ctx.npools; i++, p++)
1864    {
1865        if ((p->blksize >= block_size) &&
1866            (p->blksize < cursize))
1867        {
1868            curid = i;
1869            cursize = p->blksize;
1870            found = TRUE;
1871        }
1872    }
1873
1874    if (found)
1875    {
1876        if (pool != NULL)
1877            *pool = curid;
1878        return RTEMS_SUCCESSFUL;
1879    }
1880    else
1881    {
1882        return RTEMS_NOT_DEFINED;
1883    }
1884}
1885
1886/* rtems_bdbuf_get_pool_info --
1887 *     Obtain characteristics of buffer pool with specified number.
1888 *
1889 * PARAMETERS:
1890 *     pool       - buffer pool number
1891 *     block_size - block size for which buffer pool is configured returned
1892 *                  there
1893 *     blocks     - number of buffers in buffer pool returned there
1894 *
1895 * RETURNS:
1896 *     RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
1897 *     RTEMS_INVALID_NUMBER if appropriate buffer pool is not configured.
1898 *
1899 * NOTE:
1900 *     Buffer pools enumerated contiguously starting from 0.
1901 */
1902rtems_status_code
1903rtems_bdbuf_get_pool_info(rtems_bdpool_id pool, int *block_size,
1904                          int *blocks)
1905{
1906    if (pool >= bd_ctx.npools)
1907        return RTEMS_INVALID_NUMBER;
1908
1909    if (block_size != NULL)
1910    {
1911        *block_size = bd_ctx.pool[pool].blksize;
1912    }
1913
1914    if (blocks != NULL)
1915    {
1916        *blocks = bd_ctx.pool[pool].nblks;
1917    }
1918
1919    return RTEMS_SUCCESSFUL;
1920}
Note: See TracBrowser for help on using the repository browser.