2 * This file contains routines to perform certain block device operations.
3 * These routines are called when a user application opens or closes a block
4 * device node, or performs an ioctl(2) call on such an opened node. Reading
5 * and writing on an opened block device is routed through the file system
6 * service that has mounted that block device, or the root file system service
7 * if the block device is not mounted. All block device operations by file
8 * system services themselves are going directly to the block device, and not
11 * Block device drivers may not suspend operations for later processing, and
12 * thus, block device operations simply block their calling thread for the
13 * duration of the operation.
15 * The entry points in this file are:
16 * bdev_open: open a block device
17 * bdev_close: close a block device
18 * bdev_ioctl: issue an I/O control request on a block device
19 * bdev_reply: process the result of a block driver request
20 * bdev_up: a block driver has been mapped in
30 * Send a request to a block device, and suspend the current thread until a
31 * reply from the driver comes in.
34 bdev_sendrec(endpoint_t driver_e
, message
* mess_ptr
)
36 int r
, status
, retry_count
;
39 assert(IS_BDEV_RQ(mess_ptr
->m_type
));
40 mess_retry
= *mess_ptr
;
44 r
= drv_sendrec(driver_e
, mess_ptr
);
48 status
= mess_ptr
->m_lblockdriver_lbdev_reply
.status
;
49 if (status
== ERESTART
) {
51 *mess_ptr
= mess_retry
;
54 } while (status
== ERESTART
&& retry_count
< 5);
56 /* If we failed to restart the request, return EIO. */
57 if (status
== ERESTART
&& retry_count
>= 5)
61 if (r
== EDEADSRCDST
|| r
== EDEADEPT
) {
62 printf("VFS: dead driver %d\n", driver_e
);
63 dmap_unmap_by_endpt(driver_e
);
65 } else if (r
== ELOCKED
) {
66 printf("VFS: deadlock talking to %d\n", driver_e
);
69 panic("VFS: uncaught bdev_sendrec failure: %d", r
);
76 * Open a block device.
79 bdev_open(dev_t dev
, int bits
)
86 major_dev
= major(dev
);
87 minor_dev
= minor(dev
);
88 if (major_dev
< 0 || major_dev
>= NR_DEVICES
) return ENXIO
;
89 if (dmap
[major_dev
].dmap_driver
== NONE
) return ENXIO
;
92 if (bits
& R_BIT
) access
|= BDEV_R_BIT
;
93 if (bits
& W_BIT
) access
|= BDEV_W_BIT
;
95 /* Set up the message passed to the driver. */
96 memset(&dev_mess
, 0, sizeof(dev_mess
));
97 dev_mess
.m_type
= BDEV_OPEN
;
98 dev_mess
.m_lbdev_lblockdriver_msg
.minor
= minor_dev
;
99 dev_mess
.m_lbdev_lblockdriver_msg
.access
= access
;
100 dev_mess
.m_lbdev_lblockdriver_msg
.id
= 0;
102 /* Call the driver. */
103 r
= bdev_sendrec(dmap
[major_dev
].dmap_driver
, &dev_mess
);
107 return dev_mess
.m_lblockdriver_lbdev_reply
.status
;
111 * Close a block device.
114 bdev_close(dev_t dev
)
116 devmajor_t major_dev
;
117 devminor_t minor_dev
;
121 major_dev
= major(dev
);
122 minor_dev
= minor(dev
);
123 if (major_dev
< 0 || major_dev
>= NR_DEVICES
) return ENXIO
;
124 if (dmap
[major_dev
].dmap_driver
== NONE
) return ENXIO
;
126 /* Set up the message passed to the driver. */
127 memset(&dev_mess
, 0, sizeof(dev_mess
));
128 dev_mess
.m_type
= BDEV_CLOSE
;
129 dev_mess
.m_lbdev_lblockdriver_msg
.minor
= minor_dev
;
130 dev_mess
.m_lbdev_lblockdriver_msg
.id
= 0;
132 /* Call the driver. */
133 r
= bdev_sendrec(dmap
[major_dev
].dmap_driver
, &dev_mess
);
137 return dev_mess
.m_lblockdriver_lbdev_reply
.status
;
141 * Perform an I/O control operation on a block device.
144 bdev_ioctl(dev_t dev
, endpoint_t proc_e
, unsigned long req
, vir_bytes buf
)
149 devmajor_t major_dev
;
150 devminor_t minor_dev
;
153 major_dev
= major(dev
);
154 minor_dev
= minor(dev
);
156 /* Determine driver dmap. */
157 dp
= &dmap
[major_dev
];
158 if (dp
->dmap_driver
== NONE
) {
159 printf("VFS: bdev_ioctl: no driver for major %d\n", major_dev
);
163 /* Set up a grant if necessary. */
164 grant
= make_ioctl_grant(dp
->dmap_driver
, proc_e
, buf
, req
);
166 /* Set up the message passed to the driver. */
167 memset(&dev_mess
, 0, sizeof(dev_mess
));
168 dev_mess
.m_type
= BDEV_IOCTL
;
169 dev_mess
.m_lbdev_lblockdriver_msg
.minor
= minor_dev
;
170 dev_mess
.m_lbdev_lblockdriver_msg
.request
= req
;
171 dev_mess
.m_lbdev_lblockdriver_msg
.grant
= grant
;
172 dev_mess
.m_lbdev_lblockdriver_msg
.user
= proc_e
;
173 dev_mess
.m_lbdev_lblockdriver_msg
.id
= 0;
175 /* Call the driver. */
176 r
= bdev_sendrec(dp
->dmap_driver
, &dev_mess
);
179 if (GRANT_VALID(grant
)) cpf_revoke(grant
);
181 /* Return the result. */
185 return dev_mess
.m_lblockdriver_lbdev_reply
.status
;
189 * A block driver has results for a call. There must be a thread waiting for
190 * these results; wake it up. This function MUST NOT block its calling thread.
195 struct worker_thread
*wp
;
198 if ((dp
= get_dmap_by_endpt(who_e
)) == NULL
) {
199 printf("VFS: ignoring block dev reply from unknown driver "
204 if (dp
->dmap_servicing
== INVALID_THREAD
) {
205 printf("VFS: ignoring spurious block dev reply from %d\n",
210 wp
= worker_get(dp
->dmap_servicing
);
211 if (wp
== NULL
|| wp
->w_task
!= who_e
|| wp
->w_drv_sendrec
== NULL
) {
212 printf("VFS: no worker thread waiting for a reply from %d\n",
217 *wp
->w_drv_sendrec
= m_in
;
218 wp
->w_drv_sendrec
= NULL
;
223 * A new block device driver has been mapped in. This may affect both mounted
224 * file systems and open block-special files.
227 bdev_up(devmajor_t maj
)
235 if (maj
< 0 || maj
>= NR_DEVICES
) panic("VFS: out-of-bound major");
236 label
= dmap
[maj
].dmap_label
;
240 * For each block-special file that was previously opened on the
241 * affected device, we need to reopen it on the new driver.
243 for (rfilp
= filp
; rfilp
< &filp
[NR_FILPS
]; rfilp
++) {
244 if (rfilp
->filp_count
< 1) continue;
245 if ((vp
= rfilp
->filp_vno
) == NULL
) continue;
246 if (major(vp
->v_sdev
) != maj
) continue;
247 if (!S_ISBLK(vp
->v_mode
)) continue;
249 /* Reopen the device on the driver, once per filp. */
250 bits
= rfilp
->filp_mode
& (R_BIT
| W_BIT
);
251 if ((r
= bdev_open(vp
->v_sdev
, bits
)) != OK
) {
252 printf("VFS: mounted dev %d/%d re-open failed: %d\n",
253 maj
, minor(vp
->v_sdev
), r
);
254 dmap
[maj
].dmap_recovering
= 0;
255 return; /* Give up entirely */
261 /* Tell each affected mounted file system about the new endpoint. */
262 for (vmp
= &vmnt
[0]; vmp
< &vmnt
[NR_MNTS
]; ++vmp
) {
263 if (major(vmp
->m_dev
) != maj
) continue;
265 /* Send the driver label to the mounted file system. */
266 if (req_newdriver(vmp
->m_fs_e
, vmp
->m_dev
, label
) != OK
)
267 printf("VFS: error sending new driver label to %d\n",
272 * If any block-special file was open for this major at all, also
273 * inform the root file system about the new driver. We do this even
274 * if the block-special file is linked to another mounted file system,
275 * merely because it is more work to check for that case.
278 if (req_newdriver(ROOT_FS_E
, makedev(maj
, 0), label
) != OK
)
279 printf("VFS: error sending new driver label to %d\n",