source: rtems/cpukit/libblock/src/bdbuf.c @ 72d2ec4d

4.104.114.9
Last change on this file since 72d2ec4d was 72d2ec4d, checked in by Chris Johns <chrisj@…>, on Jul 3, 2008 at 1:37:38 AM

2008-07-03 Chris Johns <chrisj@…>

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