source: rtems/cpukit/libblock/src/diskdevs.c @ bdac0eed

4.104.11
Last change on this file since bdac0eed was bdac0eed, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on Apr 9, 2010 at 12:28:24 PM

fixed disk delete
Obtain/release disk during open/close

  • Property mode set to 100644
File size: 12.2 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 embedded brains GmbH.
14 *
15 * @(#) $Id$
16 */
17
18#if HAVE_CONFIG_H
19#include "config.h"
20#endif
21
22#include <stdlib.h>
23#include <unistd.h>
24#include <string.h>
25
26#include <rtems.h>
27#include <rtems/libio.h>
28#include <rtems/diskdevs.h>
29#include <rtems/blkdev.h>
30#include <rtems/bdbuf.h>
31
32#define DISKTAB_INITIAL_SIZE 8
33
34/* Table of disk devices having the same major number */
35typedef struct rtems_disk_device_table {
36  rtems_disk_device **minor; /* minor-indexed disk device table */
37  rtems_device_minor_number size; /* Number of entries in the table */
38} rtems_disk_device_table;
39
40/* Pointer to [major].minor[minor] indexed array of disk devices */
41static rtems_disk_device_table *disktab;
42
43/* Number of allocated entries in disktab table */
44static rtems_device_major_number disktab_size;
45
46/* Mutual exclusion semaphore for disk devices table */
47static rtems_id diskdevs_mutex;
48
49/* diskdevs data structures protection flag.
50 * Normally, only table lookup operations performed. It is quite fast, so
51 * it is possible to done lookups when interrupts are disabled, avoiding
52 * obtaining the semaphore. This flags sets immediately after entering in
53 * mutex-protected section and cleared before leaving this section in
54 * "big" primitives like add/delete new device etc. Lookup function first
55 * disable interrupts and check this flag. If it is set, lookup function
56 * will be blocked on semaphore and lookup operation will be performed in
57 * semaphore-protected code. If it is not set (very-very frequent case),
58 * we can do lookup safely, enable interrupts and return result.
59 */
60static volatile bool diskdevs_protected;
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(dev_t dev, const char *name, rtems_disk_device **dd_ptr)
176{
177  rtems_disk_device **dd_entry = create_disk_table_entry(dev);
178  rtems_disk_device *dd = NULL;
179  char *alloc_name = NULL;
180
181  if (dd_entry == NULL) {
182    return RTEMS_NO_MEMORY;
183  }
184
185  if (*dd_entry != NULL) {
186    return RTEMS_RESOURCE_IN_USE;
187  }
188
189  dd = malloc(sizeof(*dd));
190  if (dd == NULL) {
191    return RTEMS_NO_MEMORY;
192  }
193
194  if (name != NULL) {
195    alloc_name = strdup(name);
196
197    if (alloc_name == NULL) {
198      free(dd);
199
200      return RTEMS_NO_MEMORY;
201    }
202  }
203
204  if (name != NULL) {
205    if (mknod(alloc_name, 0777 | S_IFBLK, dev) < 0) {
206      free(alloc_name);
207      free(dd);
208      return RTEMS_UNSATISFIED;
209    }
210  }
211
212  dd->dev = dev;
213  dd->name = alloc_name;
214  dd->uses = 0;
215  dd->deleted = false;
216
217  *dd_entry = dd;
218  *dd_ptr = dd;
219
220  return RTEMS_SUCCESSFUL;
221}
222
223rtems_status_code rtems_disk_create_phys(
224  dev_t dev,
225  uint32_t block_size,
226  rtems_blkdev_bnum block_count,
227  rtems_block_device_ioctl handler,
228  void *driver_data,
229  const char *name
230)
231{
232  rtems_disk_device *dd = NULL;
233  rtems_status_code sc = RTEMS_SUCCESSFUL;
234
235  if (handler == NULL) {
236    return RTEMS_INVALID_ADDRESS;
237  }
238
239  if (block_size == 0) {
240    return RTEMS_INVALID_NUMBER;
241  }
242
243  sc = disk_lock();
244  if (sc != RTEMS_SUCCESSFUL) {
245    return sc;
246  }
247
248  sc = create_disk(dev, name, &dd);
249  if (sc != RTEMS_SUCCESSFUL) {
250    disk_unlock();
251
252    return sc;
253  }
254
255  dd->phys_dev = dd;
256  dd->start = 0;
257  dd->size = block_count;
258  dd->block_size = dd->media_block_size = block_size;
259  dd->ioctl = handler;
260  dd->driver_data = driver_data;
261
262  if ((*handler)(dd, RTEMS_BLKDEV_CAPABILITIES, &dd->capabilities) < 0) {
263    dd->capabilities = 0;
264  }
265
266  disk_unlock();
267
268  return RTEMS_SUCCESSFUL;
269}
270
271static bool
272is_physical_disk(const rtems_disk_device *dd)
273{
274  return dd->phys_dev == dd;
275}
276
277rtems_status_code rtems_disk_create_log(
278  dev_t dev,
279  dev_t phys,
280  rtems_blkdev_bnum begin_block,
281  rtems_blkdev_bnum block_count,
282  const char *name
283)
284{
285  rtems_status_code sc = RTEMS_SUCCESSFUL;
286  rtems_disk_device *physical_disk = NULL;
287  rtems_disk_device *dd = NULL;
288  rtems_blkdev_bnum end_block = begin_block + block_count;
289
290  sc = disk_lock();
291  if (sc != RTEMS_SUCCESSFUL) {
292    return sc;
293  }
294
295  physical_disk = get_disk_entry(phys, true);
296  if (physical_disk == NULL || !is_physical_disk(physical_disk)) {
297    disk_unlock();
298
299    return RTEMS_INVALID_ID;
300  }
301
302  if (
303    begin_block >= physical_disk->size
304      || end_block <= begin_block
305      || end_block > physical_disk->size
306  ) {
307    disk_unlock();
308
309    return RTEMS_INVALID_NUMBER;
310  }
311
312  sc = create_disk(dev, name, &dd);
313  if (sc != RTEMS_SUCCESSFUL) {
314    disk_unlock();
315
316    return sc;
317  }
318
319  dd->phys_dev = physical_disk;
320  dd->start = begin_block;
321  dd->size = block_count;
322  dd->block_size = dd->media_block_size = physical_disk->block_size;
323  dd->ioctl = physical_disk->ioctl;
324  dd->driver_data = physical_disk->driver_data;
325
326  ++physical_disk->uses;
327
328  disk_unlock();
329
330  return RTEMS_SUCCESSFUL;
331}
332
333static void
334free_disk_device(rtems_disk_device *dd)
335{
336  if (is_physical_disk(dd)) {
337    (*dd->ioctl)(dd, RTEMS_BLKIO_DELETED, NULL);
338  }
339  if (dd->name != NULL) {
340    unlink(dd->name);
341    free(dd->name);
342  }
343  free(dd);
344}
345
346static void
347rtems_disk_cleanup(rtems_disk_device *disk_to_remove)
348{
349  rtems_disk_device *const physical_disk = disk_to_remove->phys_dev;
350  rtems_device_major_number major = 0;
351  rtems_device_minor_number minor = 0;
352
353  if (physical_disk->deleted) {
354    dev_t dev = physical_disk->dev;
355    unsigned deleted_count = 0;
356
357    for (major = 0; major < disktab_size; ++major) {
358      rtems_disk_device_table *dtab = disktab + major;
359
360      for (minor = 0; minor < dtab->size; ++minor) {
361        rtems_disk_device *dd = dtab->minor [minor];
362
363        if (dd != NULL && dd->phys_dev->dev == dev && dd != physical_disk) {
364          if (dd->uses == 0) {
365            ++deleted_count;
366            dtab->minor [minor] = NULL;
367            free_disk_device(dd);
368          } else {
369            dd->deleted = true;
370          }
371        }
372      }
373    }
374
375    physical_disk->uses -= deleted_count;
376    if (physical_disk->uses == 0) {
377      rtems_filesystem_split_dev_t(physical_disk->dev, major, minor);
378      disktab [major].minor [minor] = NULL;
379      free_disk_device(physical_disk);
380    }
381  } else {
382    if (disk_to_remove->uses == 0) {
383      --physical_disk->uses;
384      rtems_filesystem_split_dev_t(disk_to_remove->dev, major, minor);
385      disktab [major].minor [minor] = NULL;
386      free_disk_device(disk_to_remove);
387    }
388  }
389}
390
391rtems_status_code
392rtems_disk_delete(dev_t dev)
393{
394  rtems_status_code sc = RTEMS_SUCCESSFUL;
395  rtems_disk_device *dd = NULL;
396
397  sc = disk_lock();
398  if (sc != RTEMS_SUCCESSFUL) {
399    return sc;
400  }
401
402  dd = get_disk_entry(dev, true);
403  if (dd == NULL) {
404    disk_unlock();
405
406    return RTEMS_INVALID_ID;
407  }
408
409  dd->deleted = true;
410  rtems_disk_cleanup(dd);
411
412  disk_unlock();
413
414  return RTEMS_SUCCESSFUL;
415}
416
417rtems_disk_device *
418rtems_disk_obtain(dev_t dev)
419{
420  rtems_status_code sc = RTEMS_SUCCESSFUL;
421  rtems_disk_device *dd = NULL;
422  rtems_interrupt_level level;
423
424  rtems_interrupt_disable(level);
425  if (!diskdevs_protected) {
426    /* Frequent and quickest case */
427    dd = get_disk_entry(dev, false);
428    rtems_interrupt_enable(level);
429  } else {
430    rtems_interrupt_enable(level);
431
432    sc = disk_lock();
433    if (sc == RTEMS_SUCCESSFUL) {
434      dd = get_disk_entry(dev, false);
435      disk_unlock();
436    }
437  }
438
439  return dd;
440}
441
442rtems_status_code
443rtems_disk_release(rtems_disk_device *dd)
444{
445  rtems_interrupt_level level;
446  dev_t dev = dd->dev;
447  unsigned uses = 0;
448  bool deleted = false;
449
450  rtems_interrupt_disable(level);
451  uses = --dd->uses;
452  deleted = dd->deleted;
453  rtems_interrupt_enable(level);
454
455  if (uses == 0 && deleted) {
456    rtems_disk_delete(dev);
457  }
458
459  return RTEMS_SUCCESSFUL;
460}
461
462rtems_disk_device *
463rtems_disk_next(dev_t dev)
464{
465    rtems_device_major_number major;
466    rtems_device_minor_number minor;
467    rtems_disk_device_table *dtab;
468
469    dev++;
470    rtems_filesystem_split_dev_t (dev, major, minor);
471
472    if (major >= disktab_size)
473        return NULL;
474
475    dtab = disktab + major;
476    while (true)
477    {
478        if ((dtab == NULL) || (minor > dtab->size))
479        {
480             major++; minor = 0;
481             if (major >= disktab_size)
482                 return NULL;
483             dtab = disktab + major;
484        }
485        else if (dtab->minor[minor] == NULL)
486        {
487            minor++;
488        }
489        else
490            return dtab->minor[minor];
491    }
492}
493
494rtems_status_code
495rtems_disk_io_initialize(void)
496{
497  rtems_status_code sc = RTEMS_SUCCESSFUL;
498  rtems_device_major_number size = DISKTAB_INITIAL_SIZE;
499
500  if (disktab_size > 0) {
501    return RTEMS_SUCCESSFUL;
502  }
503
504  disktab = calloc(size, sizeof(rtems_disk_device_table));
505  if (disktab == NULL) {
506    return RTEMS_NO_MEMORY;
507  }
508
509  diskdevs_protected = false;
510  sc = rtems_semaphore_create(
511    rtems_build_name('D', 'D', 'E', 'V'),
512    1,
513    RTEMS_FIFO | RTEMS_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY
514      | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
515    0,
516    &diskdevs_mutex
517  );
518  if (sc != RTEMS_SUCCESSFUL) {
519    free(disktab);
520
521    return RTEMS_NO_MEMORY;
522  }
523
524  sc = rtems_bdbuf_init();
525  if (sc != RTEMS_SUCCESSFUL) {
526    rtems_semaphore_delete(diskdevs_mutex);
527    free(disktab);
528
529    return RTEMS_UNSATISFIED;
530  }
531
532  disktab_size = size;
533
534  return RTEMS_SUCCESSFUL;
535}
536
537rtems_status_code
538rtems_disk_io_done(void)
539{
540  rtems_device_major_number major = 0;
541  rtems_device_minor_number minor = 0;
542
543  for (major = 0; major < disktab_size; ++major) {
544    rtems_disk_device_table *dtab = disktab + major;
545
546    for (minor = 0; minor < dtab->size; ++minor) {
547      rtems_disk_device *dd = dtab->minor [minor];
548
549      if (dd != NULL) {
550        free_disk_device(dd);
551      }
552    }
553    free(dtab->minor);
554  }
555  free(disktab);
556
557  rtems_semaphore_delete(diskdevs_mutex);
558
559  diskdevs_mutex = RTEMS_ID_NONE;
560  disktab = NULL;
561  disktab_size = 0;
562
563  return RTEMS_SUCCESSFUL;
564}
Note: See TracBrowser for help on using the repository browser.