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

Last change on this file was bcef89f2, checked in by Sebastian Huber <sebastian.huber@…>, on 05/19/23 at 06:18:25

Update company name

The embedded brains GmbH & Co. KG is the legal successor of embedded
brains GmbH.

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