source: rtems/cpukit/libblock/src/diskdevs.c @ 1b2da177

Last change on this file since 1b2da177 was 1b2da177, checked in by Sebastian Huber <sebastian.huber@…>, on Nov 18, 2017 at 3:52:32 PM

libblock: Use self-contained mutex for disk lock

Update #2843.

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