📄 block.c
字号:
/* 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 must be multiples of the block size. *OFF, *BUF and *RESID are updated if the operation completed successfully. */static intrdwr_full (int rw, kdev_t dev, loff_t *off, char **buf, int *resid, int bshift){ int cc, err = 0, i, j, nb, nbuf; long blk; struct buffer_head bhead[MAX_BUF], *bh, *bhp[MAX_BUF]; assert ((*off & BMASK) == 0); nbuf = *resid >> bshift; blk = *off >> bshift; for (i = nb = 0, bh = bhead; nb < nbuf; bh++) { memset (bh, 0, sizeof (*bh)); bh->b_dev = dev; bh->b_blocknr = blk; set_bit (BH_Lock, &bh->b_state); if (rw == WRITE) set_bit (BH_Dirty, &bh->b_state); cc = PAGE_SIZE - (((int) *buf) & PAGE_MASK); if (cc >= BSIZE && ((int) *buf & 511) == 0) cc &= ~BMASK; else { cc = PAGE_SIZE; set_bit (BH_Bounce, &bh->b_state); } if (cc > ((nbuf - nb) << bshift)) cc = (nbuf - nb) << bshift; if (! test_bit (BH_Bounce, &bh->b_state)) bh->b_data = (char *) pmap_extract (vm_map_pmap (device_io_map), (((vm_offset_t) *buf) + (nb << bshift))); else { bh->b_data = alloc_buffer (cc); if (! bh->b_data) { err = -LINUX_ENOMEM; break; } if (rw == WRITE) memcpy (bh->b_data, *buf + (nb << bshift), cc); } bh->b_size = cc; bhp[i] = bh; nb += cc >> bshift; blk += nb; if (++i == MAX_BUF) break; } if (! err) { ll_rw_block (rw, i, bhp); wait_on_buffer (bhp[i - 1]); } for (bh = bhead, cc = 0, j = 0; j < i; cc += bh->b_size, bh++, j++) { if (! err && buffer_uptodate (bh) && rw == READ && test_bit (BH_Bounce, &bh->b_state)) memcpy (*buf + cc, bh->b_data, bh->b_size); else if (! err && ! buffer_uptodate (bh)) err = -LINUX_EIO; if (test_bit (BH_Bounce, &bh->b_state)) free_buffer (bh->b_data, bh->b_size); } if (! err) { *buf += cc; *resid -= cc; *off += cc; } return err;}/* Perform read/write operation RW on device DEV starting at *off to/from buffer BUF of size COUNT. *OFF is updated if the operation completed successfully. */static intdo_rdwr (int rw, kdev_t dev, loff_t *off, char *buf, int count){ int bsize, bshift, err = 0, resid = count; get_block_size (dev, &bsize, &bshift); if (*off & BMASK) err = rdwr_partial (rw, dev, off, &buf, &resid, bshift); while (resid >= bsize && ! err) err = rdwr_full (rw, dev, off, &buf, &resid, bshift); if (! err && resid) err = rdwr_partial (rw, dev, off, &buf, &resid, bshift); return err ? err : count - resid;}intblock_write (struct inode *inode, struct file *filp, const char *buf, int count){ return do_rdwr (WRITE, inode->i_rdev, &filp->f_pos, (char *) buf, count);}intblock_read (struct inode *inode, struct file *filp, char *buf, int count){ return do_rdwr (READ, inode->i_rdev, &filp->f_pos, buf, count);}/* * This routine checks whether a removable media has been changed, * and invalidates all buffer-cache-entries in that case. This * is a relatively slow routine, so we have to try to minimize using * it. Thus it is called only upon a 'mount' or 'open'. This * is the best way of combining speed and utility, I think. * People changing diskettes in the middle of an operation deserve * to loose :-) */intcheck_disk_change (kdev_t dev){ unsigned i; struct file_operations * fops; i = MAJOR(dev); if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL) return 0; if (fops->check_media_change == NULL) return 0; if (! (*fops->check_media_change) (dev)) return 0; /* printf ("Disk change detected on device %s\n", kdevname(dev));*/ if (fops->revalidate) (*fops->revalidate) (dev); return 1;}/* Mach device interface routines. *//* Mach name to Linux major/minor number mapping table. */static struct name_map name_to_major[] ={ /* IDE disks */ { "hd0", IDE0_MAJOR, 0, 0 }, { "hd1", IDE0_MAJOR, 1, 0 }, { "hd2", IDE1_MAJOR, 0, 0 }, { "hd3", IDE1_MAJOR, 1, 0 }, { "hd4", IDE2_MAJOR, 0, 0 }, { "hd5", IDE2_MAJOR, 1, 0 }, { "hd6", IDE3_MAJOR, 0, 0 }, { "hd7", IDE3_MAJOR, 1, 0 }, /* IDE CDROMs */ { "wcd0", IDE0_MAJOR, 0, 1 }, { "wcd1", IDE0_MAJOR, 1, 1 }, { "wcd2", IDE1_MAJOR, 0, 1 }, { "wcd3", IDE1_MAJOR, 1, 1 }, { "wcd4", IDE2_MAJOR, 0, 1 }, { "wcd5", IDE2_MAJOR, 1, 1 }, { "wcd6", IDE3_MAJOR, 0, 1 }, { "wcd7", IDE3_MAJOR, 1, 1 }, /* SCSI disks */ { "sd0", SCSI_DISK_MAJOR, 0, 0 }, { "sd1", SCSI_DISK_MAJOR, 1, 0 }, { "sd2", SCSI_DISK_MAJOR, 2, 0 }, { "sd3", SCSI_DISK_MAJOR, 3, 0 }, { "sd4", SCSI_DISK_MAJOR, 4, 0 }, { "sd5", SCSI_DISK_MAJOR, 5, 0 }, { "sd6", SCSI_DISK_MAJOR, 6, 0 }, { "sd7", SCSI_DISK_MAJOR, 7, 0 }, /* SCSI CDROMs */ { "cd0", SCSI_CDROM_MAJOR, 0, 1 }, { "cd1", SCSI_CDROM_MAJOR, 1, 1 }, /* Floppy disks */ { "fd0", FLOPPY_MAJOR, 0, 0 }, { "fd1", FLOPPY_MAJOR, 1, 0 },};#define NUM_NAMES (sizeof (name_to_major) / sizeof (name_to_major[0]))/* One of these is associated with each open instance of a device. */struct block_data{ const char *name; /* Mach name for device */ int want:1; /* someone is waiting for I/O to complete */ int open_count; /* number of opens */ int iocount; /* number of pending I/O operations */ int part; /* BSD partition number (-1 if none) */ int flags; /* Linux file flags */ int mode; /* Linux file mode */ kdev_t dev; /* Linux device number */ ipc_port_t port; /* port representing device */ struct device_struct *ds; /* driver operation table entry */ struct device device; /* generic device header */ struct name_map *np; /* name to inode map */ struct block_data *next; /* forward link */};/* List of open devices. */static struct block_data *open_list;/* Forward declarations. */extern struct device_emulation_ops linux_block_emulation_ops;static io_return_t device_close (void *);/* Return a send right for block device BD. */static ipc_port_tdev_to_port (void *bd){ return (bd ? ipc_port_make_send (((struct block_data *) bd)->port) : IP_NULL);}/* Return 1 if C is a letter of the alphabet. */static inline intisalpha (int c){ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');}/* Return 1 if C is a digit. */static inline intisdigit (int c){ return c >= '0' && c <= '9';}/* Find the name map entry for device NAME. Set *SLICE to be the DOS partition and *PART the BSD/Mach partition, if any. */static struct name_map *find_name (char *name, int *slice, int *part){ char *p, *q; int i, len; struct name_map *np; /* Parse name into name, unit, DOS partition (slice) and partition. */ for (*slice = 0, *part = -1, p = name; isalpha (*p); p++) ; if (p == name || ! isdigit (*p)) return NULL; do p++; while (isdigit (*p)); if (*p) { q = p; if (*q == 's' && isdigit (*(q + 1))) { q++; do *slice = *slice * 10 + *q++ - '0'; while (isdigit (*q)); if (! *q) goto find_major; } if (! isalpha (*q) || *(q + 1)) return NULL; *part = *q - 'a'; }find_major: /* Convert name to major number. */ for (i = 0, np = name_to_major; i < NUM_NAMES; i++, np++) { len = strlen (np->name); if (len == (p - name) && ! strncmp (np->name, name, len)) return np; } return NULL;}/* Attempt to read a BSD disklabel from device DEV. */static struct disklabel *read_bsd_label (kdev_t dev){ int bsize, bshift; struct buffer_head *bh; struct disklabel *dlp, *lp = NULL; get_block_size (dev, &bsize, &bshift); bh = bread (dev, LBLLOC >> (bshift - 9), bsize); if (bh) { dlp = (struct disklabel *) (bh->b_data + ((LBLLOC << 9) & (bsize - 1))); if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC) { lp = (struct disklabel *) kalloc (sizeof (*lp)); assert (lp); memcpy (lp, dlp, sizeof (*lp)); } __brelse (bh); } return lp;}/* Attempt to read a VTOC from device DEV. */static struct disklabel *read_vtoc (kdev_t dev){ int bshift, bsize, i; struct buffer_head *bh; struct evtoc *evp; struct disklabel *lp = NULL; get_block_size (dev, &bsize, &bshift); bh = bread (dev, PDLOCATION >> (bshift - 9), bsize); if (bh) { evp = (struct evtoc *) (bh->b_data + ((PDLOCATION << 9) & (bsize - 1))); if (evp->sanity == VTOC_SANE) { lp = (struct disklabel *) kalloc (sizeof (*lp)); assert (lp); lp->d_npartitions = evp->nparts; if (lp->d_npartitions > MAXPARTITIONS) lp->d_npartitions = MAXPARTITIONS; for (i = 0; i < lp->d_npartitions; i++) { lp->d_partitions[i].p_size = evp->part[i].p_size; lp->d_partitions[i].p_offset = evp->part[i].p_start; lp->d_partitions[i].p_fstype = FS_BSDFFS; } } __brelse (bh); } return lp;}/* Initialize BSD/Mach partition table for device specified by NP, DS and *DEV. Check SLICE and *PART for validity. */static kern_return_tinit_partition (struct name_map *np, kdev_t *dev, struct device_struct *ds, int slice, int *part){ int err, i, j; struct disklabel *lp; struct gendisk *gd = ds->gd; struct partition *p; struct temp_data *d = current_thread ()->pcb->data; if (! gd) { *part = -1; return 0; } if (ds->labels) goto check; ds->labels = (struct disklabel **) kalloc (sizeof (struct disklabel *) * gd->max_nr * gd->max_p); if (! ds->labels) return D_NO_MEMORY; memset ((void *) ds->labels, 0, sizeof (struct disklabel *) * gd->max_nr * gd->max_p); for (i = 1; i < gd->max_p; i++) { d->inode.i_rdev = *dev | i; if (gd->part[MINOR (d->inode.i_rdev)].nr_sects <= 0 || gd->part[MINOR (d->inode.i_rdev)].start_sect < 0) continue; linux_intr_pri = SPL5; d->file.f_flags = 0; d->file.f_mode = O_RDONLY; if (ds->fops->open && (*ds->fops->open) (&d->inode, &d->file)) continue; lp = read_bsd_label (d->inode.i_rdev); if (! lp && gd->part[MINOR (d->inode.i_rdev)].nr_sects > PDLOCATION) lp = read_vtoc (d->inode.i_rdev); if (ds->fops->release) (*ds->fops->release) (&d->inode, &d->file); if (lp) { if (ds->default_slice == 0) ds->default_slice = i; for (j = 0, p = lp->d_partitions; j < lp->d_npartitions; j++, p++) { if (p->p_offset < 0 || p->p_size <= 0) continue; /* Sanity check. */ if (p->p_size > gd->part[MINOR (d->inode.i_rdev)].nr_sects) p->p_size = gd->part[MINOR (d->inode.i_rdev)].nr_sects; } } ds->labels[MINOR (d->inode.i_rdev)] = lp; }check: if (*part >= 0 && slice == 0) slice = ds->default_slice; if (*part >= 0 && slice == 0) return D_NO_SUCH_DEVICE; *dev = MKDEV (MAJOR (*dev), MINOR (*dev) | slice); if (slice >= gd->max_p || gd->part[MINOR (*dev)].start_sect < 0 || gd->part[MINOR (*dev)].nr_sects <= 0) return D_NO_SUCH_DEVICE; if (*part >= 0) { lp = ds->labels[MINOR (*dev)]; if (! lp || *part >= lp->d_npartitions || lp->d_partitions[*part].p_offset < 0 || lp->d_partitions[*part].p_size <= 0) return D_NO_SUCH_DEVICE; } return 0;}#define DECL_DATA struct temp_data td#define INIT_DATA() \{ \ queue_init (&td.pages); \ td.inode.i_rdev = bd->dev; \ td.file.f_mode = bd->mode; \ td.file.f_flags = bd->flags; \ current_thread ()->pcb->data = &td; \}static io_return_tdevice_open (ipc_port_t reply_port, mach_msg_type_name_t reply_port_type, dev_mode_t mode, char *name, device_t *devp){ int part, slice, err; unsigned major, minor; kdev_t dev; ipc_port_t notify; struct block_data *bd = NULL, *bdp; struct device_struct *ds; struct gendisk *gd; struct name_map *np; DECL_DATA; np = find_name (name, &slice, &part); if (! np) return D_NO_SUCH_DEVICE; major = np->major; ds = &blkdevs[major]; /* Check that driver exists. */ if (! ds->fops) return D_NO_SUCH_DEVICE; /* Wait for any other open/close calls to finish. */ ds = &blkdevs[major]; while (ds->busy) { ds->want = 1; assert_wait ((event_t) ds, FALSE); schedule (); } ds->busy = 1; /* Compute minor number. */ if (! ds->gd) { for (gd = gendisk_head; gd && gd->major != major; gd = gd->next) ; ds->gd = gd; } minor = np->unit; gd = ds->gd; if (gd) minor <<= gd->minor_shift; dev = MKDEV (major, minor); queue_init (&td.pages); current_thread ()->pcb->data = &td; /* Check partition. */ err = init_partition (np, &dev, ds, slice, &part); if (err) goto out; /* Initialize file structure. */ switch (mode & (D_READ|D_WRITE)) { case D_WRITE: td.file.f_mode = O_WRONLY; break; case D_READ|D_WRITE: td.file.f_mode = O_RDWR; break; default: td.file.f_mode = O_RDONLY; break; } td.file.f_flags = (mode & D_NODELAY) ? O_NDELAY : 0; /* Check if the device is currently open. */ for (bdp = open_list; bdp; bdp = bdp->next) if (bdp->dev == dev && bdp->part == part && bdp->mode == td.file.f_mode && bdp->flags == td.file.f_flags) { bd = bdp; goto out; } /* Open the device. */ if (ds->fops->open) { td.inode.i_rdev = dev; linux_intr_pri = SPL5; err = (*ds->fops->open) (&td.inode, &td.file); if (err) { err = linux_to_mach_error (err); goto out; } } /* Allocate and initialize device data. */ bd = (struct block_data *) kalloc (sizeof (struct block_data)); if (! bd) { err = D_NO_MEMORY; goto bad; } bd->want = 0; bd->open_count = 0; bd->iocount = 0; bd->part = part; bd->ds = ds; bd->device.emul_data = bd; bd->device.emul_ops = &linux_block_emulation_ops; bd->dev = dev; bd->mode = td.file.f_mode; bd->flags = td.file.f_flags; bd->port = ipc_port_alloc_kernel (); if (bd->port == IP_NULL) { err = KERN_RESOURCE_SHORTAGE; goto bad; } ipc_kobject_set (bd->port, (ipc_kobject_t) &bd->device, IKOT_DEVICE); notify = ipc_port_make_sonce (bd->port); ip_lock (bd->port); ipc_port_nsrequest (bd->port, 1, notify, ¬ify); assert (notify == IP_NULL); goto out;bad: if (ds->fops->release) (*ds->fops->release) (&td.inode, &td.file);out: ds->busy = 0; if (ds->want) { ds->want = 0; thread_wakeup ((event_t) ds); } if (bd && bd->open_count > 0) { if (err) *devp = NULL; else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -