source: rtems/cpukit/libblock/src/bdbuf.c @ 0ebfac19

4.104.114.84.95
Last change on this file since 0ebfac19 was 0ebfac19, checked in by Joel Sherrill <joel.sherrill@…>, on 03/07/06 at 21:26:23

2006-03-07 Thomas Doerfler <Thomas.Doerfler@…>

PR 852/filesystem

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