819 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			819 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* When a needed block is not in the cache, it must be fetched from the disk.
 | 
						|
 * Special character files also require I/O.  The routines for these are here.
 | 
						|
 *
 | 
						|
 * The entry points in this file are:
 | 
						|
 *   cdev_open:   open a character device
 | 
						|
 *   cdev_close:  close a character device
 | 
						|
 *   cdev_io:     initiate a read, write, or ioctl to a character device
 | 
						|
 *   cdev_select: initiate a select call on a device
 | 
						|
 *   cdev_cancel: cancel an I/O request, blocking until it has been cancelled
 | 
						|
 *   cdev_reply:  process the result of a character driver request
 | 
						|
 *   bdev_open:   open a block device
 | 
						|
 *   bdev_close:  close a block device
 | 
						|
 *   bdev_reply:  process the result of a block driver request
 | 
						|
 *   bdev_up:     a block driver has been mapped in
 | 
						|
 *   do_ioctl:    perform the IOCTL system call
 | 
						|
 */
 | 
						|
 | 
						|
#include "fs.h"
 | 
						|
#include <string.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <assert.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <sys/ttycom.h>
 | 
						|
#include <minix/callnr.h>
 | 
						|
#include <minix/com.h>
 | 
						|
#include <minix/endpoint.h>
 | 
						|
#include <minix/ioctl.h>
 | 
						|
#include <minix/u64.h>
 | 
						|
#include "file.h"
 | 
						|
#include "scratchpad.h"
 | 
						|
#include "dmap.h"
 | 
						|
#include <minix/vfsif.h>
 | 
						|
#include "vnode.h"
 | 
						|
#include "vmnt.h"
 | 
						|
 | 
						|
static int cdev_opcl(int op, dev_t dev, int flags);
 | 
						|
static int block_io(endpoint_t driver_e, message *mess_ptr);
 | 
						|
