⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 block.c

📁 GNU Mach 微内核源代码, 基于美国卡内基美隆大学的 Mach 研究项目
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Linux block driver support. * * Copyright (C) 1996 The University of Utah and the Computer Systems * Laboratory at the University of Utah (CSL) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * *	Author: Shantanu Goel, University of Utah CSL *//* *  linux/drivers/block/ll_rw_blk.c * * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1994,      Karl Keyte: Added support for disk statistics *//* *  linux/fs/block_dev.c * *  Copyright (C) 1991, 1992  Linus Torvalds *//* *  linux/fs/buffer.c * *  Copyright (C) 1991, 1992  Linus Torvalds */#include <sys/types.h>#include <machine/spl.h>#include <mach/mach_types.h>#include <mach/kern_return.h>#include <mach/mig_errors.h>#include <mach/port.h>#include <mach/vm_param.h>#include <mach/notify.h>#include <ipc/ipc_port.h>#include <ipc/ipc_space.h>#include <vm/vm_map.h>#include <vm/vm_kern.h>#include <vm/vm_page.h>#include <device/device_types.h>#include <device/device_port.h>#include <device/disk_status.h>#include "device_reply.h"#include <linux_emul.h>#define MACH_INCLUDE#include <linux/fs.h>#include <linux/blk.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/fcntl.h>#include <linux/major.h>#include <linux/kdev_t.h>#include <linux/delay.h>#include <linux/malloc.h>#include <linux/hdreg.h>#include <asm/io.h>extern int linux_auto_config;extern int linux_intr_pri;extern int linux_to_mach_error (int);/* This task queue is not used in Mach: just for fixing undefined symbols. */DECLARE_TASK_QUEUE (tq_disk);/* Location of VTOC in units for sectors (512 bytes).  */#define PDLOCATION 29/* Linux kernel variables.  *//* Temporary data allocated on the stack.  */struct temp_data{  struct inode inode;  struct file file;  struct request req;  queue_head_t pages;};/* One of these exists for each   driver associated with a major number.  */struct device_struct{  const char *name;		/* device name */  struct file_operations *fops;	/* operations vector */  int busy:1;			/* driver is being opened/closed */  int want:1;			/* someone wants to open/close driver */  struct gendisk *gd;		/* DOS partition information */  int default_slice;		/* what slice to use when none is given */  struct disklabel **labels;	/* disklabels for each DOS partition */};/* An entry in the Mach name to Linux major number conversion table.  */struct name_map{  const char *name;	/* Mach name for device */  unsigned major;	/* Linux major number */  unsigned unit;	/* Linux unit number */  int read_only;	/* 1 if device is read only */};/* Driver operation table.  */static struct device_struct blkdevs[MAX_BLKDEV];/* Driver request function table.  */struct blk_dev_struct blk_dev[MAX_BLKDEV] ={  { NULL, NULL },		/* 0 no_dev */  { NULL, NULL },		/* 1 dev mem */  { NULL, NULL },		/* 2 dev fd */  { NULL, NULL },		/* 3 dev ide0 or hd */  { NULL, NULL },		/* 4 dev ttyx */  { NULL, NULL },		/* 5 dev tty */  { NULL, NULL },		/* 6 dev lp */  { NULL, NULL },		/* 7 dev pipes */  { NULL, NULL },		/* 8 dev sd */  { NULL, NULL },		/* 9 dev st */  { NULL, NULL },		/* 10 */  { NULL, NULL },		/* 11 */  { NULL, NULL },		/* 12 */  { NULL, NULL },		/* 13 */  { NULL, NULL },		/* 14 */  { NULL, NULL },		/* 15 */  { NULL, NULL },		/* 16 */  { NULL, NULL },		/* 17 */  { NULL, NULL },		/* 18 */  { NULL, NULL },		/* 19 */  { NULL, NULL },		/* 20 */  { NULL, NULL },		/* 21 */  { NULL, NULL }		/* 22 dev ide1 */};/* * blk_size contains the size of all block-devices in units of 1024 byte * sectors: * * blk_size[MAJOR][MINOR] * * if (!blk_size[MAJOR]) then no minor size checking is done. */int *blk_size[MAX_BLKDEV] = { NULL, NULL, };/* * blksize_size contains the size of all block-devices: * * blksize_size[MAJOR][MINOR] * * if (!blksize_size[MAJOR]) then 1024 bytes is assumed. */int *blksize_size[MAX_BLKDEV] = { NULL, NULL, };/* * hardsect_size contains the size of the hardware sector of a device. * * hardsect_size[MAJOR][MINOR] * * if (!hardsect_size[MAJOR]) *		then 512 bytes is assumed. * else *		sector_size is hardsect_size[MAJOR][MINOR] * This is currently set by some scsi device and read by the msdos fs driver * This might be a some uses later. */int *hardsect_size[MAX_BLKDEV] = { NULL, NULL, };/* This specifies how many sectors to read ahead on the disk.   This is unused in Mach.  It is here to make drivers compile.  */int read_ahead[MAX_BLKDEV] = {0, };/* Use to wait on when there are no free requests.   This is unused in Mach.  It is here to make drivers compile.  */struct wait_queue *wait_for_request = NULL;/* Map for allocating device memory.  */extern vm_map_t device_io_map;/* Initialize block drivers.  */intblk_dev_init (){#ifdef CONFIG_BLK_DEV_IDE  ide_init ();#endif#ifdef CONFIG_BLK_DEV_FD  floppy_init ();#else  outb_p (0xc, 0x3f2);#endif  return 0;}/* Return 1 if major number MAJOR corresponds to a disk device.  */static inline intdisk_major (int major){  return (major == IDE0_MAJOR	  || major == IDE1_MAJOR	  || major == IDE2_MAJOR	  || major == IDE3_MAJOR	  || major == SCSI_DISK_MAJOR);}/* Linux kernel block support routines.  *//* Register a driver for major number MAJOR,   with name NAME, and operations vector FOPS.  */intregister_blkdev (unsigned major, const char *name,		 struct file_operations *fops){  int err = 0;  if (major == 0)    {      for (major = MAX_BLKDEV - 1; major > 0; major--)	if (blkdevs[major].fops == NULL)	  goto out;      return -LINUX_EBUSY;    }  if (major >= MAX_BLKDEV)    return -LINUX_EINVAL;  if (blkdevs[major].fops && blkdevs[major].fops != fops)    return -LINUX_EBUSY;out:  blkdevs[major].name = name;  blkdevs[major].fops = fops;  blkdevs[major].busy = 0;  blkdevs[major].want = 0;  blkdevs[major].gd = NULL;  blkdevs[major].default_slice = 0;  blkdevs[major].labels = NULL;  return 0;}/* Unregister the driver associated with   major number MAJOR and having the name NAME.  */intunregister_blkdev (unsigned major, const char *name){  int err;  if (major >= MAX_BLKDEV)    return -LINUX_EINVAL;  if (! blkdevs[major].fops || strcmp (blkdevs[major].name, name))    return -LINUX_EINVAL;  blkdevs[major].fops = NULL;  if (blkdevs[major].labels)    {      assert (blkdevs[major].gd);      kfree ((vm_offset_t) blkdevs[major].labels,	     (sizeof (struct disklabel *)	      * blkdevs[major].gd->max_p * blkdevs[major].gd->max_nr));    }  return 0;}voidset_blocksize (kdev_t dev, int size){  extern int *blksize_size[];  if (! blksize_size[MAJOR (dev)])    return;  switch (size)    {    case 512:    case 1024:    case 2048:    case 4096:      break;    default:      panic ("Invalid blocksize passed to set_blocksize");      break;    }  blksize_size[MAJOR (dev)][MINOR (dev)] = size;}/* Allocate a buffer SIZE bytes long.  */static void *alloc_buffer (int size){  vm_page_t m;  struct temp_data *d;  assert (size <= PAGE_SIZE);  if (! linux_auto_config)    {      while ((m = vm_page_grab (FALSE)) == 0)	VM_PAGE_WAIT (0);      d = current_thread ()->pcb->data;      assert (d);      queue_enter (&d->pages, m, vm_page_t, pageq);      return (void *) m->phys_addr;    }  return (void *) __get_free_pages (GFP_KERNEL, 0, ~0UL);}/* Free buffer P which is SIZE bytes long.  */static voidfree_buffer (void *p, int size){  int i;  struct temp_data *d;  vm_page_t m;  assert (size <= PAGE_SIZE);  if (! linux_auto_config)    {      d = current_thread ()->pcb->data;      assert (d);      queue_iterate (&d->pages, m, vm_page_t, pageq)	{	  if (m->phys_addr == (vm_offset_t) p)	    {	      queue_remove (&d->pages, m, vm_page_t, pageq);	      vm_page_lock_queues ();	      vm_page_free (m);	      vm_page_lock_queues ();	      return;	    }	}      panic ("free_buffer");    }  free_pages ((unsigned long) p, 0);}/* Allocate a buffer of SIZE bytes and   associate it with block number BLOCK of device DEV.  */struct buffer_head *getblk (kdev_t dev, int block, int size){  struct buffer_head *bh;  assert (size <= PAGE_SIZE);  bh = (struct buffer_head *) kalloc (sizeof (struct buffer_head));  if (bh)    {      memset (bh, 0, sizeof (struct buffer_head));      bh->b_data = alloc_buffer (size);      if (! bh->b_data)	{	  kfree ((vm_offset_t) bh, sizeof (struct buffer_head));	  return NULL;	}      bh->b_dev = dev;      bh->b_size = size;      bh->b_state = 1 << BH_Lock;      bh->b_blocknr = block;    }  return bh;}/* Release buffer BH previously allocated by getblk.  */void__brelse (struct buffer_head *bh){  free_buffer (bh->b_data, bh->b_size);  kfree ((vm_offset_t) bh, sizeof (*bh));}/* Allocate a buffer of SIZE bytes and fill it with data   from device DEV starting at block number BLOCK.  */struct buffer_head *bread (kdev_t dev, int block, int size){  int err;  struct buffer_head *bh;  bh = getblk (dev, block, size);  if (bh)    {      ll_rw_block (READ, 1, &bh);      wait_on_buffer (bh);      if (! buffer_uptodate (bh))	{	  __brelse (bh);	  return NULL;	}    }  return bh;}/* Return the block size for device DEV in *BSIZE and   log2(block size) in *BSHIFT.  */static voidget_block_size (kdev_t dev, int *bsize, int *bshift){  int i;  *bsize = BLOCK_SIZE;  if (blksize_size[MAJOR (dev)]      && blksize_size[MAJOR (dev)][MINOR (dev)])    *bsize = blksize_size[MAJOR (dev)][MINOR (dev)];  for (i = *bsize, *bshift = 0; i != 1; i >>= 1, (*bshift)++)    ;}/* Enqueue request REQ on a driver's queue.  */static inline voidenqueue_request (struct request *req){  struct request *tmp;  struct blk_dev_struct *dev;  dev = blk_dev + MAJOR (req->rq_dev);  cli ();  tmp = dev->current_request;  if (! tmp)    {      dev->current_request = req;      (*dev->request_fn) ();      sti ();      return;    }  while (tmp->next)    {      if ((IN_ORDER (tmp, req) || ! IN_ORDER (tmp, tmp->next))	  && IN_ORDER (req, tmp->next))	break;      tmp = tmp->next;    }  req->next = tmp->next;  tmp->next = req;  if (scsi_blk_major (MAJOR (req->rq_dev)))    (*dev->request_fn) ();  sti ();}/* Perform the I/O operation RW on the buffer list BH   containing NR buffers.  */voidll_rw_block (int rw, int nr, struct buffer_head **bh){  int i, bshift, bsize;  unsigned major;  struct request *r;  static struct request req;  major = MAJOR (bh[0]->b_dev);  assert (major < MAX_BLKDEV);  get_block_size (bh[0]->b_dev, &bsize, &bshift);  if (! linux_auto_config)    {      assert (current_thread ()->pcb->data);      r = &((struct temp_data *) current_thread ()->pcb->data)->req;    }  else    r = &req;  for (i = 0, r->nr_sectors = 0; i < nr - 1; i++)    {      r->nr_sectors += bh[i]->b_size >> 9;      bh[i]->b_reqnext = bh[i + 1];    }  r->nr_sectors += bh[i]->b_size >> 9;  bh[i]->b_reqnext = NULL;  r->rq_status = RQ_ACTIVE;  r->rq_dev = bh[0]->b_dev;  r->cmd = rw;  r->errors = 0;  r->sector = bh[0]->b_blocknr << (bshift - 9);  r->current_nr_sectors = bh[0]->b_size >> 9;  r->buffer = bh[0]->b_data;  r->bh = bh[0];  r->bhtail = bh[nr - 1];  r->sem = NULL;  r->next = NULL;  enqueue_request (r);}#define BSIZE	(1 << bshift)#define BMASK	(BSIZE - 1)/* Perform read/write operation RW on device DEV   starting at *off to/from buffer *BUF of size *RESID.   The device block size is given by BSHIFT.  *OFF and   *RESID may be non-multiples of the block size.   *OFF, *BUF and *RESID are updated if the operation   completed successfully.  */static intrdwr_partial (int rw, kdev_t dev, loff_t *off,	      char **buf, int *resid, int bshift){  int c, err = 0, o;  long sect, nsect;  struct buffer_head bhead, *bh = &bhead;  struct gendisk *gd;  memset (bh, 0, sizeof (struct buffer_head));  bh->b_state = 1 << BH_Lock;  bh->b_dev = dev;  bh->b_blocknr = *off >> bshift;  bh->b_size = BSIZE;  /* Check if this device has non even number of blocks.  */  for (gd = gendisk_head, nsect = -1; gd; gd = gd->next)    if (gd->major == MAJOR (dev))      {	nsect = gd->part[MINOR (dev)].nr_sects;	break;      }  if (nsect > 0)    {      sect = bh->b_blocknr << (bshift - 9);      assert ((nsect - sect) > 0);      if (nsect - sect < (BSIZE >> 9))	bh->b_size = (nsect - sect) << 9;    }  bh->b_data = alloc_buffer (bh->b_size);  if (! bh->b_data)    return -LINUX_ENOMEM;  ll_rw_block (READ, 1, &bh);  wait_on_buffer (bh);  if (buffer_uptodate (bh))    {      o = *off & BMASK;      c = bh->b_size - o;      if (c > *resid)	c = *resid;      if (rw == READ)	memcpy (*buf, bh->b_data + o, c);      else	{	  memcpy (bh->b_data + o, *buf, c);	  bh->b_state = (1 << BH_Dirty) | (1 << BH_Lock);	  ll_rw_block (WRITE, 1, &bh);	  wait_on_buffer (bh);	  if (! buffer_uptodate (bh))	    {	      err = -LINUX_EIO;	      goto out;	    }	}      *buf += c;      *resid -= c;      *off += c;    }  else    err = -LINUX_EIO;out:  free_buffer (bh->b_data, bh->b_size);  return err;}#define BH_Bounce	16#define MAX_BUF		VM_MAP_COPY_PAGE_LIST_MAX

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -