xref: /minix3/minix/servers/vfs/bdev.c (revision dd41186aac5f9c05e657f127b7e5d33f375d1686)
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