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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
|
/*
* This file contains routines to perform certain block device operations.
* These routines are called when a user application opens or closes a block
* device node, or performs an ioctl(2) call on such an opened node. Reading
* and writing on an opened block device is routed through the file system
* service that has mounted that block device, or the root file system service
* if the block device is not mounted. All block device operations by file
* system services themselves are going directly to the block device, and not
* through VFS.
*
* Block device drivers may not suspend operations for later processing, and
* thus, block device operations simply block their calling thread for the
* duration of the operation.
*
* The entry points in this file are:
* bdev_open: open a block device
* bdev_close: close a block device
* bdev_ioctl: issue an I/O control request on a block device
* bdev_reply: process the result of a block driver request
* bdev_up: a block driver has been mapped in
*/
#include "fs.h"
#include "vnode.h"
#include "file.h"
#include <string.h>
#include <assert.h>
/*
* Send a request to a block device, and suspend the current thread until a
* reply from the driver comes in.
*/
static int
bdev_sendrec(endpoint_t driver_e, message * mess_ptr)
{
int r, status, retry_count;
message mess_retry;
assert(IS_BDEV_RQ(mess_ptr->m_type));
mess_retry = *mess_ptr;
retry_count = 0;
do {
r = drv_sendrec(driver_e, mess_ptr);
if (r != OK)
return r;
status = mess_ptr->m_lblockdriver_lbdev_reply.status;
if (status == ERESTART) {
r = EDEADEPT;
*mess_ptr = mess_retry;
retry_count++;
}
} while (status == ERESTART && retry_count < 5);
/* If we failed to restart the request, return EIO. */
if (status == ERESTART && retry_count >= 5)
return EIO;
if (r != OK) {
if (r == EDEADSRCDST || r == EDEADEPT) {
printf("VFS: dead driver %d\n", driver_e);
dmap_unmap_by_endpt(driver_e);
return EIO;
} else if (r == ELOCKED) {
printf("VFS: deadlock talking to %d\n", driver_e);
return EIO;
}
panic("VFS: uncaught bdev_sendrec failure: %d", r);
}
return OK;
}
/*
* Open a block device.
*/
int
bdev_open(dev_t dev, int bits)
{
devmajor_t major_dev;
devminor_t minor_dev;
message dev_mess;
int r, access;
major_dev = major(dev);
minor_dev = minor(dev);
if (major_dev < 0 || major_dev >= NR_DEVICES) return ENXIO;
if (dmap[major_dev].dmap_driver == NONE) return ENXIO;
access = 0;
if (bits & R_BIT) access |= BDEV_R_BIT;
if (bits & W_BIT) access |= BDEV_W_BIT;
/* Set up the message passed to the driver. */
memset(&dev_mess, 0, sizeof(dev_mess));
dev_mess.m_type = BDEV_OPEN;
dev_mess.m_lbdev_lblockdriver_msg.minor = minor_dev;
dev_mess.m_lbdev_lblockdriver_msg.access = access;
dev_mess.m_lbdev_lblockdriver_msg.id = 0;
/* Call the driver. */
r = bdev_sendrec(dmap[major_dev].dmap_driver, &dev_mess);
if (r != OK)
return r;
return dev_mess.m_lblockdriver_lbdev_reply.status;
}
/*
* Close a block device.
*/
int
bdev_close(dev_t dev)
{
devmajor_t major_dev;
devminor_t minor_dev;
message dev_mess;
int r;
major_dev = major(dev);
minor_dev = minor(dev);
if (major_dev < 0 || major_dev >= NR_DEVICES) return ENXIO;
if (dmap[major_dev].dmap_driver == NONE) return ENXIO;
/* Set up the message passed to the driver. */
memset(&dev_mess, 0, sizeof(dev_mess));
dev_mess.m_type = BDEV_CLOSE;
dev_mess.m_lbdev_lblockdriver_msg.minor = minor_dev;
dev_mess.m_lbdev_lblockdriver_msg.id = 0;
/* Call the driver. */
r = bdev_sendrec(dmap[major_dev].dmap_driver, &dev_mess);
if (r != OK)
return r;
return dev_mess.m_lblockdriver_lbdev_reply.status;
}
/*
* Perform an I/O control operation on a block device.
*/
int
bdev_ioctl(dev_t dev, endpoint_t proc_e, unsigned long req, vir_bytes buf)
{
struct dmap *dp;
cp_grant_id_t grant;
message dev_mess;
devmajor_t major_dev;
devminor_t minor_dev;
int r;
major_dev = major(dev);
minor_dev = minor(dev);
/* Determine driver dmap. */
dp = &dmap[major_dev];
if (dp->dmap_driver == NONE) {
printf("VFS: bdev_ioctl: no driver for major %d\n", major_dev);
return ENXIO;
}
/* Set up a grant if necessary. */
grant = make_ioctl_grant(dp->dmap_driver, proc_e, buf, req);
/* Set up the message passed to the driver. */
memset(&dev_mess, 0, sizeof(dev_mess));
dev_mess.m_type = BDEV_IOCTL;
dev_mess.m_lbdev_lblockdriver_msg.minor = minor_dev;
dev_mess.m_lbdev_lblockdriver_msg.request = req;
dev_mess.m_lbdev_lblockdriver_msg.grant = grant;
dev_mess.m_lbdev_lblockdriver_msg.user = proc_e;
dev_mess.m_lbdev_lblockdriver_msg.id = 0;
/* Call the driver. */
r = bdev_sendrec(dp->dmap_driver, &dev_mess);
/* Clean up. */
if (GRANT_VALID(grant)) cpf_revoke(grant);
/* Return the result. */
if (r != OK)
return r;
return dev_mess.m_lblockdriver_lbdev_reply.status;
}
/*
* A block driver has results for a call. There must be a thread waiting for
* these results; wake it up. This function MUST NOT block its calling thread.
*/
void
bdev_reply(void)
{
struct worker_thread *wp;
struct dmap *dp;
if ((dp = get_dmap_by_endpt(who_e)) == NULL) {
printf("VFS: ignoring block dev reply from unknown driver "
"%d\n", who_e);
return;
}
if (dp->dmap_servicing == INVALID_THREAD) {
printf("VFS: ignoring spurious block dev reply from %d\n",
who_e);
return;
}
wp = worker_get(dp->dmap_servicing);
if (wp == NULL || wp->w_task != who_e || wp->w_drv_sendrec == NULL) {
printf("VFS: no worker thread waiting for a reply from %d\n",
who_e);
return;
}
*wp->w_drv_sendrec = m_in;
wp->w_drv_sendrec = NULL;
worker_signal(wp);
}
/*
* A new block device driver has been mapped in. This may affect both mounted
* file systems and open block-special files.
*/
void
bdev_up(devmajor_t maj)
{
int r, found, bits;
struct filp *rfilp;
struct vmnt *vmp;
struct vnode *vp;
char *label;
if (maj < 0 || maj >= NR_DEVICES) panic("VFS: out-of-bound major");
label = dmap[maj].dmap_label;
found = 0;
/*
* For each block-special file that was previously opened on the
* affected device, we need to reopen it on the new driver.
*/
for (rfilp = filp; rfilp < &filp[NR_FILPS]; rfilp++) {
if (rfilp->filp_count < 1) continue;
if ((vp = rfilp->filp_vno) == NULL) continue;
if (major(vp->v_sdev) != maj) continue;
if (!S_ISBLK(vp->v_mode)) continue;
/* Reopen the device on the driver, once per filp. */
bits = rfilp->filp_mode & (R_BIT | W_BIT);
if ((r = bdev_open(vp->v_sdev, bits)) != OK) {
printf("VFS: mounted dev %d/%d re-open failed: %d\n",
maj, minor(vp->v_sdev), r);
dmap[maj].dmap_recovering = 0;
return; /* Give up entirely */
}
found = 1;
}
/* Tell each affected mounted file system about the new endpoint. */
for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) {
if (major(vmp->m_dev) != maj) continue;
/* Send the driver label to the mounted file system. */
if (req_newdriver(vmp->m_fs_e, vmp->m_dev, label) != OK)
printf("VFS: error sending new driver label to %d\n",
vmp->m_fs_e);
}
/*
* If any block-special file was open for this major at all, also
* inform the root file system about the new driver. We do this even
* if the block-special file is linked to another mounted file system,
* merely because it is more work to check for that case.
*/
if (found) {
if (req_newdriver(ROOT_FS_E, makedev(maj, 0), label) != OK)
printf("VFS: error sending new driver label to %d\n",
ROOT_FS_E);
}
}
|