source: rtems/cpukit/libblock/src/diskdevs.c @ 73c09b3b

4.115
Last change on this file since 73c09b3b was 73c09b3b, checked in by Sebastian Huber <sebastian.huber@…>, on 05/30/12 at 11:40:34

libblock: Simplify disk management

Add block_count and media_blocks_per_block to rtems_disk_device. Add
and use rtems_disk_init_phys() and rtems_disk_init_log().

  • Property mode set to 100644
File size: 12.4 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_status_code
61disk_lock(void)
62{
63  rtems_status_code sc = RTEMS_SUCCESSFUL;
64
65  sc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
66  if (sc == RTEMS_SUCCESSFUL) {
67    diskdevs_protected = true;
68
69    return RTEMS_SUCCESSFUL;
70  } else {
71    return RTEMS_NOT_CONFIGURED;
72  }
73}
74
75static void
76disk_unlock(void)
77{
78  rtems_status_code sc = RTEMS_SUCCESSFUL;
79
80  diskdevs_protected = false;
81
82  sc = rtems_semaphore_release(diskdevs_mutex);
83  if (sc != RTEMS_SUCCESSFUL) {
84    /* FIXME: Error number */
85    rtems_fatal_error_occurred(0xdeadbeef);
86  }
87}
88
89static rtems_disk_device *
90get_disk_entry(dev_t dev, bool lookup_only)
91{
92  rtems_device_major_number major = 0;
93  rtems_device_minor_number minor = 0;
94
95  rtems_filesystem_split_dev_t(dev, major, minor);
96
97  if (major < disktab_size && disktab != NULL) {
98    rtems_disk_device_table *dtab = disktab + major;
99
100    if (minor < dtab->size && dtab->minor != NULL) {
101      rtems_disk_device *dd = dtab->minor [minor];
102
103      if (dd != NULL && !lookup_only) {
104        if (!dd->deleted) {
105          ++dd->uses;
106        } else {
107          dd = NULL;
108        }
109      }
110
111      return dd;
112    }
113  }
114
115  return NULL;
116}
117
118static rtems_disk_device **
119create_disk_table_entry(dev_t dev)
120{
121  rtems_device_major_number major = 0;
122  rtems_device_minor_number minor = 0;
123
124  rtems_filesystem_split_dev_t(dev, major, minor);
125
126  if (major >= disktab_size) {
127    rtems_disk_device_table *table = disktab;
128    rtems_device_major_number old_size = disktab_size;
129    rtems_device_major_number new_size = 2 * old_size;
130
131    if (major >= new_size) {
132      new_size = major + 1;
133    }
134
135    table = realloc(table, new_size * sizeof(*table));
136    if (table == NULL) {
137      return NULL;
138    }
139
140    memset(table + old_size, 0, (new_size - old_size) * sizeof(*table));
141    disktab = table;
142    disktab_size = new_size;
143  }
144
145  if (disktab [major].minor == NULL || minor >= disktab[major].size) {
146    rtems_disk_device **table = disktab [major].minor;
147    rtems_device_minor_number old_size = disktab [major].size;
148    rtems_device_minor_number new_size = 0;
149
150    if (old_size == 0) {
151      new_size = DISKTAB_INITIAL_SIZE;
152    } else {
153      new_size = 2 * old_size;
154    }
155    if (minor >= new_size) {
156      new_size = minor + 1;
157    }
158
159    table = realloc(table, new_size * sizeof(*table));
160    if (table == NULL) {
161      return NULL;
162    }
163
164    memset(table + old_size, 0, (new_size - old_size) * sizeof(*table));
165    disktab [major].minor = table;
166    disktab [major].size = new_size;
167  }
168
169  return disktab [major].minor + minor;
170}
171
172static rtems_status_code
173create_disk(
174  dev_t dev,
175  const char *name,
176  rtems_disk_device **dd_ptr,
177  char **alloc_name_ptr
178)
179{
180  rtems_disk_device **dd_entry = create_disk_table_entry(dev);
181  rtems_disk_device *dd = NULL;
182  char *alloc_name = NULL;
183
184  if (dd_entry == NULL) {
185    return RTEMS_NO_MEMORY;
186  }
187
188  if (*dd_entry != NULL) {
189    return RTEMS_RESOURCE_IN_USE;
190  }
191
192  dd = malloc(sizeof(*dd));
193  if (dd == NULL) {
194    return RTEMS_NO_MEMORY;
195  }
196
197  if (name != NULL) {
198    alloc_name = strdup(name);
199
200    if (alloc_name == NULL) {
201      free(dd);
202
203      return RTEMS_NO_MEMORY;
204    }
205  }
206
207  if (name != NULL) {
208    if (mknod(alloc_name, 0777 | S_IFBLK, dev) < 0) {
209      free(alloc_name);
210      free(dd);
211      return RTEMS_UNSATISFIED;
212    }
213  }
214
215  *dd_entry = dd;
216  *dd_ptr = dd;
217  *alloc_name_ptr = alloc_name;
218
219  return RTEMS_SUCCESSFUL;
220}
221
222static int null_handler(
223  rtems_disk_device *dd,
224  uint32_t req,
225  void *argp
226)
227{
228  return -1;
229}
230
231rtems_status_code rtems_disk_create_phys(
232  dev_t dev,
233  uint32_t block_size,
234  rtems_blkdev_bnum block_count,
235  rtems_block_device_ioctl handler,
236  void *driver_data,
237  const char *name
238)
239{
240  rtems_disk_device *dd = NULL;
241  rtems_status_code sc = RTEMS_SUCCESSFUL;
242  char *alloc_name = NULL;
243
244  if (handler == NULL) {
245    return RTEMS_INVALID_ADDRESS;
246  }
247
248  sc = disk_lock();
249  if (sc != RTEMS_SUCCESSFUL) {
250    return sc;
251  }
252
253  sc = create_disk(dev, name, &dd, &alloc_name);
254  if (sc != RTEMS_SUCCESSFUL) {
255    disk_unlock();
256
257    return sc;
258  }
259
260  sc = rtems_disk_init_phys(
261    dd,
262    block_size,
263    block_count,
264    handler,
265    driver_data
266  );
267
268  dd->dev = dev;
269  dd->name = alloc_name;
270
271  if (sc != RTEMS_SUCCESSFUL) {
272    dd->ioctl = null_handler;
273    rtems_disk_delete(dev);
274    disk_unlock();
275
276    return sc;
277  }
278
279  disk_unlock();
280
281  return RTEMS_SUCCESSFUL;
282}
283
284static bool
285is_physical_disk(const rtems_disk_device *dd)
286{
287  return dd->phys_dev == dd;
288}
289
290rtems_status_code rtems_disk_create_log(
291  dev_t dev,
292  dev_t phys,
293  rtems_blkdev_bnum block_begin,
294  rtems_blkdev_bnum block_count,
295  const char *name
296)
297{
298  rtems_status_code sc = RTEMS_SUCCESSFUL;
299  rtems_disk_device *phys_dd = NULL;
300  rtems_disk_device *dd = NULL;
301  char *alloc_name = NULL;
302
303  sc = disk_lock();
304  if (sc != RTEMS_SUCCESSFUL) {
305    return sc;
306  }
307
308  phys_dd = get_disk_entry(phys, true);
309  if (phys_dd == NULL) {
310    disk_unlock();
311
312    return RTEMS_INVALID_ID;
313  }
314
315  sc = create_disk(dev, name, &dd, &alloc_name);
316  if (sc != RTEMS_SUCCESSFUL) {
317    disk_unlock();
318
319    return sc;
320  }
321
322  sc = rtems_disk_init_log(
323    dd,
324    phys_dd,
325    block_begin,
326    block_count
327  );
328
329  dd->dev = dev;
330  dd->name = alloc_name;
331
332  ++phys_dd->uses;
333
334  if (sc != RTEMS_SUCCESSFUL) {
335    dd->ioctl = null_handler;
336    rtems_disk_delete(dev);
337    disk_unlock();
338
339    return sc;
340  }
341
342  disk_unlock();
343
344  return RTEMS_SUCCESSFUL;
345}
346
347static void
348free_disk_device(rtems_disk_device *dd)
349{
350  if (is_physical_disk(dd)) {
351    (*dd->ioctl)(dd, RTEMS_BLKIO_DELETED, NULL);
352  }
353  if (dd->name != NULL) {
354    unlink(dd->name);
355    free(dd->name);
356  }
357  free(dd);
358}
359
360static void
361rtems_disk_cleanup(rtems_disk_device *disk_to_remove)
362{
363  rtems_disk_device *const physical_disk = disk_to_remove->phys_dev;
364  rtems_device_major_number major = 0;
365  rtems_device_minor_number minor = 0;
366
367  if (physical_disk->deleted) {
368    dev_t dev = physical_disk->dev;
369    unsigned deleted_count = 0;
370
371    for (major = 0; major < disktab_size; ++major) {
372      rtems_disk_device_table *dtab = disktab + major;
373
374      for (minor = 0; minor < dtab->size; ++minor) {
375        rtems_disk_device *dd = dtab->minor [minor];
376
377        if (dd != NULL && dd->phys_dev->dev == dev && dd != physical_disk) {
378          if (dd->uses == 0) {
379            ++deleted_count;
380            dtab->minor [minor] = NULL;
381            free_disk_device(dd);
382          } else {
383            dd->deleted = true;
384          }
385        }
386      }
387    }
388
389    physical_disk->uses -= deleted_count;
390    if (physical_disk->uses == 0) {
391      rtems_filesystem_split_dev_t(physical_disk->dev, major, minor);
392      disktab [major].minor [minor] = NULL;
393      free_disk_device(physical_disk);
394    }
395  } else {
396    if (disk_to_remove->uses == 0) {
397      --physical_disk->uses;
398      rtems_filesystem_split_dev_t(disk_to_remove->dev, major, minor);
399      disktab [major].minor [minor] = NULL;
400      free_disk_device(disk_to_remove);
401    }
402  }
403}
404
405rtems_status_code
406rtems_disk_delete(dev_t dev)
407{
408  rtems_status_code sc = RTEMS_SUCCESSFUL;
409  rtems_disk_device *dd = NULL;
410
411  sc = disk_lock();
412  if (sc != RTEMS_SUCCESSFUL) {
413    return sc;
414  }
415
416  dd = get_disk_entry(dev, true);
417  if (dd == NULL) {
418    disk_unlock();
419
420    return RTEMS_INVALID_ID;
421  }
422
423  dd->deleted = true;
424  rtems_disk_cleanup(dd);
425
426  disk_unlock();
427
428  return RTEMS_SUCCESSFUL;
429}
430
431rtems_disk_device *
432rtems_disk_obtain(dev_t dev)
433{
434  rtems_status_code sc = RTEMS_SUCCESSFUL;
435  rtems_disk_device *dd = NULL;
436  rtems_interrupt_level level;
437
438  rtems_interrupt_disable(level);
439  if (!diskdevs_protected) {
440    /* Frequent and quickest case */
441    dd = get_disk_entry(dev, false);
442    rtems_interrupt_enable(level);
443  } else {
444    rtems_interrupt_enable(level);
445
446    sc = disk_lock();
447    if (sc == RTEMS_SUCCESSFUL) {
448      dd = get_disk_entry(dev, false);
449      disk_unlock();
450    }
451  }
452
453  return dd;
454}
455
456rtems_status_code
457rtems_disk_release(rtems_disk_device *dd)
458{
459  rtems_interrupt_level level;
460  dev_t dev = dd->dev;
461  unsigned uses = 0;
462  bool deleted = false;
463
464  rtems_interrupt_disable(level);
465  uses = --dd->uses;
466  deleted = dd->deleted;
467  rtems_interrupt_enable(level);
468
469  if (uses == 0 && deleted) {
470    rtems_disk_delete(dev);
471  }
472
473  return RTEMS_SUCCESSFUL;
474}
475
476rtems_disk_device *
477rtems_disk_next(dev_t dev)
478{
479  rtems_status_code sc = RTEMS_SUCCESSFUL;
480  rtems_disk_device_table *dtab = NULL;
481  rtems_device_major_number major = 0;
482  rtems_device_minor_number minor = 0;
483
484  if (dev != (dev_t) -1) {
485    rtems_filesystem_split_dev_t(dev, major, minor);
486
487    /* If minor wraps around */
488    if ((minor + 1) < minor) {
489      /* If major wraps around */
490      if ((major + 1) < major) {
491        return NULL;
492      }
493      ++major;
494      minor = 0;
495    } else {
496      ++minor;
497    }
498  }
499
500  sc = disk_lock();
501  if (sc != RTEMS_SUCCESSFUL) {
502    return NULL;
503  }
504
505  if (major >= disktab_size) {
506    disk_unlock();
507
508    return NULL;
509  }
510
511  dtab = disktab + major;
512  while (true) {
513    if (dtab->minor == NULL || minor >= dtab->size) {
514       minor = 0;
515       ++major;
516       if (major >= disktab_size) {
517         disk_unlock();
518
519         return NULL;
520       }
521       dtab = disktab + major;
522    } else if (dtab->minor [minor] == NULL) {
523      ++minor;
524    } else {
525      ++dtab->minor [minor]->uses;
526      disk_unlock();
527
528      return dtab->minor [minor];
529    }
530  }
531}
532
533rtems_status_code
534rtems_disk_io_initialize(void)
535{
536  rtems_status_code sc = RTEMS_SUCCESSFUL;
537  rtems_device_major_number size = DISKTAB_INITIAL_SIZE;
538
539  if (disktab_size > 0) {
540    return RTEMS_SUCCESSFUL;
541  }
542
543  disktab = calloc(size, sizeof(rtems_disk_device_table));
544  if (disktab == NULL) {
545    return RTEMS_NO_MEMORY;
546  }
547
548  diskdevs_protected = false;
549  sc = rtems_semaphore_create(
550    rtems_build_name('D', 'D', 'E', 'V'),
551    1,
552    RTEMS_FIFO | RTEMS_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY
553      | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
554    0,
555    &diskdevs_mutex
556  );
557  if (sc != RTEMS_SUCCESSFUL) {
558    free(disktab);
559
560    return RTEMS_NO_MEMORY;
561  }
562
563  sc = rtems_bdbuf_init();
564  if (sc != RTEMS_SUCCESSFUL) {
565    rtems_semaphore_delete(diskdevs_mutex);
566    free(disktab);
567
568    return RTEMS_UNSATISFIED;
569  }
570
571  disktab_size = size;
572
573  return RTEMS_SUCCESSFUL;
574}
575
576rtems_status_code
577rtems_disk_io_done(void)
578{
579  rtems_device_major_number major = 0;
580  rtems_device_minor_number minor = 0;
581
582  for (major = 0; major < disktab_size; ++major) {
583    rtems_disk_device_table *dtab = disktab + major;
584
585    for (minor = 0; minor < dtab->size; ++minor) {
586      rtems_disk_device *dd = dtab->minor [minor];
587
588      if (dd != NULL) {
589        free_disk_device(dd);
590      }
591    }
592    free(dtab->minor);
593  }
594  free(disktab);
595
596  rtems_semaphore_delete(diskdevs_mutex);
597
598  diskdevs_mutex = RTEMS_ID_NONE;
599  disktab = NULL;
600  disktab_size = 0;
601
602  return RTEMS_SUCCESSFUL;
603}
Note: See TracBrowser for help on using the repository browser.