source: rtems/cpukit/libblock/src/diskdevs.c @ 9de9b7d2

4.115
Last change on this file since 9de9b7d2 was 9de9b7d2, checked in by Sebastian Huber <sebastian.huber@…>, on 08/14/13 at 09:06:02

libblock: Add SMP support

  • Property mode set to 100644
File size: 12.5 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup rtems_disk
5 *
6 * @brief Block device disk management implementation.
7 */
8
9/*
10 * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
11 * Author: Victor V. Vengerov <vvv@oktet.ru>
12 *
13 * Copyright (c) 2009-2012 embedded brains GmbH.
14 */
15
16#if HAVE_CONFIG_H
17#include "config.h"
18#endif
19
20#include <stdlib.h>
21#include <unistd.h>
22#include <string.h>
23
24#include <rtems.h>
25#include <rtems/libio.h>
26#include <rtems/diskdevs.h>
27#include <rtems/blkdev.h>
28#include <rtems/bdbuf.h>
29
30#define DISKTAB_INITIAL_SIZE 8
31
32/* Table of disk devices having the same major number */
33typedef struct rtems_disk_device_table {
34  rtems_disk_device **minor; /* minor-indexed disk device table */
35  rtems_device_minor_number size; /* Number of entries in the table */
36} rtems_disk_device_table;
37
38/* Pointer to [major].minor[minor] indexed array of disk devices */
39static rtems_disk_device_table *disktab;
40
41/* Number of allocated entries in disktab table */
42static rtems_device_major_number disktab_size;
43
44/* Mutual exclusion semaphore for disk devices table */
45static rtems_id diskdevs_mutex;
46
47/* diskdevs data structures protection flag.
48 * Normally, only table lookup operations performed. It is quite fast, so
49 * it is possible to done lookups when interrupts are disabled, avoiding
50 * obtaining the semaphore. This flags sets immediately after entering in
51 * mutex-protected section and cleared before leaving this section in
52 * "big" primitives like add/delete new device etc. Lookup function first
53 * disable interrupts and check this flag. If it is set, lookup function
54 * will be blocked on semaphore and lookup operation will be performed in
55 * semaphore-protected code. If it is not set (very-very frequent case),
56 * we can do lookup safely, enable interrupts and return result.
57 */
58static volatile bool diskdevs_protected;
59
60static rtems_interrupt_lock diskdevs_lock;
61
62static rtems_status_code
63disk_lock(void)
64{
65  rtems_status_code sc = RTEMS_SUCCESSFUL;
66
67  sc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
68  if (sc == RTEMS_SUCCESSFUL) {
69    diskdevs_protected = true;
70
71    return RTEMS_SUCCESSFUL;
72  } else {
73    return RTEMS_NOT_CONFIGURED;
74  }
75}
76
77static void
78disk_unlock(void)
79{
80  rtems_status_code sc = RTEMS_SUCCESSFUL;
81
82  diskdevs_protected = false;
83
84  sc = rtems_semaphore_release(diskdevs_mutex);
85  if (sc != RTEMS_SUCCESSFUL) {
86    /* FIXME: Error number */
87    rtems_fatal_error_occurred(0xdeadbeef);
88  }
89}
90
91static rtems_disk_device *
92get_disk_entry(dev_t dev, bool lookup_only)
93{
94  rtems_device_major_number major = 0;
95  rtems_device_minor_number minor = 0;
96
97  rtems_filesystem_split_dev_t(dev, major, minor);
98
99  if (major < disktab_size && disktab != NULL) {
100    rtems_disk_device_table *dtab = disktab + major;
101
102    if (minor < dtab->size && dtab->minor != NULL) {
103      rtems_disk_device *dd = dtab->minor [minor];
104
105      if (dd != NULL && !lookup_only) {
106        if (!dd->deleted) {
107          ++dd->uses;
108        } else {
109          dd = NULL;
110        }
111      }
112
113      return dd;
114    }
115  }
116
117  return NULL;
118}
119
120static rtems_disk_device **
121create_disk_table_entry(dev_t dev)
122{
123  rtems_device_major_number major = 0;
124  rtems_device_minor_number minor = 0;
125
126  rtems_filesystem_split_dev_t(dev, major, minor);
127
128  if (major >= disktab_size) {
129    rtems_disk_device_table *table = disktab;
130    rtems_device_major_number old_size = disktab_size;
131    rtems_device_major_number new_size = 2 * old_size;
132
133    if (major >= new_size) {
134      new_size = major + 1;
135    }
136
137    table = realloc(table, new_size * sizeof(*table));
138    if (table == NULL) {
139      return NULL;
140    }
141
142    memset(table + old_size, 0, (new_size - old_size) * sizeof(*table));
143    disktab = table;
144    disktab_size = new_size;
145  }
146
147  if (disktab [major].minor == NULL || minor >= disktab[major].size) {
148    rtems_disk_device **table = disktab [major].minor;
149    rtems_device_minor_number old_size = disktab [major].size;
150    rtems_device_minor_number new_size = 0;
151
152    if (old_size == 0) {
153      new_size = DISKTAB_INITIAL_SIZE;
154    } else {
155      new_size = 2 * old_size;
156    }
157    if (minor >= new_size) {
158      new_size = minor + 1;
159    }
160
161    table = realloc(table, new_size * sizeof(*table));
162    if (table == NULL) {
163      return NULL;
164    }
165
166    memset(table + old_size, 0, (new_size - old_size) * sizeof(*table));
167    disktab [major].minor = table;
168    disktab [major].size = new_size;
169  }
170
171  return disktab [major].minor + minor;
172}
173
174static rtems_status_code
175create_disk(
176  dev_t dev,
177  const char *name,
178  rtems_disk_device **dd_ptr,
179  char **alloc_name_ptr
180)
181{
182  rtems_disk_device **dd_entry = create_disk_table_entry(dev);
183  rtems_disk_device *dd = NULL;
184  char *alloc_name = NULL;
185
186  if (dd_entry == NULL) {
187    return RTEMS_NO_MEMORY;
188  }
189
190  if (*dd_entry != NULL) {
191    return RTEMS_RESOURCE_IN_USE;
192  }
193
194  dd = malloc(sizeof(*dd));
195  if (dd == NULL) {
196    return RTEMS_NO_MEMORY;
197  }
198
199  if (name != NULL) {
200    alloc_name = strdup(name);
201
202    if (alloc_name == NULL) {
203      free(dd);
204
205      return RTEMS_NO_MEMORY;
206    }
207  }
208
209  if (name != NULL) {
210    if (mknod(alloc_name, 0777 | S_IFBLK, dev) < 0) {
211      free(alloc_name);
212      free(dd);
213      return RTEMS_UNSATISFIED;
214    }
215  }
216
217  *dd_entry = dd;
218  *dd_ptr = dd;
219  *alloc_name_ptr = alloc_name;
220
221  return RTEMS_SUCCESSFUL;
222}
223
224static int null_handler(
225  rtems_disk_device *dd,
226  uint32_t req,
227  void *argp
228)
229{
230  return -1;
231}
232
233rtems_status_code rtems_disk_create_phys(
234  dev_t dev,
235  uint32_t block_size,
236  rtems_blkdev_bnum block_count,
237  rtems_block_device_ioctl handler,
238  void *driver_data,
239  const char *name
240)
241{
242  rtems_disk_device *dd = NULL;
243  rtems_status_code sc = RTEMS_SUCCESSFUL;
244  char *alloc_name = NULL;
245
246  if (handler == NULL) {
247    return RTEMS_INVALID_ADDRESS;
248  }
249
250  sc = disk_lock();
251  if (sc != RTEMS_SUCCESSFUL) {
252    return sc;
253  }
254
255  sc = create_disk(dev, name, &dd, &alloc_name);
256  if (sc != RTEMS_SUCCESSFUL) {
257    disk_unlock();
258
259    return sc;
260  }
261
262  sc = rtems_disk_init_phys(
263    dd,
264    block_size,
265    block_count,
266    handler,
267    driver_data
268  );
269
270  dd->dev = dev;
271  dd->name = alloc_name;
272
273  if (sc != RTEMS_SUCCESSFUL) {
274    dd->ioctl = null_handler;
275    rtems_disk_delete(dev);
276    disk_unlock();
277
278    return sc;
279  }
280
281  disk_unlock();
282
283  return RTEMS_SUCCESSFUL;
284}
285
286static bool
287is_physical_disk(const rtems_disk_device *dd)
288{
289  return dd->phys_dev == dd;
290}
291
292rtems_status_code rtems_disk_create_log(
293  dev_t dev,
294  dev_t phys,
295  rtems_blkdev_bnum block_begin,
296  rtems_blkdev_bnum block_count,
297  const char *name
298)
299{
300  rtems_status_code sc = RTEMS_SUCCESSFUL;
301  rtems_disk_device *phys_dd = NULL;
302  rtems_disk_device *dd = NULL;
303  char *alloc_name = NULL;
304
305  sc = disk_lock();
306  if (sc != RTEMS_SUCCESSFUL) {
307    return sc;
308  }
309
310  phys_dd = get_disk_entry(phys, true);
311  if (phys_dd == NULL) {
312    disk_unlock();
313
314    return RTEMS_INVALID_ID;
315  }
316
317  sc = create_disk(dev, name, &dd, &alloc_name);
318  if (sc != RTEMS_SUCCESSFUL) {
319    disk_unlock();
320
321    return sc;
322  }
323
324  sc = rtems_disk_init_log(
325    dd,
326    phys_dd,
327    block_begin,
328    block_count
329  );
330
331  dd->dev = dev;
332  dd->name = alloc_name;
333
334  ++phys_dd->uses;
335
336  if (sc != RTEMS_SUCCESSFUL) {
337    dd->ioctl = null_handler;
338    rtems_disk_delete(dev);
339    disk_unlock();
340
341    return sc;
342  }
343
344  disk_unlock();
345
346  return RTEMS_SUCCESSFUL;
347}
348
349static void
350free_disk_device(rtems_disk_device *dd)
351{
352  if (is_physical_disk(dd)) {
353    (*dd->ioctl)(dd, RTEMS_BLKIO_DELETED, NULL);
354  }
355  if (dd->name != NULL) {
356    unlink(dd->name);
357    free(dd->name);
358  }
359  free(dd);
360}
361
362static void
363rtems_disk_cleanup(rtems_disk_device *disk_to_remove)
364{
365  rtems_disk_device *const physical_disk = disk_to_remove->phys_dev;
366  rtems_device_major_number major = 0;
367  rtems_device_minor_number minor = 0;
368
369  if (physical_disk->deleted) {
370    dev_t dev = physical_disk->dev;
371    unsigned deleted_count = 0;
372
373    for (major = 0; major < disktab_size; ++major) {
374      rtems_disk_device_table *dtab = disktab + major;
375
376      for (minor = 0; minor < dtab->size; ++minor) {
377        rtems_disk_device *dd = dtab->minor [minor];
378
379        if (dd != NULL && dd->phys_dev->dev == dev && dd != physical_disk) {
380          if (dd->uses == 0) {
381            ++deleted_count;
382            dtab->minor [minor] = NULL;
383            free_disk_device(dd);
384          } else {
385            dd->deleted = true;
386          }
387        }
388      }
389    }
390
391    physical_disk->uses -= deleted_count;
392    if (physical_disk->uses == 0) {
393      rtems_filesystem_split_dev_t(physical_disk->dev, major, minor);
394      disktab [major].minor [minor] = NULL;
395      free_disk_device(physical_disk);
396    }
397  } else {
398    if (disk_to_remove->uses == 0) {
399      --physical_disk->uses;
400      rtems_filesystem_split_dev_t(disk_to_remove->dev, major, minor);
401      disktab [major].minor [minor] = NULL;
402      free_disk_device(disk_to_remove);
403    }
404  }
405}
406
407rtems_status_code
408rtems_disk_delete(dev_t dev)
409{
410  rtems_status_code sc = RTEMS_SUCCESSFUL;
411  rtems_disk_device *dd = NULL;
412
413  sc = disk_lock();
414  if (sc != RTEMS_SUCCESSFUL) {
415    return sc;
416  }
417
418  dd = get_disk_entry(dev, true);
419  if (dd == NULL) {
420    disk_unlock();
421
422    return RTEMS_INVALID_ID;
423  }
424
425  dd->deleted = true;
426  rtems_disk_cleanup(dd);
427
428  disk_unlock();
429
430  return RTEMS_SUCCESSFUL;
431}
432
433rtems_disk_device *
434rtems_disk_obtain(dev_t dev)
435{
436  rtems_status_code sc = RTEMS_SUCCESSFUL;
437  rtems_disk_device *dd = NULL;
438  rtems_interrupt_level level;
439
440  rtems_interrupt_lock_acquire(&diskdevs_lock, level);
441  if (!diskdevs_protected) {
442    /* Frequent and quickest case */
443    dd = get_disk_entry(dev, false);
444    rtems_interrupt_lock_release(&diskdevs_lock, level);
445  } else {
446    rtems_interrupt_lock_release(&diskdevs_lock, level);
447
448    sc = disk_lock();
449    if (sc == RTEMS_SUCCESSFUL) {
450      dd = get_disk_entry(dev, false);
451      disk_unlock();
452    }
453  }
454
455  return dd;
456}
457
458rtems_status_code
459rtems_disk_release(rtems_disk_device *dd)
460{
461  rtems_interrupt_level level;
462  dev_t dev = dd->dev;
463  unsigned uses = 0;
464  bool deleted = false;
465
466  rtems_interrupt_lock_acquire(&diskdevs_lock, level);
467  uses = --dd->uses;
468  deleted = dd->deleted;
469  rtems_interrupt_lock_release(&diskdevs_lock, level);
470
471  if (uses == 0 && deleted) {
472    rtems_disk_delete(dev);
473  }
474
475  return RTEMS_SUCCESSFUL;
476}
477
478rtems_disk_device *
479rtems_disk_next(dev_t dev)
480{
481  rtems_status_code sc = RTEMS_SUCCESSFUL;
482  rtems_disk_device_table *dtab = NULL;
483  rtems_device_major_number major = 0;
484  rtems_device_minor_number minor = 0;
485
486  if (dev != (dev_t) -1) {
487    rtems_filesystem_split_dev_t(dev, major, minor);
488
489    /* If minor wraps around */
490    if ((minor + 1) < minor) {
491      /* If major wraps around */
492      if ((major + 1) < major) {
493        return NULL;
494      }
495      ++major;
496      minor = 0;
497    } else {
498      ++minor;
499    }
500  }
501
502  sc = disk_lock();
503  if (sc != RTEMS_SUCCESSFUL) {
504    return NULL;
505  }
506
507  if (major >= disktab_size) {
508    disk_unlock();
509
510    return NULL;
511  }
512
513  dtab = disktab + major;
514  while (true) {
515    if (dtab->minor == NULL || minor >= dtab->size) {
516       minor = 0;
517       ++major;
518       if (major >= disktab_size) {
519         disk_unlock();
520
521         return NULL;
522       }
523       dtab = disktab + major;
524    } else if (dtab->minor [minor] == NULL) {
525      ++minor;
526    } else {
527      ++dtab->minor [minor]->uses;
528      disk_unlock();
529
530      return dtab->minor [minor];
531    }
532  }
533}
534
535rtems_status_code
536rtems_disk_io_initialize(void)
537{
538  rtems_status_code sc = RTEMS_SUCCESSFUL;
539  rtems_device_major_number size = DISKTAB_INITIAL_SIZE;
540
541  if (disktab_size > 0) {
542    return RTEMS_SUCCESSFUL;
543  }
544
545  disktab = calloc(size, sizeof(rtems_disk_device_table));
546  if (disktab == NULL) {
547    return RTEMS_NO_MEMORY;
548  }
549
550  diskdevs_protected = false;
551  sc = rtems_semaphore_create(
552    rtems_build_name('D', 'D', 'E', 'V'),
553    1,
554    RTEMS_FIFO | RTEMS_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY
555      | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
556    0,
557    &diskdevs_mutex
558  );
559  if (sc != RTEMS_SUCCESSFUL) {
560    free(disktab);
561
562    return RTEMS_NO_MEMORY;
563  }
564
565  sc = rtems_bdbuf_init();
566  if (sc != RTEMS_SUCCESSFUL) {
567    rtems_semaphore_delete(diskdevs_mutex);
568    free(disktab);
569
570    return RTEMS_UNSATISFIED;
571  }
572
573  disktab_size = size;
574
575  return RTEMS_SUCCESSFUL;
576}
577
578rtems_status_code
579rtems_disk_io_done(void)
580{
581  rtems_device_major_number major = 0;
582  rtems_device_minor_number minor = 0;
583
584  for (major = 0; major < disktab_size; ++major) {
585    rtems_disk_device_table *dtab = disktab + major;
586
587    for (minor = 0; minor < dtab->size; ++minor) {
588      rtems_disk_device *dd = dtab->minor [minor];
589
590      if (dd != NULL) {
591        free_disk_device(dd);
592      }
593    }
594    free(dtab->minor);
595  }
596  free(disktab);
597
598  rtems_semaphore_delete(diskdevs_mutex);
599
600  diskdevs_mutex = RTEMS_ID_NONE;
601  disktab = NULL;
602  disktab_size = 0;
603
604  return RTEMS_SUCCESSFUL;
605}
Note: See TracBrowser for help on using the repository browser.