static cp_grant_id_t make_grant(endpoint_t driver_e, endpoint_t user_e, int op,
 | 
						|
	void *buf, unsigned long size);
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				bdev_open				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int bdev_open(dev_t dev, int access)
 | 
						|
{
 | 
						|
/* Open a block device. */
 | 
						|
  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;
 | 
						|
 | 
						|
  memset(&dev_mess, 0, sizeof(dev_mess));
 | 
						|
  dev_mess.m_type = BDEV_OPEN;
 | 
						|
  dev_mess.BDEV_MINOR = minor_dev;
 | 
						|
  dev_mess.BDEV_ACCESS = 0;
 | 
						|
  if (access & R_BIT) dev_mess.BDEV_ACCESS |= BDEV_R_BIT;
 | 
						|
  if (access & W_BIT) dev_mess.BDEV_ACCESS |= BDEV_W_BIT;
 | 
						|
  dev_mess.BDEV_ID = 0;
 | 
						|
 | 
						|
  /* Call the task. */
 | 
						|
  r = block_io(dmap[major_dev].dmap_driver, &dev_mess);
 | 
						|
  if (r != OK)
 | 
						|
	return r;
 | 
						|
 | 
						|
  return dev_mess.BDEV_STATUS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				bdev_close				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int bdev_close(dev_t dev)
 | 
						|
{
 | 
						|
/* Close a block device. */
 | 
						|
  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;
 | 
						|
 | 
						|
  memset(&dev_mess, 0, sizeof(dev_mess));
 | 
						|
  dev_mess.m_type = BDEV_CLOSE;
 | 
						|
  dev_mess.BDEV_MINOR = minor_dev;
 | 
						|
  dev_mess.BDEV_ID = 0;
 | 
						|
 | 
						|
  r = block_io(dmap[major_dev].dmap_driver, &dev_mess);
 | 
						|
  if (r != OK)
 | 
						|
	return r;
 | 
						|
 | 
						|
  return dev_mess.BDEV_STATUS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				bdev_ioctl				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static int bdev_ioctl(dev_t dev, endpoint_t proc_e, unsigned long req,
 | 
						|
	void *buf)
 | 
						|
{
 | 
						|
/* Perform an I/O control operation on a block device. */
 | 
						|
  struct dmap *dp;
 | 
						|
  cp_grant_id_t gid;
 | 
						|
  message dev_mess;
 | 
						|
  devmajor_t major_dev;
 | 
						|
  devminor_t minor_dev;
 | 
						|
  int r;
 | 
						|
 | 
						|
  major_dev = major(dev);
 | 
						|
  minor_dev = minor(dev);
 | 
						|
 | 
						|
  /* Determine task 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. */
 | 
						|
  gid = make_grant(dp->dmap_driver, proc_e, BDEV_IOCTL, buf, req);
 | 
						|
 | 
						|
  /* Set up the message passed to the task. */
 | 
						|
  memset(&dev_mess, 0, sizeof(dev_mess));
 | 
						|
 | 
						|
  dev_mess.m_type = BDEV_IOCTL;
 | 
						|
  dev_mess.BDEV_MINOR = minor_dev;
 | 
						|
  dev_mess.BDEV_REQUEST = req;
 | 
						|
  dev_mess.BDEV_GRANT = gid;
 | 
						|
  dev_mess.BDEV_USER = proc_e;
 | 
						|
  dev_mess.BDEV_ID = 0;
 | 
						|
 | 
						|
  /* Call the task. */
 | 
						|
  r = block_io(dp->dmap_driver, &dev_mess);
 | 
						|
 | 
						|
  /* Clean up. */
 | 
						|
  if (GRANT_VALID(gid)) cpf_revoke(gid);
 | 
						|
 | 
						|
  /* Return the result. */
 | 
						|
  if (r != OK)
 | 
						|
	return(r);
 | 
						|
 | 
						|
  return(dev_mess.BDEV_STATUS);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				make_grant				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static cp_grant_id_t make_grant(endpoint_t driver_e, endpoint_t user_e, int op,
 | 
						|
	void *buf, unsigned long bytes)
 | 
						|
{
 | 
						|
/* Create a magic grant for the given operation and buffer. */
 | 
						|
  cp_grant_id_t gid;
 | 
						|
  int access;
 | 
						|
  size_t size;
 | 
						|
 | 
						|
  switch (op) {
 | 
						|
  case CDEV_READ:
 | 
						|
  case CDEV_WRITE:
 | 
						|
	gid = cpf_grant_magic(driver_e, user_e, (vir_bytes) buf,
 | 
						|
		(size_t) bytes, op == CDEV_READ ? CPF_WRITE : CPF_READ);
 | 
						|
	break;
 | 
						|
 | 
						|
  case CDEV_IOCTL:
 | 
						|
  case BDEV_IOCTL:
 | 
						|
	/* For IOCTLs, the bytes parameter contains the IOCTL request.
 | 
						|
	 * This request encodes the requested access method and buffer size.
 | 
						|
	 */
 | 
						|
	access = 0;
 | 
						|
	if(_MINIX_IOCTL_IOR(bytes)) access |= CPF_WRITE;
 | 
						|
	if(_MINIX_IOCTL_IOW(bytes)) access |= CPF_READ;
 | 
						|
	if(_MINIX_IOCTL_BIG(bytes))
 | 
						|
		size = _MINIX_IOCTL_SIZE_BIG(bytes);
 | 
						|
	else
 | 
						|
		size = _MINIX_IOCTL_SIZE(bytes);
 | 
						|
 | 
						|
	/* Grant access to the buffer even if no I/O happens with the ioctl,
 | 
						|
	 * although now that we no longer identify responses based on grants,
 | 
						|
	 * this is not strictly necessary.
 | 
						|
	 */
 | 
						|
	gid = cpf_grant_magic(driver_e, user_e, (vir_bytes) buf, size, access);
 | 
						|
	break;
 | 
						|
 | 
						|
  default:
 | 
						|
	panic("VFS: unknown operation %d", op);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!GRANT_VALID(gid))
 | 
						|
	panic("VFS: cpf_grant_magic failed");
 | 
						|
 | 
						|
  return gid;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cdev_get				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static struct dmap *cdev_get(dev_t dev, devminor_t *minor_dev)
 | 
						|
{
 | 
						|
/* Obtain the dmap structure for the given device, if a valid driver exists for
 | 
						|
 * the major device. Perform redirection for CTTY_MAJOR.
 | 
						|
 */
 | 
						|
  devmajor_t major_dev;
 | 
						|
  struct dmap *dp;
 | 
						|
  int slot;
 | 
						|
 | 
						|
  /* First cover one special case: /dev/tty, the magic device that translates
 | 
						|
   * to the controlling tty.
 | 
						|
   */
 | 
						|
  if (major(dev) == CTTY_MAJOR) {
 | 
						|
	/* No controlling terminal? Fail the request. */
 | 
						|
	if (fp->fp_tty == 0) return(NULL);
 | 
						|
 | 
						|
	/* Substitute the controlling terminal device. */
 | 
						|
	dev = fp->fp_tty;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Determine task dmap. */
 | 
						|
  major_dev = major(dev);
 | 
						|
  if (major_dev < 0 || major_dev >= NR_DEVICES) return(NULL);
 | 
						|
 | 
						|
  dp = &dmap[major_dev];
 | 
						|
 | 
						|
  /* See if driver is roughly valid. */
 | 
						|
  if (dp->dmap_driver == NONE) return(NULL);
 | 
						|
 | 
						|
  if (isokendpt(dp->dmap_driver, &slot) != OK) {
 | 
						|
	printf("VFS: cdev_get: old driver for major %x (%d)\n", major_dev,
 | 
						|
		dp->dmap_driver);
 | 
						|
	return(NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Also return the (possibly redirected) minor number. */
 | 
						|
  *minor_dev = minor(dev);
 | 
						|
  return dp;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cdev_io					     *
 | 
						|
 *===========================================================================*/
 | 
						|
int cdev_io(
 | 
						|
  int op,			/* CDEV_READ, CDEV_WRITE, or CDEV_IOCTL */
 | 
						|
  dev_t dev,			/* major-minor device number */
 | 
						|
  endpoint_t proc_e,		/* in whose address space is buf? */
 | 
						|
  void *buf,			/* virtual address of the buffer */
 | 
						|
  off_t pos,			/* byte position */
 | 
						|
  unsigned long bytes,		/* how many bytes to transfer, or request */
 | 
						|
  int flags			/* special flags, like O_NONBLOCK */
 | 
						|
)
 | 
						|
{
 | 
						|
/* Initiate a read, write, or ioctl to a character device. */
 | 
						|
  devminor_t minor_dev;
 | 
						|
  struct dmap *dp;
 | 
						|
  message dev_mess;
 | 
						|
  cp_grant_id_t gid;
 | 
						|
  int r;
 | 
						|
 | 
						|
  assert(op == CDEV_READ || op == CDEV_WRITE || op == CDEV_IOCTL);
 | 
						|
 | 
						|
  /* Determine task map. */
 | 
						|
  if ((dp = cdev_get(dev, &minor_dev)) == NULL)
 | 
						|
	return(EIO);
 | 
						|
 | 
						|
  /* Handle TIOCSCTTY ioctl: set controlling tty.
 | 
						|
   * TODO: cleaner implementation work in progress.
 | 
						|
   */
 | 
						|
  if (op == CDEV_IOCTL && bytes == TIOCSCTTY && major(dev) == TTY_MAJOR) {
 | 
						|
       fp->fp_tty = dev;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Create a grant for the buffer provided by the user process. */
 | 
						|
  gid = make_grant(dp->dmap_driver, proc_e, op, buf, bytes);
 | 
						|
 | 
						|
  /* Set up the rest of the message that will be sent to the driver. */
 | 
						|
  memset(&dev_mess, 0, sizeof(dev_mess));
 | 
						|
  dev_mess.m_type = op;
 | 
						|
  dev_mess.CDEV_MINOR = minor_dev;
 | 
						|
  if (op == CDEV_IOCTL) {
 | 
						|
	dev_mess.CDEV_REQUEST = bytes;
 | 
						|
	dev_mess.CDEV_USER = proc_e;
 | 
						|
  } else {
 | 
						|
	dev_mess.CDEV_POS = pos;
 | 
						|
	dev_mess.CDEV_COUNT = (size_t) bytes;
 | 
						|
  }
 | 
						|
  dev_mess.CDEV_ID = proc_e;
 | 
						|
  dev_mess.CDEV_GRANT = gid;
 | 
						|
  dev_mess.CDEV_FLAGS = 0;
 | 
						|
  if (flags & O_NONBLOCK)
 | 
						|
	  dev_mess.CDEV_FLAGS |= CDEV_NONBLOCK;
 | 
						|
 | 
						|
  /* Send the request to the driver. */
 | 
						|
  if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK)
 | 
						|
	panic("VFS: asynsend in cdev_io failed: %d", r);
 | 
						|
 | 
						|
  /* Suspend the calling process until a reply arrives. */
 | 
						|
  wait_for(dp->dmap_driver);
 | 
						|
  assert(!GRANT_VALID(fp->fp_grant));
 | 
						|
  fp->fp_grant = gid;	/* revoke this when unsuspended. */
 | 
						|
 | 
						|
  return SUSPEND;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cdev_clone				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static int cdev_clone(dev_t dev, devminor_t new_minor)
 | 
						|
{
 | 
						|
/* A new minor device number has been returned. Request PFS to create a
 | 
						|
 * temporary device file to hold it.
 | 
						|
 */
 | 
						|
  struct vnode *vp;
 | 
						|
  struct node_details res;
 | 
						|
  int r;
 | 
						|
 | 
						|
  /* Device number of the new device. */
 | 
						|
  dev = makedev(major(dev), new_minor);
 | 
						|
 | 
						|
  /* Issue request */
 | 
						|
  r = req_newnode(PFS_PROC_NR, fp->fp_effuid, fp->fp_effgid,
 | 
						|
      ALL_MODES | I_CHAR_SPECIAL, dev, &res);
 | 
						|
  if (r != OK) {
 | 
						|
	(void) cdev_opcl(CDEV_CLOSE, dev, 0);
 | 
						|
	return r;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Drop old node and use the new values */
 | 
						|
  if ((vp = get_free_vnode()) == NULL) {
 | 
						|
	req_putnode(PFS_PROC_NR, res.inode_nr, 1); /* is this right? */
 | 
						|
	(void) cdev_opcl(CDEV_CLOSE, dev, 0);
 | 
						|
	return(err_code);
 | 
						|
  }
 | 
						|
  lock_vnode(vp, VNODE_OPCL);
 | 
						|
 | 
						|
  assert(fp->fp_filp[scratch(fp).file.fd_nr] != NULL);
 | 
						|
  unlock_vnode(fp->fp_filp[scratch(fp).file.fd_nr]->filp_vno);
 | 
						|
  put_vnode(fp->fp_filp[scratch(fp).file.fd_nr]->filp_vno);
 | 
						|
 | 
						|
  vp->v_fs_e = res.fs_e;
 | 
						|
  vp->v_vmnt = NULL;
 | 
						|
  vp->v_dev = NO_DEV;
 | 
						|
  vp->v_fs_e = res.fs_e;
 | 
						|
  vp->v_inode_nr = res.inode_nr;
 | 
						|
  vp->v_mode = res.fmode;
 | 
						|
  vp->v_sdev = dev;
 | 
						|
  vp->v_fs_count = 1;
 | 
						|
  vp->v_ref_count = 1;
 | 
						|
  fp->fp_filp[scratch(fp).file.fd_nr]->filp_vno = vp;
 | 
						|
 | 
						|
  return OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cdev_opcl				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static int cdev_opcl(
 | 
						|
  int op,			/* operation, CDEV_OPEN or CDEV_CLOSE */
 | 
						|
  dev_t dev,			/* device to open or close */
 | 
						|
  int flags			/* mode bits and flags */
 | 
						|
)
 | 
						|
{
 | 
						|
/* Open or close a character device. */
 | 
						|
  devminor_t minor_dev, new_minor;
 | 
						|
  struct dmap *dp;
 | 
						|
  struct fproc *rfp;
 | 
						|
  message dev_mess;
 | 
						|
  int r, r2;
 | 
						|
 | 
						|
  assert(op == CDEV_OPEN || op == CDEV_CLOSE);
 | 
						|
 | 
						|
  /* Determine task dmap. */
 | 
						|
  if ((dp = cdev_get(dev, &minor_dev)) == NULL)
 | 
						|
	return(ENXIO);
 | 
						|
 | 
						|
  /* CTTY exception: do not actually send the open/close request for /dev/tty
 | 
						|
   * to the driver.  This avoids the case that the actual device will remain
 | 
						|
   * open forever if the process calls setsid() after opening /dev/tty.
 | 
						|
   */
 | 
						|
  if (major(dev) == CTTY_MAJOR) return(OK);
 | 
						|
 | 
						|
  /* Add O_NOCTTY to the access flags if this process is not a session leader,
 | 
						|
   * or if it already has a controlling tty, or if it is someone else's
 | 
						|
   * controlling tty.  For performance reasons, only search the full process
 | 
						|
   * table if this driver has set controlling ttys before.
 | 
						|
   */
 | 
						|
  if (!(fp->fp_flags & FP_SESLDR) || fp->fp_tty != 0) {
 | 
						|
	flags |= O_NOCTTY;
 | 
						|
  } else if (!(flags & O_NOCTTY) && dp->dmap_seen_tty) {
 | 
						|
	for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++)
 | 
						|
		if (rfp->fp_pid != PID_FREE && rfp->fp_tty == dev)
 | 
						|
			flags |= O_NOCTTY;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Prepare the request message. */
 | 
						|
  memset(&dev_mess, 0, sizeof(dev_mess));
 | 
						|
 | 
						|
  dev_mess.m_type = op;
 | 
						|
  dev_mess.CDEV_MINOR = minor_dev;
 | 
						|
  dev_mess.CDEV_ID = who_e;
 | 
						|
  if (op == CDEV_OPEN) {
 | 
						|
	dev_mess.CDEV_USER = who_e;
 | 
						|
	dev_mess.CDEV_ACCESS = 0;
 | 
						|
	if (flags & R_BIT)	dev_mess.CDEV_ACCESS |= CDEV_R_BIT;
 | 
						|
	if (flags & W_BIT)	dev_mess.CDEV_ACCESS |= CDEV_W_BIT;
 | 
						|
	if (flags & O_NOCTTY)	dev_mess.CDEV_ACCESS |= CDEV_NOCTTY;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Send the request to the driver. */
 | 
						|
  if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK)
 | 
						|
	panic("VFS: asynsend in cdev_opcl failed: %d", r);
 | 
						|
 | 
						|
  /* Block the thread waiting for a reply. */
 | 
						|
  fp->fp_task = dp->dmap_driver;
 | 
						|
  self->w_task = dp->dmap_driver;
 | 
						|
  self->w_drv_sendrec = &dev_mess;
 | 
						|
 | 
						|
  worker_wait();
 | 
						|
 | 
						|
  self->w_task = NONE;
 | 
						|
  self->w_drv_sendrec = NULL;
 | 
						|
 | 
						|
  /* Process the reply. */
 | 
						|
  r = dev_mess.CDEV_STATUS;
 | 
						|
 | 
						|
  if (op == CDEV_OPEN && r >= 0) {
 | 
						|
	/* Some devices need special processing upon open. Such a device is
 | 
						|
	 * "cloned", i.e. on a succesful open it is replaced by a new device
 | 
						|
	 * with a new unique minor device number. This new device number
 | 
						|
	 * identifies a new object (such as a new network connection) that has
 | 
						|
	 * been allocated within a driver.
 | 
						|
	 */
 | 
						|
	if (r & CDEV_CLONED) {
 | 
						|
		new_minor = r & ~(CDEV_CLONED | CDEV_CTTY);
 | 
						|
		if ((r2 = cdev_clone(dev, new_minor)) < 0)
 | 
						|
			return(r2);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Did this call make the tty the controlling tty? */
 | 
						|
	if (r & CDEV_CTTY) {
 | 
						|
		fp->fp_tty = dev;
 | 
						|
		dp->dmap_seen_tty = TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
	r = OK;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Return the result from the driver. */
 | 
						|
  return(r);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cdev_open				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int cdev_open(dev_t dev, int flags)
 | 
						|
{
 | 
						|
/* Open a character device. */
 | 
						|
 | 
						|
  return cdev_opcl(CDEV_OPEN, dev, flags);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cdev_close				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int cdev_close(dev_t dev)
 | 
						|
{
 | 
						|
/* Close a character device. */
 | 
						|
 | 
						|
  return cdev_opcl(CDEV_CLOSE, dev, 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				do_ioctl				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int do_ioctl(void)
 | 
						|
{
 | 
						|
/* Perform the ioctl(2) system call. */
 | 
						|
  unsigned long ioctlrequest;
 | 
						|
  int r = OK;
 | 
						|
  struct filp *f;
 | 
						|
  register struct vnode *vp;
 | 
						|
  dev_t dev;
 | 
						|
  void *argx;
 | 
						|
 | 
						|
  scratch(fp).file.fd_nr = job_m_in.VFS_IOCTL_FD;
 | 
						|
  ioctlrequest = job_m_in.VFS_IOCTL_REQ;
 | 
						|
  argx = job_m_in.VFS_IOCTL_ARG;
 | 
						|
 | 
						|
  if ((f = get_filp(scratch(fp).file.fd_nr, VNODE_READ)) == NULL)
 | 
						|
	return(err_code);
 | 
						|
  vp = f->filp_vno;		/* get vnode pointer */
 | 
						|
  if (!S_ISCHR(vp->v_mode) && !S_ISBLK(vp->v_mode)) {
 | 
						|
	r = ENOTTY;
 | 
						|
  }
 | 
						|
 | 
						|
  if (r == OK) {
 | 
						|
	dev = vp->v_sdev;
 | 
						|
 | 
						|
	if (S_ISBLK(vp->v_mode)) {
 | 
						|
		f->filp_ioctl_fp = fp;
 | 
						|
 | 
						|
		r = bdev_ioctl(dev, who_e, ioctlrequest, argx);
 | 
						|
 | 
						|
		f->filp_ioctl_fp = NULL;
 | 
						|
	} else
 | 
						|
		r = cdev_io(CDEV_IOCTL, dev, who_e, argx, 0, ioctlrequest,
 | 
						|
			f->filp_flags);
 | 
						|
  }
 | 
						|
 | 
						|
  unlock_filp(f);
 | 
						|
 | 
						|
  return(r);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cdev_select				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int cdev_select(dev_t dev, int ops)
 | 
						|
{
 | 
						|
/* Initiate a select call on a device. Return OK iff the request was sent. */
 | 
						|
  devminor_t minor_dev;
 | 
						|
  message dev_mess;
 | 
						|
  struct dmap *dp;
 | 
						|
  int r;
 | 
						|
 | 
						|
  /* Determine task dmap. */
 | 
						|
  if ((dp = cdev_get(dev, &minor_dev)) == NULL)
 | 
						|
	return(EIO);
 | 
						|
 | 
						|
  /* Prepare the request message. */
 | 
						|
  memset(&dev_mess, 0, sizeof(dev_mess));
 | 
						|
 | 
						|
  dev_mess.m_type = CDEV_SELECT;
 | 
						|
  dev_mess.CDEV_MINOR = minor_dev;
 | 
						|
  dev_mess.CDEV_OPS = ops;
 | 
						|
 | 
						|
  /* Send the request to the driver. */
 | 
						|
  if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK)
 | 
						|
	panic("VFS: asynsend in cdev_select failed: %d", r);
 | 
						|
 | 
						|
  return(OK);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cdev_cancel				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int cdev_cancel(dev_t dev)
 | 
						|
{
 | 
						|
/* Cancel an I/O request, blocking until it has been cancelled. */
 | 
						|
  devminor_t minor_dev;
 | 
						|
  message dev_mess;
 | 
						|
  struct dmap *dp;
 | 
						|
  int r;
 | 
						|
 | 
						|
  /* Determine task dmap. */
 | 
						|
  if ((dp = cdev_get(dev, &minor_dev)) == NULL)
 | 
						|
	return(EIO);
 | 
						|
 | 
						|
  /* Prepare the request message. */
 | 
						|
  memset(&dev_mess, 0, sizeof(dev_mess));
 | 
						|
 | 
						|
  dev_mess.m_type = CDEV_CANCEL;
 | 
						|
  dev_mess.CDEV_MINOR = minor_dev;
 | 
						|
  dev_mess.CDEV_ID = fp->fp_endpoint;
 | 
						|
 | 
						|
  /* Send the request to the driver. */
 | 
						|
  if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK)
 | 
						|
	panic("VFS: asynsend in cdev_cancel failed: %d", r);
 | 
						|
 | 
						|
  /* Suspend this thread until we have received the response. */
 | 
						|
  fp->fp_task = dp->dmap_driver;
 | 
						|
  self->w_task = dp->dmap_driver;
 | 
						|
  self->w_drv_sendrec = &dev_mess;
 | 
						|
 | 
						|
  worker_wait();
 | 
						|
 | 
						|
  self->w_task = NONE;
 | 
						|
  self->w_drv_sendrec = NULL;
 | 
						|
 | 
						|
  /* Clean up and return the result (note: the request may have completed). */
 | 
						|
  if (GRANT_VALID(fp->fp_grant)) {
 | 
						|
	(void) cpf_revoke(fp->fp_grant);
 | 
						|
	fp->fp_grant = GRANT_INVALID;
 | 
						|
  }
 | 
						|
 | 
						|
  r = dev_mess.CDEV_STATUS;
 | 
						|
  return (r == EAGAIN) ? EINTR : r;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				block_io				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static int block_io(endpoint_t driver_e, message *mess_ptr)
 | 
						|
{
 | 
						|
/* Perform I/O on a block device. The current thread is suspended until a reply
 | 
						|
 * comes in from the driver.
 | 
						|
 */
 | 
						|
  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->BDEV_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: ELOCKED talking to %d\n", driver_e);
 | 
						|
		return(EIO);
 | 
						|
	}
 | 
						|
	panic("block_io: can't send/receive: %d", r);
 | 
						|
  }
 | 
						|
 | 
						|
  return(OK);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				bdev_up					     *
 | 
						|
 *===========================================================================*/
 | 
						|
void bdev_up(devmajor_t maj)
 | 
						|
{
 | 
						|
  /* A new block device driver has been mapped in. This may affect both mounted
 | 
						|
   * file systems and open block-special files.
 | 
						|
   */
 | 
						|
  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 || !(vp = rfilp->filp_vno)) 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 (OK != req_newdriver(vmp->m_fs_e, vmp->m_dev, label))
 | 
						|
		printf("VFS dev_up: 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 (OK != req_newdriver(ROOT_FS_E, makedev(maj, 0), label))
 | 
						|
		printf("VFSdev_up: error sending new driver label to %d\n",
 | 
						|
			ROOT_FS_E);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cdev_generic_reply			     *
 | 
						|
 *===========================================================================*/
 | 
						|
static void cdev_generic_reply(message *m_ptr)
 | 
						|
{
 | 
						|
/* A character driver has results for an open, close, read, write, or ioctl
 | 
						|
 * call (i.e., everything except select). There may be a thread waiting for
 | 
						|
 * these results as part of an ongoing open, close, or (for read/write/ioctl)
 | 
						|
 * cancel call. If so, wake up that thread; if not, send a reply to the
 | 
						|
 * requesting process. This function MUST NOT block its calling thread.
 | 
						|
 */
 | 
						|
  struct fproc *rfp;
 | 
						|
  struct worker_thread *wp;
 | 
						|
  endpoint_t proc_e;
 | 
						|
  int slot;
 | 
						|
 | 
						|
  proc_e = m_ptr->CDEV_ID;
 | 
						|
 | 
						|
  if (m_ptr->CDEV_STATUS == SUSPEND) {
 | 
						|
	printf("VFS: got SUSPEND from %d, not reviving\n", m_ptr->m_source);
 | 
						|
	return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (isokendpt(proc_e, &slot) != OK) {
 | 
						|
	printf("VFS: proc %d from %d not found\n", proc_e, m_ptr->m_source);
 | 
						|
	return;
 | 
						|
  }
 | 
						|
  rfp = &fproc[slot];
 | 
						|
  wp = rfp->fp_worker;
 | 
						|
  if (wp != NULL && wp->w_task == who_e) {
 | 
						|
	assert(!fp_is_blocked(rfp));
 | 
						|
	*wp->w_drv_sendrec = *m_ptr;
 | 
						|
	worker_signal(wp);	/* Continue open/close/cancel */
 | 
						|
  } else if (rfp->fp_blocked_on != FP_BLOCKED_ON_OTHER ||
 | 
						|
		rfp->fp_task != m_ptr->m_source) {
 | 
						|
	/* This would typically be caused by a protocol error, i.e. a driver
 | 
						|
	 * not properly following the character driver protocol rules.
 | 
						|
	 */
 | 
						|
	printf("VFS: proc %d not blocked on %d\n", proc_e, m_ptr->m_source);
 | 
						|
  } else {
 | 
						|
	revive(proc_e, m_ptr->CDEV_STATUS);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			       cdev_reply				     *
 | 
						|
 *===========================================================================*/
 | 
						|
void cdev_reply(void)
 | 
						|
{
 | 
						|
/* A character driver has results for us. */
 | 
						|
 | 
						|
  if (get_dmap(who_e) == NULL) {
 | 
						|
	printf("VFS: ignoring char dev reply from unknown driver %d\n", who_e);
 | 
						|
	return;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (call_nr) {
 | 
						|
  case CDEV_REPLY:
 | 
						|
	cdev_generic_reply(&m_in);
 | 
						|
	break;
 | 
						|
  case CDEV_SEL1_REPLY:
 | 
						|
	select_reply1(m_in.m_source, m_in.CDEV_MINOR, m_in.CDEV_STATUS);
 | 
						|
	break;
 | 
						|
  case CDEV_SEL2_REPLY:
 | 
						|
	select_reply2(m_in.m_source, m_in.CDEV_MINOR, m_in.CDEV_STATUS);
 | 
						|
	break;
 | 
						|
  default:
 | 
						|
	printf("VFS: char driver %u sent unknown reply %x\n", who_e, call_nr);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				bdev_reply				     *
 | 
						|
 *===========================================================================*/
 | 
						|
void bdev_reply(void)
 | 
						|
{
 | 
						|
/* 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.
 | 
						|
 */
 | 
						|
  struct worker_thread *wp;
 | 
						|
  struct dmap *dp;
 | 
						|
 | 
						|
  if ((dp = get_dmap(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) {
 | 
						|
	printf("VFS: no worker thread waiting for a reply from %d\n", who_e);
 | 
						|
	return;
 | 
						|
  }
 | 
						|
 | 
						|
  assert(wp->w_drv_sendrec != NULL);
 | 
						|
  *wp->w_drv_sendrec = m_in;
 | 
						|
  wp->w_drv_sendrec = NULL;
 | 
						|
  worker_signal(wp);
 | 
						|
}
 |