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
RevLine 
[57aa979]1/**
2 * @file
3 *
[4670d91]4 * @ingroup rtems_disk
5 *
[e41369ef]6 * @brief Block device disk management implementation.
[57aa979]7 */
[18daff9]8
[8b96149]9/*
[e51bd96]10 * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
11 * Author: Victor V. Vengerov <vvv@oktet.ru>
12 *
[73c09b3b]13 * Copyright (c) 2009-2012 embedded brains GmbH.
[e51bd96]14 */
15
[006fa1ef]16#if HAVE_CONFIG_H
17#include "config.h"
18#endif
[e51bd96]19
20#include <stdlib.h>
[e41369ef]21#include <unistd.h>
[e51bd96]22#include <string.h>
23
[e41369ef]24#include <rtems.h>
25#include <rtems/libio.h>
26#include <rtems/diskdevs.h>
27#include <rtems/blkdev.h>
28#include <rtems/bdbuf.h>
[e51bd96]29
[e41369ef]30#define DISKTAB_INITIAL_SIZE 8
[e51bd96]31
32/* Table of disk devices having the same major number */
[3899a537]33typedef struct rtems_disk_device_table {
[e41369ef]34  rtems_disk_device **minor; /* minor-indexed disk device table */
35  rtems_device_minor_number size; /* Number of entries in the table */
[3899a537]36} rtems_disk_device_table;
[e51bd96]37
38/* Pointer to [major].minor[minor] indexed array of disk devices */
[3899a537]39static rtems_disk_device_table *disktab;
[e51bd96]40
41/* Number of allocated entries in disktab table */
[e41369ef]42static rtems_device_major_number disktab_size;
[e51bd96]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 */
[e03c37a]58static volatile bool diskdevs_protected;
[e51bd96]59
[9de9b7d2]60static rtems_interrupt_lock diskdevs_lock;
61
[e41369ef]62static rtems_status_code
63disk_lock(void)
[e51bd96]64{
[e41369ef]65  rtems_status_code sc = RTEMS_SUCCESSFUL;
[e51bd96]66
[e41369ef]67  sc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
68  if (sc == RTEMS_SUCCESSFUL) {
69    diskdevs_protected = true;
[048dcd2b]70
[e41369ef]71    return RTEMS_SUCCESSFUL;
72  } else {
73    return RTEMS_NOT_CONFIGURED;
74  }
75}
[048dcd2b]76
[e41369ef]77static void
78disk_unlock(void)
79{
80  rtems_status_code sc = RTEMS_SUCCESSFUL;
[048dcd2b]81
[e41369ef]82  diskdevs_protected = false;
[048dcd2b]83
[e41369ef]84  sc = rtems_semaphore_release(diskdevs_mutex);
85  if (sc != RTEMS_SUCCESSFUL) {
86    /* FIXME: Error number */
87    rtems_fatal_error_occurred(0xdeadbeef);
88  }
[e51bd96]89}
90
[3899a537]91static rtems_disk_device *
[bdac0eed]92get_disk_entry(dev_t dev, bool lookup_only)
[e51bd96]93{
[e41369ef]94  rtems_device_major_number major = 0;
95  rtems_device_minor_number minor = 0;
[e51bd96]96
[e41369ef]97  rtems_filesystem_split_dev_t(dev, major, minor);
[048dcd2b]98
[e41369ef]99  if (major < disktab_size && disktab != NULL) {
100    rtems_disk_device_table *dtab = disktab + major;
[048dcd2b]101
[e41369ef]102    if (minor < dtab->size && dtab->minor != NULL) {
103      rtems_disk_device *dd = dtab->minor [minor];
[048dcd2b]104
[bdac0eed]105      if (dd != NULL && !lookup_only) {
106        if (!dd->deleted) {
107          ++dd->uses;
108        } else {
109          dd = NULL;
110        }
[e41369ef]111      }
[bdac0eed]112
113      return dd;
[e41369ef]114    }
115  }
116
117  return NULL;
[e51bd96]118}
119
[e41369ef]120static rtems_disk_device **
121create_disk_table_entry(dev_t dev)
[e51bd96]122{
[e41369ef]123  rtems_device_major_number major = 0;
124  rtems_device_minor_number minor = 0;
[048dcd2b]125
[e41369ef]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;
[e51bd96]135    }
[048dcd2b]136
[e41369ef]137    table = realloc(table, new_size * sizeof(*table));
138    if (table == NULL) {
139      return NULL;
[e51bd96]140    }
[e41369ef]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;
[e51bd96]159    }
[048dcd2b]160
[e41369ef]161    table = realloc(table, new_size * sizeof(*table));
162    if (table == NULL) {
163      return NULL;
[e51bd96]164    }
[048dcd2b]165
[e41369ef]166    memset(table + old_size, 0, (new_size - old_size) * sizeof(*table));
167    disktab [major].minor = table;
168    disktab [major].size = new_size;
169  }
[048dcd2b]170
[e41369ef]171  return disktab [major].minor + minor;
172}
[048dcd2b]173
[e41369ef]174static rtems_status_code
[73c09b3b]175create_disk(
176  dev_t dev,
177  const char *name,
178  rtems_disk_device **dd_ptr,
179  char **alloc_name_ptr
180)
[e41369ef]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);
[18daff9]201
[e41369ef]202    if (alloc_name == NULL) {
203      free(dd);
204
205      return RTEMS_NO_MEMORY;
206    }
207  }
208
209  if (name != NULL) {
[cca94a2]210    if (mknod(alloc_name, 0777 | S_IFBLK, dev) < 0) {
[e41369ef]211      free(alloc_name);
212      free(dd);
213      return RTEMS_UNSATISFIED;
214    }
215  }
216
217  *dd_entry = dd;
218  *dd_ptr = dd;
[73c09b3b]219  *alloc_name_ptr = alloc_name;
[e41369ef]220
221  return RTEMS_SUCCESSFUL;
[e51bd96]222}
223
[b467782b]224static int null_handler(
225  rtems_disk_device *dd,
226  uint32_t req,
227  void *argp
228)
229{
230  return -1;
231}
232
[57aa979]233rtems_status_code rtems_disk_create_phys(
234  dev_t dev,
235  uint32_t block_size,
[e41369ef]236  rtems_blkdev_bnum block_count,
[57aa979]237  rtems_block_device_ioctl handler,
[b96e09c]238  void *driver_data,
[57aa979]239  const char *name
240)
[e51bd96]241{
[e41369ef]242  rtems_disk_device *dd = NULL;
243  rtems_status_code sc = RTEMS_SUCCESSFUL;
[73c09b3b]244  char *alloc_name = NULL;
[e51bd96]245
[e41369ef]246  if (handler == NULL) {
247    return RTEMS_INVALID_ADDRESS;
248  }
[e51bd96]249
[e41369ef]250  sc = disk_lock();
251  if (sc != RTEMS_SUCCESSFUL) {
252    return sc;
253  }
[048dcd2b]254
[73c09b3b]255  sc = create_disk(dev, name, &dd, &alloc_name);
[e41369ef]256  if (sc != RTEMS_SUCCESSFUL) {
257    disk_unlock();
[e51bd96]258
[e41369ef]259    return sc;
260  }
[048dcd2b]261
[73c09b3b]262  sc = rtems_disk_init_phys(
263    dd,
264    block_size,
265    block_count,
266    handler,
267    driver_data
268  );
[e41369ef]269
[73c09b3b]270  dd->dev = dev;
271  dd->name = alloc_name;
[18daff9]272
[b467782b]273  if (sc != RTEMS_SUCCESSFUL) {
274    dd->ioctl = null_handler;
275    rtems_disk_delete(dev);
276    disk_unlock();
277
278    return sc;
279  }
280
[e41369ef]281  disk_unlock();
[e51bd96]282
[e41369ef]283  return RTEMS_SUCCESSFUL;
284}
285
286static bool
287is_physical_disk(const rtems_disk_device *dd)
288{
289  return dd->phys_dev == dd;
[e51bd96]290}
291
[57aa979]292rtems_status_code rtems_disk_create_log(
293  dev_t dev,
294  dev_t phys,
[73c09b3b]295  rtems_blkdev_bnum block_begin,
[e41369ef]296  rtems_blkdev_bnum block_count,
[57aa979]297  const char *name
298)
[e51bd96]299{
[e41369ef]300  rtems_status_code sc = RTEMS_SUCCESSFUL;
[73c09b3b]301  rtems_disk_device *phys_dd = NULL;
[57aa979]302  rtems_disk_device *dd = NULL;
[73c09b3b]303  char *alloc_name = NULL;
[e51bd96]304
[e41369ef]305  sc = disk_lock();
306  if (sc != RTEMS_SUCCESSFUL) {
307    return sc;
[57aa979]308  }
309
[73c09b3b]310  phys_dd = get_disk_entry(phys, true);
311  if (phys_dd == NULL) {
[e41369ef]312    disk_unlock();
313
314    return RTEMS_INVALID_ID;
315  }
[57aa979]316
[73c09b3b]317  sc = create_disk(dev, name, &dd, &alloc_name);
318  if (sc != RTEMS_SUCCESSFUL) {
[e41369ef]319    disk_unlock();
320
[73c09b3b]321    return sc;
[57aa979]322  }
[048dcd2b]323
[73c09b3b]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
[e41369ef]336  if (sc != RTEMS_SUCCESSFUL) {
[73c09b3b]337    dd->ioctl = null_handler;
338    rtems_disk_delete(dev);
[e41369ef]339    disk_unlock();
340
341    return sc;
[57aa979]342  }
[048dcd2b]343
[e41369ef]344  disk_unlock();
[e51bd96]345
[e41369ef]346  return RTEMS_SUCCESSFUL;
347}
[048dcd2b]348
[e41369ef]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  }
[e51bd96]405}
406
407rtems_status_code
408rtems_disk_delete(dev_t dev)
409{
[e41369ef]410  rtems_status_code sc = RTEMS_SUCCESSFUL;
[57aa979]411  rtems_disk_device *dd = NULL;
412
[e41369ef]413  sc = disk_lock();
414  if (sc != RTEMS_SUCCESSFUL) {
415    return sc;
[57aa979]416  }
[048dcd2b]417
[bdac0eed]418  dd = get_disk_entry(dev, true);
[57aa979]419  if (dd == NULL) {
[e41369ef]420    disk_unlock();
421
422    return RTEMS_INVALID_ID;
[57aa979]423  }
[048dcd2b]424
[e41369ef]425  dd->deleted = true;
426  rtems_disk_cleanup(dd);
[e51bd96]427
[e41369ef]428  disk_unlock();
[57aa979]429
[e41369ef]430  return RTEMS_SUCCESSFUL;
[e51bd96]431}
432
[3899a537]433rtems_disk_device *
434rtems_disk_obtain(dev_t dev)
[e51bd96]435{
[e41369ef]436  rtems_status_code sc = RTEMS_SUCCESSFUL;
437  rtems_disk_device *dd = NULL;
438  rtems_interrupt_level level;
[048dcd2b]439
[9de9b7d2]440  rtems_interrupt_lock_acquire(&diskdevs_lock, level);
[e41369ef]441  if (!diskdevs_protected) {
442    /* Frequent and quickest case */
[bdac0eed]443    dd = get_disk_entry(dev, false);
[9de9b7d2]444    rtems_interrupt_lock_release(&diskdevs_lock, level);
[e41369ef]445  } else {
[9de9b7d2]446    rtems_interrupt_lock_release(&diskdevs_lock, level);
[e41369ef]447
448    sc = disk_lock();
449    if (sc == RTEMS_SUCCESSFUL) {
[bdac0eed]450      dd = get_disk_entry(dev, false);
[e41369ef]451      disk_unlock();
[e51bd96]452    }
[e41369ef]453  }
454
455  return dd;
[e51bd96]456}
457
458rtems_status_code
[3899a537]459rtems_disk_release(rtems_disk_device *dd)
[e51bd96]460{
[e41369ef]461  rtems_interrupt_level level;
462  dev_t dev = dd->dev;
463  unsigned uses = 0;
464  bool deleted = false;
465
[9de9b7d2]466  rtems_interrupt_lock_acquire(&diskdevs_lock, level);
[e41369ef]467  uses = --dd->uses;
468  deleted = dd->deleted;
[9de9b7d2]469  rtems_interrupt_lock_release(&diskdevs_lock, level);
[e41369ef]470
471  if (uses == 0 && deleted) {
472    rtems_disk_delete(dev);
473  }
474
475  return RTEMS_SUCCESSFUL;
[e51bd96]476}
477
[3899a537]478rtems_disk_device *
[e51bd96]479rtems_disk_next(dev_t dev)
480{
[c8522fd]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;
[e51bd96]485
[c8522fd]486  if (dev != (dev_t) -1) {
487    rtems_filesystem_split_dev_t(dev, major, minor);
[e51bd96]488
[c8522fd]489    /* If minor wraps around */
490    if ((minor + 1) < minor) {
491      /* If major wraps around */
492      if ((major + 1) < major) {
[e51bd96]493        return NULL;
[c8522fd]494      }
495      ++major;
496      minor = 0;
497    } else {
498      ++minor;
499    }
500  }
[048dcd2b]501
[c8522fd]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];
[e51bd96]531    }
[c8522fd]532  }
[e51bd96]533}
534
535rtems_status_code
536rtems_disk_io_initialize(void)
537{
[e41369ef]538  rtems_status_code sc = RTEMS_SUCCESSFUL;
539  rtems_device_major_number size = DISKTAB_INITIAL_SIZE;
[e51bd96]540
[e41369ef]541  if (disktab_size > 0) {
542    return RTEMS_SUCCESSFUL;
543  }
[048dcd2b]544
[e41369ef]545  disktab = calloc(size, sizeof(rtems_disk_device_table));
546  if (disktab == NULL) {
547    return RTEMS_NO_MEMORY;
548  }
[e51bd96]549
[e41369ef]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);
[048dcd2b]561
[e41369ef]562    return RTEMS_NO_MEMORY;
563  }
[048dcd2b]564
[e41369ef]565  sc = rtems_bdbuf_init();
566  if (sc != RTEMS_SUCCESSFUL) {
567    rtems_semaphore_delete(diskdevs_mutex);
568    free(disktab);
[048dcd2b]569
[e41369ef]570    return RTEMS_UNSATISFIED;
571  }
[048dcd2b]572
[e41369ef]573  disktab_size = size;
574
575  return RTEMS_SUCCESSFUL;
[e51bd96]576}
577
578rtems_status_code
579rtems_disk_io_done(void)
580{
[e41369ef]581  rtems_device_major_number major = 0;
582  rtems_device_minor_number minor = 0;
[e51bd96]583
[e41369ef]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      }
[e51bd96]593    }
[e41369ef]594    free(dtab->minor);
595  }
596  free(disktab);
597
598  rtems_semaphore_delete(diskdevs_mutex);
[e51bd96]599
[e41369ef]600  diskdevs_mutex = RTEMS_ID_NONE;
601  disktab = NULL;
602  disktab_size = 0;
[048dcd2b]603
[e41369ef]604  return RTEMS_SUCCESSFUL;
[e51bd96]605}
Note: See TracBrowser for help on using the repository browser.