summaryrefslogtreecommitdiff
path: root/minix/lib/libbdev/minor.c
blob: e25c92c3353e794f11752a3223524717284f09b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/* libbdev - tracking and reopening of opened minor devices */

#include <minix/drivers.h>
#include <minix/bdev.h>
#include <assert.h>

#include "const.h"
#include "type.h"
#include "proto.h"

static struct {
  dev_t dev;
  int count;
  int access;
} open_dev[NR_OPEN_DEVS] = { { NO_DEV, 0, 0 } };

int bdev_minor_reopen(dev_t dev)
{
/* Reopen all minor devices on a major device. This function duplicates some
 * code from elsewhere, because in this case we must avoid performing recovery.
 * FIXME: if reopening fails with a non-IPC error, we should attempt to close
 * all minors that we did manage to reopen so far, or they might stay open
 * forever.
 */
  endpoint_t endpt;
  message m;
  int i, j, r, major;

  major = major(dev);
  endpt = bdev_driver_get(dev);

  assert(endpt != NONE);

  for (i = 0; i < NR_OPEN_DEVS; i++) {
	if (major(open_dev[i].dev) != major)
		continue;

	/* Each minor device may have been opened multiple times. Send an open
	 * request for each time that it was opened before. We could reopen it
	 * just once, but then we'd have to keep a shadow open count as well.
	 */
	for (j = 0; j < open_dev[i].count; j++) {
		memset(&m, 0, sizeof(m));
		m.m_type = BDEV_OPEN;
		m.m_lbdev_lblockdriver_msg.minor = minor(open_dev[i].dev);
		m.m_lbdev_lblockdriver_msg.access = open_dev[i].access;
		m.m_lbdev_lblockdriver_msg.id = NO_ID;

		if ((r = ipc_sendrec(endpt, &m)) != OK) {
			printf("bdev: IPC to driver (%d) failed (%d)\n",
				endpt, r);
			return r;
		}

		if (m.m_type != BDEV_REPLY) {
			printf("bdev: driver (%d) sent weird response (%d)\n",
				endpt, m.m_type);
			return EINVAL;
		}

		if (m.m_lblockdriver_lbdev_reply.id != NO_ID) {
			printf("bdev: driver (%d) sent invalid ID (%d)\n",
				endpt, m.m_lblockdriver_lbdev_reply.id);
			return EINVAL;
		}

		if ((r = m.m_lblockdriver_lbdev_reply.status) != OK) {
			printf("bdev: driver (%d) failed device reopen (%d)\n",
				endpt, r);
			return r;
		}
	}
  }

  return OK;
}

void bdev_minor_add(dev_t dev, int bits)
{
/* Increase the reference count of the given minor device.
 */
  int i, ifree = -1;

  for (i = 0; i < NR_OPEN_DEVS; i++) {
	if (open_dev[i].dev == dev) {
		open_dev[i].count++;
		open_dev[i].access |= bits;

		return;
	}

	if (ifree < 0 && open_dev[i].dev == NO_DEV)
		ifree = i;
  }

  if (ifree < 0) {
	printf("bdev: too many open devices, increase NR_OPEN_DEVS\n");
	return;
  }

  open_dev[ifree].dev = dev;
  open_dev[ifree].count = 1;
  open_dev[ifree].access = bits;
}

void bdev_minor_del(dev_t dev)
{
/* Decrease the reference count of the given minor device, if present.
 */
  int i;

  for (i = 0; i < NR_OPEN_DEVS; i++) {
	if (open_dev[i].dev == dev) {
		if (!--open_dev[i].count)
			open_dev[i].dev = NO_DEV;

		break;
	}
  }
}

int bdev_minor_is_open(dev_t dev)
{
/* Return whether any minor is open for the major of the given device.
 */
  int i, major;

  major = major(dev);

  for (i = 0; i < NR_OPEN_DEVS; i++) {
	if (major(open_dev[i].dev) == major)
		return TRUE;
  }

  return FALSE;
}