xref: /minix/minix/servers/vfs/bdev.c (revision 045e0ed3)
1 /*
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
9  * through VFS.
10  *
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.
14  *
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
21  */
22 
23 #include "fs.h"
24 #include "vnode.h"
25 #include "file.h"
26 #include <string.h>
27 #include <assert.h>
28 
29 /*
30  * Send a request to a block device, and suspend the current thread until a
31  * reply from the driver comes in.
32  */
33 static int
34 bdev_sendrec(endpoint_t driver_e, message * mess_ptr)
35 {
36 	int r, status, retry_count;
37 	message mess_retry;
38 
39 	assert(IS_BDEV_RQ(mess_ptr->m_type));
40 	mess_retry = *mess_ptr;
41 	retry_count = 0;
42 
43 	do {
44 		r = drv_sendrec(driver_e, mess_ptr);
45 		if (r != OK)
46 			return r;
47 
48 		status = mess_ptr->m_lblockdriver_lbdev_reply.status;
49 		if (status == ERESTART) {
50 			r = EDEADEPT;
51 			*mess_ptr = mess_retry;
52 			retry_count++;
53 		}
54 	} while (status == ERESTART && retry_count < 5);
55 
56 	/* If we failed to restart the request, return EIO. */
57 	if (status == ERESTART && retry_count >= 5)
58 		return EIO;
59 
60 	if (r != OK) {
61 		if (r == EDEADSRCDST || r == EDEADEPT) {
62 			printf("VFS: dead driver %d\n", driver_e);
63 			dmap_unmap_by_endpt(driver_e);
64 			return EIO;
65 		} else if (r == ELOCKED) {
66 			printf("VFS: deadlock talking to %d\n", driver_e);
67 			return EIO;
68 		}
69 		panic("VFS: uncaught bdev_sendrec failure: %d", r);
70 	}
71 
72 	return OK;
73 }
74 
75 /*
76  * Open a block device.
77  */
78 int
79 bdev_open(dev_t dev, int bits)
80 {
81 	devmajor_t major_dev;
82 	devminor_t minor_dev;
83 	message dev_mess;
84 	int r, access;
85 
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;
90 
91 	access = 0;
92 	if (bits & R_BIT) access |= BDEV_R_BIT;
93 	if (bits & W_BIT) access |= BDEV_W_BIT;
94 
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;
101 
102 	/* Call the driver. */
103 	r = bdev_sendrec(dmap[major_dev].dmap_driver, &dev_mess);
104 	if (r != OK)
105 		return r;
106 
107 	return dev_mess.m_lblockdriver_lbdev_reply.status;
108 }
109 
110 /*
111  * Close a block device.
112  */
113 int
114 bdev_close(dev_t dev)
115 {
116 	devmajor_t major_dev;
117 	devminor_t minor_dev;
118 	message dev_mess;
119 	int r;
120 
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;
125 
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;
131 
132 	/* Call the driver. */
133 	r = bdev_sendrec(dmap[major_dev].dmap_driver, &dev_mess);
134 	if (r != OK)
135 		return r;
136 
137 	return dev_mess.m_lblockdriver_lbdev_reply.status;
138 }
139 
140 /*
141  * Perform an I/O control operation on a block device.
142  */
143 int
144 bdev_ioctl(dev_t dev, endpoint_t proc_e, unsigned long req, vir_bytes buf)
145 {
146 	struct dmap *dp;
147 	cp_grant_id_t grant;
148 	message dev_mess;
149 	devmajor_t major_dev;
150 	devminor_t minor_dev;
151 	int r;
152 
153 	major_dev = major(dev);
154 	minor_dev = minor(dev);
155 
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);
160 		return ENXIO;
161 	}
162 
163 	/* Set up a grant if necessary. */
164 	grant = make_ioctl_grant(dp->dmap_driver, proc_e, buf, req);
165 
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;
174 
175 	/* Call the driver. */
176 	r = bdev_sendrec(dp->dmap_driver, &dev_mess);
177 
178 	/* Clean up. */
179 	if (GRANT_VALID(grant)) cpf_revoke(grant);
180 
181 	/* Return the result. */
182 	if (r != OK)
183 		return r;
184 
185 	return dev_mess.m_lblockdriver_lbdev_reply.status;
186 }
187 
188 /*
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.
191  */
192 void
193 bdev_reply(void)
194 {
195 	struct worker_thread *wp;
196 	struct dmap *dp;
197 
198 	if ((dp = get_dmap(who_e)) == NULL) {
199 		printf("VFS: ignoring block dev reply from unknown driver "
200 		    "%d\n", who_e);
201 		return;
202 	}
203 
204 	if (dp->dmap_servicing == INVALID_THREAD) {
205 		printf("VFS: ignoring spurious block dev reply from %d\n",
206 		    who_e);
207 		return;
208 	}
209 
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",
213 		    who_e);
214 		return;
215 	}
216 
217 	*wp->w_drv_sendrec = m_in;
218 	wp->w_drv_sendrec = NULL;
219 	worker_signal(wp);
220 }
221 
222 /*
223  * A new block device driver has been mapped in.  This may affect both mounted
224  * file systems and open block-special files.
225  */
226 void
227 bdev_up(devmajor_t maj)
228 {
229 	int r, found, bits;
230 	struct filp *rfilp;
231 	struct vmnt *vmp;
232 	struct vnode *vp;
233 	char *label;
234 
235 	if (maj < 0 || maj >= NR_DEVICES) panic("VFS: out-of-bound major");
236 	label = dmap[maj].dmap_label;
237 	found = 0;
238 
239 	/*
240 	 * For each block-special file that was previously opened on the
241 	 * affected device, we need to reopen it on the new driver.
242 	 */
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;
248 
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 */
256 		}
257 
258 		found = 1;
259 	}
260 
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;
264 
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",
268 			    vmp->m_fs_e);
269 	}
270 
271 	/*
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.
276 	 */
277 	if (found) {
278 		if (req_newdriver(ROOT_FS_E, makedev(maj, 0), label) != OK)
279 			printf("VFS: error sending new driver label to %d\n",
280 			    ROOT_FS_E);
281 	}
282 }
283