xref: /minix3/minix/servers/vfs/bdev.c (revision e3b8d4bb58a799dc7fd563ac39bf3015762af03b)
189a4204bSDavid van Moolenbroek /*
289a4204bSDavid van Moolenbroek  * This file contains routines to perform certain block device operations.
389a4204bSDavid van Moolenbroek  * These routines are called when a user application opens or closes a block
489a4204bSDavid van Moolenbroek  * device node, or performs an ioctl(2) call on such an opened node.  Reading
589a4204bSDavid van Moolenbroek  * and writing on an opened block device is routed through the file system
689a4204bSDavid van Moolenbroek  * service that has mounted that block device, or the root file system service
789a4204bSDavid van Moolenbroek  * if the block device is not mounted.  All block device operations by file
889a4204bSDavid van Moolenbroek  * system services themselves are going directly to the block device, and not
989a4204bSDavid van Moolenbroek  * through VFS.
1089a4204bSDavid van Moolenbroek  *
1189a4204bSDavid van Moolenbroek  * Block device drivers may not suspend operations for later processing, and
1289a4204bSDavid van Moolenbroek  * thus, block device operations simply block their calling thread for the
1389a4204bSDavid van Moolenbroek  * duration of the operation.
1489a4204bSDavid van Moolenbroek  *
1589a4204bSDavid van Moolenbroek  * The entry points in this file are:
1689a4204bSDavid van Moolenbroek  *   bdev_open:   open a block device
1789a4204bSDavid van Moolenbroek  *   bdev_close:  close a block device
1889a4204bSDavid van Moolenbroek  *   bdev_ioctl:  issue an I/O control request on a block device
1989a4204bSDavid van Moolenbroek  *   bdev_reply:  process the result of a block driver request
2089a4204bSDavid van Moolenbroek  *   bdev_up:     a block driver has been mapped in
2189a4204bSDavid van Moolenbroek  */
2289a4204bSDavid van Moolenbroek 
2389a4204bSDavid van Moolenbroek #include "fs.h"
2489a4204bSDavid van Moolenbroek #include "vnode.h"
2589a4204bSDavid van Moolenbroek #include "file.h"
2689a4204bSDavid van Moolenbroek #include <string.h>
2789a4204bSDavid van Moolenbroek #include <assert.h>
2889a4204bSDavid van Moolenbroek 
2989a4204bSDavid van Moolenbroek /*
3089a4204bSDavid van Moolenbroek  * Send a request to a block device, and suspend the current thread until a
3189a4204bSDavid van Moolenbroek  * reply from the driver comes in.
3289a4204bSDavid van Moolenbroek  */
3389a4204bSDavid van Moolenbroek static int
bdev_sendrec(endpoint_t driver_e,message * mess_ptr)3489a4204bSDavid van Moolenbroek bdev_sendrec(endpoint_t driver_e, message * mess_ptr)
3589a4204bSDavid van Moolenbroek {
3689a4204bSDavid van Moolenbroek 	int r, status, retry_count;
3789a4204bSDavid van Moolenbroek 	message mess_retry;
3889a4204bSDavid van Moolenbroek 
3989a4204bSDavid van Moolenbroek 	assert(IS_BDEV_RQ(mess_ptr->m_type));
4089a4204bSDavid van Moolenbroek 	mess_retry = *mess_ptr;
4189a4204bSDavid van Moolenbroek 	retry_count = 0;
4289a4204bSDavid van Moolenbroek 
4389a4204bSDavid van Moolenbroek 	do {
4489a4204bSDavid van Moolenbroek 		r = drv_sendrec(driver_e, mess_ptr);
4589a4204bSDavid van Moolenbroek 		if (r != OK)
4689a4204bSDavid van Moolenbroek 			return r;
4789a4204bSDavid van Moolenbroek 
4889a4204bSDavid van Moolenbroek 		status = mess_ptr->m_lblockdriver_lbdev_reply.status;
4989a4204bSDavid van Moolenbroek 		if (status == ERESTART) {
5089a4204bSDavid van Moolenbroek 			r = EDEADEPT;
5189a4204bSDavid van Moolenbroek 			*mess_ptr = mess_retry;
5289a4204bSDavid van Moolenbroek 			retry_count++;
5389a4204bSDavid van Moolenbroek 		}
5489a4204bSDavid van Moolenbroek 	} while (status == ERESTART && retry_count < 5);
5589a4204bSDavid van Moolenbroek 
5689a4204bSDavid van Moolenbroek 	/* If we failed to restart the request, return EIO. */
5789a4204bSDavid van Moolenbroek 	if (status == ERESTART && retry_count >= 5)
5889a4204bSDavid van Moolenbroek 		return EIO;
5989a4204bSDavid van Moolenbroek 
6089a4204bSDavid van Moolenbroek 	if (r != OK) {
6189a4204bSDavid van Moolenbroek 		if (r == EDEADSRCDST || r == EDEADEPT) {
6289a4204bSDavid van Moolenbroek 			printf("VFS: dead driver %d\n", driver_e);
6389a4204bSDavid van Moolenbroek 			dmap_unmap_by_endpt(driver_e);
6489a4204bSDavid van Moolenbroek 			return EIO;
6589a4204bSDavid van Moolenbroek 		} else if (r == ELOCKED) {
6689a4204bSDavid van Moolenbroek 			printf("VFS: deadlock talking to %d\n", driver_e);
6789a4204bSDavid van Moolenbroek 			return EIO;
6889a4204bSDavid van Moolenbroek 		}
6989a4204bSDavid van Moolenbroek 		panic("VFS: uncaught bdev_sendrec failure: %d", r);
7089a4204bSDavid van Moolenbroek 	}
7189a4204bSDavid van Moolenbroek 
7289a4204bSDavid van Moolenbroek 	return OK;
7389a4204bSDavid van Moolenbroek }
7489a4204bSDavid van Moolenbroek 
7589a4204bSDavid van Moolenbroek /*
7689a4204bSDavid van Moolenbroek  * Open a block device.
7789a4204bSDavid van Moolenbroek  */
7889a4204bSDavid van Moolenbroek int
bdev_open(dev_t dev,int bits)7989a4204bSDavid van Moolenbroek bdev_open(dev_t dev, int bits)
8089a4204bSDavid van Moolenbroek {
8189a4204bSDavid van Moolenbroek 	devmajor_t major_dev;
8289a4204bSDavid van Moolenbroek 	devminor_t minor_dev;
8389a4204bSDavid van Moolenbroek 	message dev_mess;
8489a4204bSDavid van Moolenbroek 	int r, access;
8589a4204bSDavid van Moolenbroek 
8689a4204bSDavid van Moolenbroek 	major_dev = major(dev);
8789a4204bSDavid van Moolenbroek 	minor_dev = minor(dev);
8889a4204bSDavid van Moolenbroek 	if (major_dev < 0 || major_dev >= NR_DEVICES) return ENXIO;
8989a4204bSDavid van Moolenbroek 	if (dmap[major_dev].dmap_driver == NONE) return ENXIO;
9089a4204bSDavid van Moolenbroek 
9189a4204bSDavid van Moolenbroek 	access = 0;
9289a4204bSDavid van Moolenbroek 	if (bits & R_BIT) access |= BDEV_R_BIT;
9389a4204bSDavid van Moolenbroek 	if (bits & W_BIT) access |= BDEV_W_BIT;
9489a4204bSDavid van Moolenbroek 
9589a4204bSDavid van Moolenbroek 	/* Set up the message passed to the driver. */
9689a4204bSDavid van Moolenbroek 	memset(&dev_mess, 0, sizeof(dev_mess));
9789a4204bSDavid van Moolenbroek 	dev_mess.m_type = BDEV_OPEN;
9889a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.minor = minor_dev;
9989a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.access = access;
10089a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.id = 0;
10189a4204bSDavid van Moolenbroek 
10289a4204bSDavid van Moolenbroek 	/* Call the driver. */
10389a4204bSDavid van Moolenbroek 	r = bdev_sendrec(dmap[major_dev].dmap_driver, &dev_mess);
10489a4204bSDavid van Moolenbroek 	if (r != OK)
10589a4204bSDavid van Moolenbroek 		return r;
10689a4204bSDavid van Moolenbroek 
10789a4204bSDavid van Moolenbroek 	return dev_mess.m_lblockdriver_lbdev_reply.status;
10889a4204bSDavid van Moolenbroek }
10989a4204bSDavid van Moolenbroek 
11089a4204bSDavid van Moolenbroek /*
11189a4204bSDavid van Moolenbroek  * Close a block device.
11289a4204bSDavid van Moolenbroek  */
11389a4204bSDavid van Moolenbroek int
bdev_close(dev_t dev)11489a4204bSDavid van Moolenbroek bdev_close(dev_t dev)
11589a4204bSDavid van Moolenbroek {
11689a4204bSDavid van Moolenbroek 	devmajor_t major_dev;
11789a4204bSDavid van Moolenbroek 	devminor_t minor_dev;
11889a4204bSDavid van Moolenbroek 	message dev_mess;
11989a4204bSDavid van Moolenbroek 	int r;
12089a4204bSDavid van Moolenbroek 
12189a4204bSDavid van Moolenbroek 	major_dev = major(dev);
12289a4204bSDavid van Moolenbroek 	minor_dev = minor(dev);
12389a4204bSDavid van Moolenbroek 	if (major_dev < 0 || major_dev >= NR_DEVICES) return ENXIO;
12489a4204bSDavid van Moolenbroek 	if (dmap[major_dev].dmap_driver == NONE) return ENXIO;
12589a4204bSDavid van Moolenbroek 
12689a4204bSDavid van Moolenbroek 	/* Set up the message passed to the driver. */
12789a4204bSDavid van Moolenbroek 	memset(&dev_mess, 0, sizeof(dev_mess));
12889a4204bSDavid van Moolenbroek 	dev_mess.m_type = BDEV_CLOSE;
12989a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.minor = minor_dev;
13089a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.id = 0;
13189a4204bSDavid van Moolenbroek 
13289a4204bSDavid van Moolenbroek 	/* Call the driver. */
13389a4204bSDavid van Moolenbroek 	r = bdev_sendrec(dmap[major_dev].dmap_driver, &dev_mess);
13489a4204bSDavid van Moolenbroek 	if (r != OK)
13589a4204bSDavid van Moolenbroek 		return r;
13689a4204bSDavid van Moolenbroek 
13789a4204bSDavid van Moolenbroek 	return dev_mess.m_lblockdriver_lbdev_reply.status;
13889a4204bSDavid van Moolenbroek }
13989a4204bSDavid van Moolenbroek 
14089a4204bSDavid van Moolenbroek /*
14189a4204bSDavid van Moolenbroek  * Perform an I/O control operation on a block device.
14289a4204bSDavid van Moolenbroek  */
14389a4204bSDavid van Moolenbroek int
bdev_ioctl(dev_t dev,endpoint_t proc_e,unsigned long req,vir_bytes buf)14489a4204bSDavid van Moolenbroek bdev_ioctl(dev_t dev, endpoint_t proc_e, unsigned long req, vir_bytes buf)
14589a4204bSDavid van Moolenbroek {
14689a4204bSDavid van Moolenbroek 	struct dmap *dp;
14789a4204bSDavid van Moolenbroek 	cp_grant_id_t grant;
14889a4204bSDavid van Moolenbroek 	message dev_mess;
14989a4204bSDavid van Moolenbroek 	devmajor_t major_dev;
15089a4204bSDavid van Moolenbroek 	devminor_t minor_dev;
15189a4204bSDavid van Moolenbroek 	int r;
15289a4204bSDavid van Moolenbroek 
15389a4204bSDavid van Moolenbroek 	major_dev = major(dev);
15489a4204bSDavid van Moolenbroek 	minor_dev = minor(dev);
15589a4204bSDavid van Moolenbroek 
15689a4204bSDavid van Moolenbroek 	/* Determine driver dmap. */
15789a4204bSDavid van Moolenbroek 	dp = &dmap[major_dev];
15889a4204bSDavid van Moolenbroek 	if (dp->dmap_driver == NONE) {
15989a4204bSDavid van Moolenbroek 		printf("VFS: bdev_ioctl: no driver for major %d\n", major_dev);
16089a4204bSDavid van Moolenbroek 		return ENXIO;
16189a4204bSDavid van Moolenbroek 	}
16289a4204bSDavid van Moolenbroek 
16389a4204bSDavid van Moolenbroek 	/* Set up a grant if necessary. */
16489a4204bSDavid van Moolenbroek 	grant = make_ioctl_grant(dp->dmap_driver, proc_e, buf, req);
16589a4204bSDavid van Moolenbroek 
16689a4204bSDavid van Moolenbroek 	/* Set up the message passed to the driver. */
16789a4204bSDavid van Moolenbroek 	memset(&dev_mess, 0, sizeof(dev_mess));
16889a4204bSDavid van Moolenbroek 	dev_mess.m_type = BDEV_IOCTL;
16989a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.minor = minor_dev;
17089a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.request = req;
17189a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.grant = grant;
17289a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.user = proc_e;
17389a4204bSDavid van Moolenbroek 	dev_mess.m_lbdev_lblockdriver_msg.id = 0;
17489a4204bSDavid van Moolenbroek 
17589a4204bSDavid van Moolenbroek 	/* Call the driver. */
17689a4204bSDavid van Moolenbroek 	r = bdev_sendrec(dp->dmap_driver, &dev_mess);
17789a4204bSDavid van Moolenbroek 
17889a4204bSDavid van Moolenbroek 	/* Clean up. */
17989a4204bSDavid van Moolenbroek 	if (GRANT_VALID(grant)) cpf_revoke(grant);
18089a4204bSDavid van Moolenbroek 
18189a4204bSDavid van Moolenbroek 	/* Return the result. */
18289a4204bSDavid van Moolenbroek 	if (r != OK)
18389a4204bSDavid van Moolenbroek 		return r;
18489a4204bSDavid van Moolenbroek 
18589a4204bSDavid van Moolenbroek 	return dev_mess.m_lblockdriver_lbdev_reply.status;
18689a4204bSDavid van Moolenbroek }
18789a4204bSDavid van Moolenbroek 
18889a4204bSDavid van Moolenbroek /*
18989a4204bSDavid van Moolenbroek  * A block driver has results for a call.  There must be a thread waiting for
19089a4204bSDavid van Moolenbroek  * these results; wake it up.  This function MUST NOT block its calling thread.
19189a4204bSDavid van Moolenbroek  */
19289a4204bSDavid van Moolenbroek void
bdev_reply(void)19389a4204bSDavid van Moolenbroek bdev_reply(void)
19489a4204bSDavid van Moolenbroek {
19589a4204bSDavid van Moolenbroek 	struct worker_thread *wp;
19689a4204bSDavid van Moolenbroek 	struct dmap *dp;
19789a4204bSDavid van Moolenbroek 
198*e3b8d4bbSDavid van Moolenbroek 	if ((dp = get_dmap_by_endpt(who_e)) == NULL) {
19989a4204bSDavid van Moolenbroek 		printf("VFS: ignoring block dev reply from unknown driver "
20089a4204bSDavid van Moolenbroek 		    "%d\n", who_e);
20189a4204bSDavid van Moolenbroek 		return;
20289a4204bSDavid van Moolenbroek 	}
20389a4204bSDavid van Moolenbroek 
20489a4204bSDavid van Moolenbroek 	if (dp->dmap_servicing == INVALID_THREAD) {
20589a4204bSDavid van Moolenbroek 		printf("VFS: ignoring spurious block dev reply from %d\n",
20689a4204bSDavid van Moolenbroek 		    who_e);
20789a4204bSDavid van Moolenbroek 		return;
20889a4204bSDavid van Moolenbroek 	}
20989a4204bSDavid van Moolenbroek 
21089a4204bSDavid van Moolenbroek 	wp = worker_get(dp->dmap_servicing);
21189a4204bSDavid van Moolenbroek 	if (wp == NULL || wp->w_task != who_e || wp->w_drv_sendrec == NULL) {
21289a4204bSDavid van Moolenbroek 		printf("VFS: no worker thread waiting for a reply from %d\n",
21389a4204bSDavid van Moolenbroek 		    who_e);
21489a4204bSDavid van Moolenbroek 		return;
21589a4204bSDavid van Moolenbroek 	}
21689a4204bSDavid van Moolenbroek 
21789a4204bSDavid van Moolenbroek 	*wp->w_drv_sendrec = m_in;
21889a4204bSDavid van Moolenbroek 	wp->w_drv_sendrec = NULL;
21989a4204bSDavid van Moolenbroek 	worker_signal(wp);
22089a4204bSDavid van Moolenbroek }
22189a4204bSDavid van Moolenbroek 
22289a4204bSDavid van Moolenbroek /*
22389a4204bSDavid van Moolenbroek  * A new block device driver has been mapped in.  This may affect both mounted
22489a4204bSDavid van Moolenbroek  * file systems and open block-special files.
22589a4204bSDavid van Moolenbroek  */
22689a4204bSDavid van Moolenbroek void
bdev_up(devmajor_t maj)22789a4204bSDavid van Moolenbroek bdev_up(devmajor_t maj)
22889a4204bSDavid van Moolenbroek {
22989a4204bSDavid van Moolenbroek 	int r, found, bits;
23089a4204bSDavid van Moolenbroek 	struct filp *rfilp;
23189a4204bSDavid van Moolenbroek 	struct vmnt *vmp;
23289a4204bSDavid van Moolenbroek 	struct vnode *vp;
23389a4204bSDavid van Moolenbroek 	char *label;
23489a4204bSDavid van Moolenbroek 
23589a4204bSDavid van Moolenbroek 	if (maj < 0 || maj >= NR_DEVICES) panic("VFS: out-of-bound major");
23689a4204bSDavid van Moolenbroek 	label = dmap[maj].dmap_label;
23789a4204bSDavid van Moolenbroek 	found = 0;
23889a4204bSDavid van Moolenbroek 
23989a4204bSDavid van Moolenbroek 	/*
24089a4204bSDavid van Moolenbroek 	 * For each block-special file that was previously opened on the
24189a4204bSDavid van Moolenbroek 	 * affected device, we need to reopen it on the new driver.
24289a4204bSDavid van Moolenbroek 	 */
24389a4204bSDavid van Moolenbroek 	for (rfilp = filp; rfilp < &filp[NR_FILPS]; rfilp++) {
24489a4204bSDavid van Moolenbroek 		if (rfilp->filp_count < 1) continue;
24589a4204bSDavid van Moolenbroek 		if ((vp = rfilp->filp_vno) == NULL) continue;
24689a4204bSDavid van Moolenbroek 		if (major(vp->v_sdev) != maj) continue;
24789a4204bSDavid van Moolenbroek 		if (!S_ISBLK(vp->v_mode)) continue;
24889a4204bSDavid van Moolenbroek 
24989a4204bSDavid van Moolenbroek 		/* Reopen the device on the driver, once per filp. */
25089a4204bSDavid van Moolenbroek 		bits = rfilp->filp_mode & (R_BIT | W_BIT);
25189a4204bSDavid van Moolenbroek 		if ((r = bdev_open(vp->v_sdev, bits)) != OK) {
25289a4204bSDavid van Moolenbroek 			printf("VFS: mounted dev %d/%d re-open failed: %d\n",
25389a4204bSDavid van Moolenbroek 			    maj, minor(vp->v_sdev), r);
25489a4204bSDavid van Moolenbroek 			dmap[maj].dmap_recovering = 0;
25589a4204bSDavid van Moolenbroek 			return; /* Give up entirely */
25689a4204bSDavid van Moolenbroek 		}
25789a4204bSDavid van Moolenbroek 
25889a4204bSDavid van Moolenbroek 		found = 1;
25989a4204bSDavid van Moolenbroek 	}
26089a4204bSDavid van Moolenbroek 
26189a4204bSDavid van Moolenbroek 	/* Tell each affected mounted file system about the new endpoint. */
26289a4204bSDavid van Moolenbroek 	for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) {
26389a4204bSDavid van Moolenbroek 		if (major(vmp->m_dev) != maj) continue;
26489a4204bSDavid van Moolenbroek 
26589a4204bSDavid van Moolenbroek 		/* Send the driver label to the mounted file system. */
26689a4204bSDavid van Moolenbroek 		if (req_newdriver(vmp->m_fs_e, vmp->m_dev, label) != OK)
26789a4204bSDavid van Moolenbroek 			printf("VFS: error sending new driver label to %d\n",
26889a4204bSDavid van Moolenbroek 			    vmp->m_fs_e);
26989a4204bSDavid van Moolenbroek 	}
27089a4204bSDavid van Moolenbroek 
27189a4204bSDavid van Moolenbroek 	/*
27289a4204bSDavid van Moolenbroek 	 * If any block-special file was open for this major at all, also
27389a4204bSDavid van Moolenbroek 	 * inform the root file system about the new driver.  We do this even
27489a4204bSDavid van Moolenbroek 	 * if the block-special file is linked to another mounted file system,
27589a4204bSDavid van Moolenbroek 	 * merely because it is more work to check for that case.
27689a4204bSDavid van Moolenbroek 	 */
27789a4204bSDavid van Moolenbroek 	if (found) {
27889a4204bSDavid van Moolenbroek 		if (req_newdriver(ROOT_FS_E, makedev(maj, 0), label) != OK)
27989a4204bSDavid van Moolenbroek 			printf("VFS: error sending new driver label to %d\n",
28089a4204bSDavid van Moolenbroek 			    ROOT_FS_E);
28189a4204bSDavid van Moolenbroek 	}
28289a4204bSDavid van Moolenbroek }
283