source: rtems/cpukit/libblock/src/diskdevs.c @ 904ced08

4.104.114.9
Last change on this file since 904ced08 was 904ced08, checked in by Chris Johns <chrisj@…>, on Aug 6, 2008 at 4:04:59 AM

2008-08-06 Till Straumann <strauman@…>

  • libblock/src/diskdevs.c: PR1269. Delete the semaphore rather than release it.
  • Property mode set to 100644
File size: 17.2 KB
Line 
1/*
2 * diskdevs.c - Physical and logical block devices (disks) support
3 *
4 * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
5 * Author: Victor V. Vengerov <vvv@oktet.ru>
6 *
7 * @(#) $Id$
8 */
9
10#if HAVE_CONFIG_H
11#include "config.h"
12#endif
13
14#include <rtems.h>
15#include <rtems/libio.h>
16#include <stdlib.h>
17#include <unistd.h>     /* unlink */
18#include <string.h>
19
20#include "rtems/diskdevs.h"
21#include "rtems/bdbuf.h"
22
23#define DISKTAB_INITIAL_SIZE 32
24
25/* Table of disk devices having the same major number */
26typedef struct rtems_disk_device_table {
27    rtems_disk_device **minor; /* minor-indexed disk device table */
28    int size;            /* Number of entries in the table */
29} rtems_disk_device_table;
30
31/* Pointer to [major].minor[minor] indexed array of disk devices */
32static rtems_disk_device_table *disktab;
33
34/* Number of allocated entries in disktab table */
35static int disktab_size;
36
37/* Mutual exclusion semaphore for disk devices table */
38static rtems_id diskdevs_mutex;
39
40/* Flag meaning that disk I/O, buffering etc. already has been initialized. */
41static boolean disk_io_initialized = FALSE;
42
43/* diskdevs data structures protection flag.
44 * Normally, only table lookup operations performed. It is quite fast, so
45 * it is possible to done lookups when interrupts are disabled, avoiding
46 * obtaining the semaphore. This flags sets immediately after entering in
47 * mutex-protected section and cleared before leaving this section in
48 * "big" primitives like add/delete new device etc. Lookup function first
49 * disable interrupts and check this flag. If it is set, lookup function
50 * will be blocked on semaphore and lookup operation will be performed in
51 * semaphore-protected code. If it is not set (very-very frequent case),
52 * we can do lookup safely, enable interrupts and return result.
53 */
54static volatile rtems_boolean diskdevs_protected;
55
56/* create_disk_entry --
57 *     Return pointer to the disk_entry structure for the specified device, or
58 *     create one if it is not exists.
59 *
60 * PARAMETERS:
61 *     dev - device id (major, minor)
62 *
63 * RETURNS:
64 *     pointer to the disk device descriptor entry, or NULL if no memory
65 *     available for its creation.
66 */
67static rtems_disk_device *
68create_disk_entry(dev_t dev)
69{
70    rtems_device_major_number major;
71    rtems_device_minor_number minor;
72    rtems_disk_device **d;
73
74    rtems_filesystem_split_dev_t (dev, major, minor);
75
76    if (major >= disktab_size)
77    {
78        rtems_disk_device_table *p;
79        int newsize;
80        int i;
81        newsize = disktab_size * 2;
82        if (major >= newsize)
83            newsize = major + 1;
84        p = realloc(disktab, sizeof(rtems_disk_device_table) * newsize);
85        if (p == NULL)
86            return NULL;
87        p += disktab_size;
88        for (i = disktab_size; i < newsize; i++, p++)
89        {
90            p->minor = NULL;
91            p->size = 0;
92        }
93        disktab_size = newsize;
94    }
95
96    if ((disktab[major].minor == NULL) ||
97        (minor >= disktab[major].size))
98    {
99        int newsize;
100        rtems_disk_device **p;
101        int i;
102        int s = disktab[major].size;
103
104        if (s == 0)
105            newsize = DISKTAB_INITIAL_SIZE;
106        else
107            newsize = s * 2;
108        if (minor >= newsize)
109            newsize = minor + 1;
110
111        p = realloc(disktab[major].minor,
112                    sizeof(rtems_disk_device *) * newsize);
113        if (p == NULL)
114            return NULL;
115        disktab[major].minor = p;
116        p += s;
117        for (i = s; i < newsize; i++, p++)
118            *p = NULL;
119        disktab[major].size = newsize;
120    }
121
122    d = disktab[major].minor + minor;
123    if (*d == NULL)
124    {
125        *d = calloc(1, sizeof(rtems_disk_device));
126    }
127    return *d;
128}
129
130/* get_disk_entry --
131 *     Get disk device descriptor by device number.
132 *
133 * PARAMETERS:
134 *     dev - block device number
135 *
136 * RETURNS:
137 *     Pointer to the disk device descriptor corresponding to the specified
138 *     device number, or NULL if disk device with such number not exists.
139 */
140static rtems_disk_device *
141get_disk_entry(dev_t dev)
142{
143    rtems_device_major_number major;
144    rtems_device_minor_number minor;
145    rtems_disk_device_table *dtab;
146
147    rtems_filesystem_split_dev_t (dev, major, minor);
148
149    if ((major >= disktab_size) || (disktab == NULL))
150        return NULL;
151
152    dtab = disktab + major;
153
154    if ((minor >= dtab->size) || (dtab->minor == NULL))
155        return NULL;
156
157    return dtab->minor[minor];
158}
159
160/* create_disk --
161 *     Check that disk entry for specified device number is not defined
162 *     and create it.
163 *
164 * PARAMETERS:
165 *     dev        - device identifier (major, minor numbers)
166 *     name       - character name of device (e.g. /dev/hda)
167 *     disdev     - placeholder for pointer to created disk descriptor
168 *
169 * RETURNS:
170 *     RTEMS_SUCCESSFUL if disk entry successfully created, or
171 *     error code if error occured (device already registered,
172 *     no memory available).
173 */
174static rtems_status_code
175create_disk(dev_t dev, const char *name, rtems_disk_device **diskdev)
176{
177    rtems_disk_device *dd;
178    char *n;
179
180    dd = get_disk_entry(dev);
181    if (dd != NULL)
182    {
183        return RTEMS_RESOURCE_IN_USE;
184    }
185
186    if (name == NULL)
187    {
188        n = NULL;
189    }
190    else
191    {
192        int nlen = strlen(name) + 1;
193        n = malloc(nlen);
194        if (n == NULL)
195            return RTEMS_NO_MEMORY;
196        strncpy(n, name, nlen);
197    }
198
199    dd = create_disk_entry(dev);
200    if (dd == NULL)
201    {
202        free(n);
203        return RTEMS_NO_MEMORY;
204    }
205
206    dd->dev = dev;
207    dd->name = n;
208
209    *diskdev = dd;
210
211    return RTEMS_SUCCESSFUL;
212}
213
214/* rtems_disk_create_phys --
215 *     Create physical disk entry. This function usually invoked from
216 *     block device driver initialization code when physical device
217 *     detected in the system. Device driver should provide ioctl handler
218 *     to allow block device access operations. This primitive will register
219 *     device in rtems (invoke rtems_io_register_name).
220 *
221 * PARAMETERS:
222 *     dev        - device identifier (major, minor numbers)
223 *     block_size - size of disk block (minimum data transfer unit); must be
224 *                  power of 2
225 *     disk_size  - number of blocks on device
226 *     handler    - IOCTL handler (function providing basic block input/output
227 *                  request handling BIOREQUEST and other device management
228 *                  operations)
229 *     name       - character name of device (e.g. /dev/hda)
230 *
231 * RETURNS:
232 *     RTEMS_SUCCESSFUL if information about new physical disk added, or
233 *     error code if error occured (device already registered, wrong block
234 *     size value, no memory available).
235 */
236rtems_status_code
237rtems_disk_create_phys(dev_t dev, int block_size, int disk_size,
238                       rtems_block_device_ioctl handler,
239                       const char *name)
240{
241    int bs_log2;
242    int i;
243    rtems_disk_device *dd;
244    rtems_status_code rc;
245    rtems_bdpool_id pool;
246    rtems_device_major_number major;
247    rtems_device_minor_number minor;
248
249    rtems_filesystem_split_dev_t (dev, major, minor);
250
251
252    for (bs_log2 = 0, i = block_size; (i & 1) == 0; i >>= 1, bs_log2++);
253    if ((bs_log2 < 9) || (i != 1)) /* block size < 512 or not power of 2 */
254        return RTEMS_INVALID_NUMBER;
255
256    rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
257    if (rc != RTEMS_SUCCESSFUL)
258        return rc;
259    diskdevs_protected = TRUE;
260
261    rc = rtems_bdbuf_find_pool(block_size, &pool);
262    if (rc != RTEMS_SUCCESSFUL)
263    {
264        diskdevs_protected = FALSE;
265        rtems_semaphore_release(diskdevs_mutex);
266        return rc;
267    }
268
269    rc = create_disk(dev, name, &dd);
270    if (rc != RTEMS_SUCCESSFUL)
271    {
272        diskdevs_protected = FALSE;
273        rtems_semaphore_release(diskdevs_mutex);
274        return rc;
275    }
276
277    dd->phys_dev = dd;
278    dd->uses = 0;
279    dd->start = 0;
280    dd->size = disk_size;
281    dd->block_size = block_size;
282    dd->block_size_log2 = bs_log2;
283    dd->ioctl = handler;
284    dd->pool = pool;
285
286    rc = rtems_io_register_name(name, major, minor);
287
288    if (handler (dd->phys_dev->dev,
289                 RTEMS_BLKDEV_CAPABILITIES,
290                 &dd->capabilities) < 0)
291      dd->capabilities = 0;
292   
293    diskdevs_protected = FALSE;
294    rtems_semaphore_release(diskdevs_mutex);
295
296    return rc;
297}
298
299/* rtems_disk_create_log --
300 *     Create logical disk entry. Logical disk is contiguous area on physical
301 *     disk. Disk may be splitted to several logical disks in several ways:
302 *     manually or using information stored in blocks on physical disk
303 *     (DOS-like partition table, BSD disk label, etc). This function usually
304 *     invoked from application when application-specific splitting are in use,
305 *     or from generic code which handle different logical disk organizations.
306 *     This primitive will register device in rtems (invoke
307 *     rtems_io_register_name).
308 *
309 * PARAMETERS:
310 *     dev   - logical device identifier (major, minor numbers)
311 *     phys  - physical device (block device which holds this logical disk)
312 *             identifier
313 *     start - starting block number on the physical device
314 *     size  - logical disk size in blocks
315 *     name  - logical disk name
316 *
317 * RETURNS:
318 *     RTEMS_SUCCESSFUL if logical device successfully added, or error code
319 *     if error occured (device already registered, no physical device
320 *     exists, logical disk is out of physical disk boundaries, no memory
321 *     available).
322 */
323rtems_status_code
324rtems_disk_create_log(dev_t dev, dev_t phys, int start, int size, char *name)
325{
326    rtems_disk_device *dd;
327    rtems_disk_device *pdd;
328    rtems_status_code rc;
329    rtems_device_major_number major;
330    rtems_device_minor_number minor;
331
332    rtems_filesystem_split_dev_t (dev, major, minor);
333
334    rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
335    if (rc != RTEMS_SUCCESSFUL)
336        return rc;
337    diskdevs_protected = TRUE;
338
339    pdd = get_disk_entry(phys);
340    if (pdd == NULL)
341    {
342        diskdevs_protected = FALSE;
343        rtems_semaphore_release(diskdevs_mutex);
344        return RTEMS_INVALID_NUMBER;
345    }
346
347    rc = create_disk(dev, name, &dd);
348    if (rc != RTEMS_SUCCESSFUL)
349    {
350        diskdevs_protected = FALSE;
351        rtems_semaphore_release(diskdevs_mutex);
352        return rc;
353    }
354
355    dd->phys_dev = pdd;
356    dd->uses = 0;
357    dd->start = start;
358    dd->size = size;
359    dd->block_size = pdd->block_size;
360    dd->block_size_log2 = pdd->block_size_log2;
361    dd->ioctl = pdd->ioctl;
362
363    rc = rtems_io_register_name(name, major, minor);
364
365    diskdevs_protected = FALSE;
366    rc = rtems_semaphore_release(diskdevs_mutex);
367
368    return rc;
369}
370
371/* rtems_disk_delete --
372 *     Delete physical or logical disk device. Device may be deleted if its
373 *     use counter (and use counters of all logical devices - if it is
374 *     physical device) equal to 0. When physical device deleted,
375 *     all logical devices deleted inherently. Appropriate devices removed
376 *     from "/dev" filesystem.
377 *
378 * PARAMETERS:
379 *     dev - device identifier (major, minor numbers)
380 *
381 * RETURNS:
382 *     RTEMS_SUCCESSFUL if block device successfully deleted, or error code
383 *     if error occured (device is not defined, device is in use).
384 */
385rtems_status_code
386rtems_disk_delete(dev_t dev)
387{
388    rtems_status_code rc;
389    int used;
390    rtems_device_major_number maj;
391    rtems_device_minor_number min;
392
393    rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
394    if (rc != RTEMS_SUCCESSFUL)
395        return rc;
396    diskdevs_protected = TRUE;
397
398    /* Check if this device is in use -- calculate usage counter */
399    used = 0;
400    for (maj = 0; maj < disktab_size; maj++)
401    {
402        rtems_disk_device_table *dtab = disktab + maj;
403        if (dtab != NULL)
404        {
405            for (min = 0; min < dtab->size; min++)
406            {
407                rtems_disk_device *dd = dtab->minor[min];
408                if ((dd != NULL) && (dd->phys_dev->dev == dev))
409                    used += dd->uses;
410            }
411        }
412    }
413
414    if (used != 0)
415    {
416        diskdevs_protected = FALSE;
417        rtems_semaphore_release(diskdevs_mutex);
418        return RTEMS_RESOURCE_IN_USE;
419    }
420
421    /* Delete this device and all of its logical devices */
422    for (maj = 0; maj < disktab_size; maj++)
423    {
424        rtems_disk_device_table *dtab = disktab +maj;
425        if (dtab != NULL)
426        {
427            for (min = 0; min < dtab->size; min++)
428            {
429                rtems_disk_device *dd = dtab->minor[min];
430                if ((dd != NULL) && (dd->phys_dev->dev == dev))
431                {
432                    unlink(dd->name);
433                    free(dd->name);
434                    free(dd);
435                    dtab->minor[min] = NULL;
436                }
437            }
438        }
439    }
440
441    diskdevs_protected = FALSE;
442    rc = rtems_semaphore_release(diskdevs_mutex);
443    return rc;
444}
445
446/* rtems_disk_obtain --
447 *     Find block device descriptor by its device identifier.
448 *
449 * PARAMETERS:
450 *     dev - device identifier (major, minor numbers)
451 *
452 * RETURNS:
453 *     pointer to the block device descriptor, or NULL if no such device
454 *     exists.
455 */
456rtems_disk_device *
457rtems_disk_obtain(dev_t dev)
458{
459    rtems_interrupt_level level;
460    rtems_disk_device *dd;
461    rtems_status_code rc;
462
463    rtems_interrupt_disable(level);
464    if (diskdevs_protected)
465    {
466        rtems_interrupt_enable(level);
467        rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT,
468                                    RTEMS_NO_TIMEOUT);
469        if (rc != RTEMS_SUCCESSFUL)
470            return NULL;
471        diskdevs_protected = TRUE;
472        dd = get_disk_entry(dev);
473        dd->uses++;
474        diskdevs_protected = FALSE;
475        rtems_semaphore_release(diskdevs_mutex);
476        return dd;
477    }
478    else
479    {
480        /* Frequent and quickest case */
481        dd = get_disk_entry(dev);
482        dd->uses++;
483        rtems_interrupt_enable(level);
484        return dd;
485    }
486}
487
488/* rtems_disk_release --
489 *     Release rtems_disk_device structure (decrement usage counter to 1).
490 *
491 * PARAMETERS:
492 *     dd - pointer to disk device structure
493 *
494 * RETURNS:
495 *     RTEMS_SUCCESSFUL
496 */
497rtems_status_code
498rtems_disk_release(rtems_disk_device *dd)
499{
500    rtems_interrupt_level level;
501    rtems_interrupt_disable(level);
502    dd->uses--;
503    rtems_interrupt_enable(level);
504    return RTEMS_SUCCESSFUL;
505}
506
507/* rtems_disk_next --
508 *     Disk device enumerator. Looking for device having device number larger
509 *     than dev and return disk device descriptor for it. If there are no
510 *     such device, NULL value returned.
511 *
512 * PARAMETERS:
513 *     dev - device number (use -1 to start search)
514 *
515 * RETURNS:
516 *     Pointer to the disk descriptor for next disk device, or NULL if all
517 *     devices enumerated.
518 */
519rtems_disk_device *
520rtems_disk_next(dev_t dev)
521{
522    rtems_device_major_number major;
523    rtems_device_minor_number minor;
524    rtems_disk_device_table *dtab;
525
526    dev++;
527    rtems_filesystem_split_dev_t (dev, major, minor);
528
529    if (major >= disktab_size)
530        return NULL;
531
532    dtab = disktab + major;
533    while (TRUE)
534    {
535        if ((dtab == NULL) || (minor > dtab->size))
536        {
537             major++; minor = 0;
538             if (major >= disktab_size)
539                 return NULL;
540             dtab = disktab + major;
541        }
542        else if (dtab->minor[minor] == NULL)
543        {
544            minor++;
545        }
546        else
547            return dtab->minor[minor];
548    }
549}
550
551/* rtems_disk_initialize --
552 *     Initialization of disk device library (initialize all data structures,
553 *     etc.)
554 *
555 * PARAMETERS:
556 *     none
557 *
558 * RETURNS:
559 *     RTEMS_SUCCESSFUL if library initialized, or error code if error
560 *     occured.
561 */
562rtems_status_code
563rtems_disk_io_initialize(void)
564{
565    rtems_status_code rc;
566
567    if (disk_io_initialized)
568        return RTEMS_SUCCESSFUL;
569
570    disktab_size = DISKTAB_INITIAL_SIZE;
571    disktab = calloc(disktab_size, sizeof(rtems_disk_device_table));
572    if (disktab == NULL)
573        return RTEMS_NO_MEMORY;
574
575    diskdevs_protected = FALSE;
576    rc = rtems_semaphore_create(
577        rtems_build_name('D', 'D', 'E', 'V'), 1,
578        RTEMS_FIFO | RTEMS_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
579        RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, &diskdevs_mutex);
580
581    if (rc != RTEMS_SUCCESSFUL)
582    {
583        free(disktab);
584        return rc;
585    }
586
587    rc = rtems_bdbuf_init();
588
589    if (rc != RTEMS_SUCCESSFUL)
590    {
591        rtems_semaphore_delete(diskdevs_mutex);
592        free(disktab);
593        return rc;
594    }
595
596    disk_io_initialized = 1;
597    return RTEMS_SUCCESSFUL;
598}
599
600/* rtems_disk_io_done --
601 *     Release all resources allocated for disk device interface.
602 *
603 * PARAMETERS:
604 *     none
605 *
606 * RETURNS:
607 *     RTEMS_SUCCESSFUL if all resources released, or error code if error
608 *     occured.
609 */
610rtems_status_code
611rtems_disk_io_done(void)
612{
613    rtems_device_major_number maj;
614    rtems_device_minor_number min;
615    rtems_status_code rc;
616
617    /* Free data structures */
618    for (maj = 0; maj < disktab_size; maj++)
619    {
620        rtems_disk_device_table *dtab = disktab + maj;
621        if (dtab != NULL)
622        {
623            for (min = 0; min < dtab->size; min++)
624            {
625                rtems_disk_device *dd = dtab->minor[min];
626                unlink(dd->name);
627                free(dd->name);
628                free(dd);
629            }
630            free(dtab);
631        }
632    }
633    free(disktab);
634
635    rc = rtems_semaphore_delete(diskdevs_mutex);
636
637    /* XXX bdbuf should be released too! */
638    disk_io_initialized = 0;
639    return rc;
640}
Note: See TracBrowser for help on using the repository browser.