Date: Wed, 12 Feb 1997 21:01:06 +0300 From: vaki@cronyx.ru To: FreeBSD-gnats-submit@freebsd.org Subject: kern/2719: [patch] added support for 2k-block magneto-optical disks Message-ID: <199702121801.VAA02043@crox.net.kiae.su> Resent-Message-ID: <199702121810.KAA15783@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 2719 >Category: kern >Synopsis: added support for magneto-optical SCSI disks with 2k logical block size >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Wed Feb 12 10:10:01 PST 1997 >Last-Modified: >Originator: Serge V.Vakulenko >Organization: Cronyx Engineering Ltd. >Release: FreeBSD 2.1.5-RELEASE >Environment: FreeBSD 2.1.5, magneto-optical SCSI disk drive Fujitsu M2513A2, removable disk 640 Mb >Description: The driver `od' does not support the media with 2kb logical block. Neither FreeBSD 2.2 nor 3.0 seem to handle it too. >How-To-Repeat: Insert the disk and try any disk i/o, say, "od -c /dev/rod0". You will see the error message on the console: "Can't deal with 2048 bytes logical blocks". >Fix: Try the following patch. -------------------------------------------------------------------- Here is a new version of SCSI driver for magneto-optical drives, with support for 640-Mb drives (2k logical block size). It was tested on Fujitsu M2513A2 drive with 640Mb disk, FreeBSD 2.1.5, NCR53c810-based PCI SCSI adapter. The data rate is 502 kbyte/sec on write, 1.46 Mbyte/sec on read. (on-drive write cache mode and verify mode enabled) The rate was measured by dd command between /dev/zero and /dev/null and the file on the mounted MO disk, total 50 Mb test file size, default dd block size (512 bytes). The disklabel/partition table support was removed from the driver. There were two reasons for this: 1) The diskslice management routines cannot handle logical blocks different from the default 512 byte size. I tried to fix this, but it seems to be too hard for me to do. After all, it's not of vital importance for these disks, I could happily live without it! 2) The reason why the diskslice support would be needed is the compatibility with the MSDOS-formatted disks. But the FreeBSD implementation of the MSDOS filesystem does not work on these disks! Probably, the problem is in the different logical block size, but I did not dig it deeper. There is a minimal disklabel support in the driver, at least the newfs formats it without the need of the entry in /etc/disktab. For the (minimal) support of the MSDOS-formatted disks (when the msdos filesystem will be fixed - somewhere in the future), the four precompiled partitions E, F, G, H are added, with different offsets from the beginning of the volume. Serge Vakulenko, Cronyx Engineering Ltd. --- od215.c Tue Feb 11 19:42:47 1997 +++ od.c Wed Feb 12 17:14:49 1997 @@ -99,23 +99,19 @@ #define ODINIT 0x04 /* device has been init'd */ struct disk_parms { u_char heads; /* Number of heads */ - u_int16_t cyls; /* Number of cylinders (ficticous) */ u_int16_t sectors; /* Number of sectors/track */ u_int16_t secsiz; /* Number of bytes/sector */ u_int32_t disksize; /* total number sectors */ u_int16_t rpm; /* medium rotation rate */ } params; - struct diskslices *dk_slices; /* virtual drives */ - struct buf buf_queue; int dkunit; /* disk stats unit number */ + u_char bsector[2048]; /* data buffer for partial i/o */ }; static errval od_get_parms __P((int unit, int flags)); static errval od_reassign_blocks __P((int unit, int block)); static u_int32_t od_size __P((int unit, int flags)); static int od_sense_handler __P((struct scsi_xfer *)); -static void odstart __P((u_int32_t, u_int32_t)); -static void odstrategy1 __P((struct buf *)); static dev_t odsetunit(dev_t dev, int unit) { return ODSETUNIT(dev, unit); } static int odunit(dev_t dev) { return ODUNIT(dev); } @@ -136,7 +132,7 @@ static struct scsi_device od_switch = { od_sense_handler, - odstart, /* have a queue, served by this */ + NULL, /* have no queue */ NULL, /* have no async handler */ NULL, /* Use default 'done' routine */ "od", @@ -156,6 +152,15 @@ od_strategy, }; +/* + * There are the following preconfigured partitions: + * e - offset 1 logical block + * f - offset 32 logical block (1 track) + * g - offset 64 logical blocks (1 track on some BIOSes) + * h - offset 2048 logical blocks (1 cylinder). + */ +short od_off[8] = { 0, 0, 0, 0, 1, 32, 64, 2048 }; + static int od_externalize(struct proc *p, struct kern_devconf *kdc, void *userp, size_t len) @@ -241,8 +246,8 @@ #endif { sc_print_addr(sc_link); - printf("with approximate %d cyls, %d heads, and %d sectors/track", - dp->cyls, dp->heads, dp->sectors); + printf("with approximate %d heads, and %d sectors/track", + dp->heads, dp->sectors); } #ifdef OD_AUTO_TURNOFF scsi_stop_unit(sc_link, 0, SCSI_ERR_OK | SCSI_SILENT); @@ -267,7 +272,6 @@ { errval errcode = 0; u_int32_t unit; - struct disklabel label; struct scsi_data *od; unit = ODUNIT(dev); @@ -303,21 +307,6 @@ SC_DEBUG(sc_link, SDEV_DB3, ("'start' attempted ")); sc_link->flags |= SDEV_OPEN; /* unit attn becomes an err now */ - /* - * If it's been invalidated, then forget the label. - */ - if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { - /* - * If somebody still has it open, then forbid re-entry. - */ - if (dsisopen(od->dk_slices)) { - errcode = ENXIO; - goto bad; - } - - if (od->dk_slices != NULL) - dsgone(&od->dk_slices); - } /* * This time actually take notice of error returns @@ -339,9 +328,7 @@ switch (od->params.secsiz) { case SECSIZE : case 1024 : -#ifdef notyet case 2048 : -#endif break; default : printf("od%ld: Can't deal with %d bytes logical blocks\n", @@ -352,40 +339,16 @@ } SC_DEBUG(sc_link, SDEV_DB3, ("params loaded ")); - /* Build label for whole disk. */ - bzero(&label, sizeof label); - label.d_secsize = od->params.secsiz; - label.d_nsectors = od->params.sectors; - label.d_ntracks = od->params.heads; - label.d_ncylinders = od->params.cyls; - label.d_secpercyl = od->params.heads * od->params.sectors; - label.d_rpm = od->params.rpm; /* maybe wrong */ - if (label.d_secpercyl == 0) - label.d_secpercyl = 64*32; - /* XXX as long as it's not 0 - * - readdisklabel divides by it (?) - */ - label.d_secperunit = od->params.disksize; - - /* Initialize slice tables. */ - errcode = dsopen("od", dev, fmt, &od->dk_slices, &label, odstrategy1, - (ds_setgeom_t *)NULL); - if (errcode != 0) - goto bad; - SC_DEBUG(sc_link, SDEV_DB3, ("Slice tables initialized ")); - SC_DEBUG(sc_link, SDEV_DB3, ("open %ld %ld\n", odstrats, odqueues)); return 0; bad: - if (!dsisopen(od->dk_slices)) { - scsi_prevent(sc_link, PR_ALLOW, SCSI_ERR_OK | SCSI_SILENT); + scsi_prevent(sc_link, PR_ALLOW, SCSI_ERR_OK | SCSI_SILENT); #ifdef OD_AUTO_TURNOFF - scsi_stop_unit(sc_link, 0, SCSI_ERR_OK | SCSI_SILENT); + scsi_stop_unit(sc_link, 0, SCSI_ERR_OK | SCSI_SILENT); #endif /* OD_AUTO_TURNOFF */ - sc_link->flags &= ~SDEV_OPEN; - } + sc_link->flags &= ~SDEV_OPEN; return errcode; } @@ -401,20 +364,86 @@ struct proc *p; struct scsi_link *sc_link; { - struct scsi_data *od; + struct scsi_data *od = sc_link->sd; - od = sc_link->sd; - dsclose(dev, fmt, od->dk_slices); - if (!dsisopen(od->dk_slices)) { - scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT | SCSI_ERR_OK); + scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT | SCSI_ERR_OK); #ifdef OD_AUTO_TURNOFF - scsi_stop_unit(sc_link, 0, SCSI_ERR_OK | SCSI_SILENT); + scsi_stop_unit(sc_link, 0, SCSI_ERR_OK | SCSI_SILENT); #endif /* OD_AUTO_TURNOFF */ - sc_link->flags &= ~SDEV_OPEN; - } + sc_link->flags &= ~SDEV_OPEN; return 0; } +int od_sector_io (struct scsi_link *sc_link, struct scsi_data *od, int op, + int blkno, int bnum, u_char *addr) +{ + struct scsi_rw_big cmd; + + /* + * Fill out the scsi command + */ + bzero (&cmd, sizeof(cmd)); + cmd.op_code = (op == B_READ) ? READ_BIG : WRITE_BIG; + cmd.addr_3 = (blkno & 0xff000000UL) >> 24; + cmd.addr_2 = (blkno & 0xff0000) >> 16; + cmd.addr_1 = (blkno & 0xff00) >> 8; + cmd.addr_0 = blkno & 0xff; + cmd.length2 = (bnum & 0xff00) >> 8; + cmd.length1 = bnum & 0xff; + + /* printf("od%ld: %s block %d\n", sc_link->dev_unit, + (op == B_READ) ? "read" : "write", blkno); */ + return scsi_scsi_cmd (sc_link, (struct scsi_generic*) &cmd, + sizeof(cmd), addr, bnum * od->params.secsiz, + OD_RETRIES, 10000, 0, (op == B_READ) ? + SCSI_DATA_IN : SCSI_DATA_OUT); +} + +/* + * Handle the partial logical sector i/o, + * for logical block sizes 1024 and 2048 bytes. + */ +int od_io (struct scsi_link *sc_link, struct buf *bp) +{ + struct scsi_data *od = sc_link->sd; + int lbs = od->params.secsiz / SECSIZE; + int off = bp->b_pblkno % lbs * SECSIZE; + int blkno = bp->b_pblkno / lbs; + int bcount = bp->b_bcount; + u_char *addr = bp->b_un.b_addr; + + while (bcount > 0) { + int len = bcount; + if (len > od->params.secsiz - off) + len = od->params.secsiz - off; + + /* Read new sector. */ + if ((bp->b_flags & B_READ) || len != od->params.secsiz) { + if (od_sector_io (sc_link, od, B_READ, blkno, 1, od->bsector) != 0) + return 0; + } else { + bzero (od->bsector, od->params.secsiz); + } + + if (bp->b_flags & B_READ) { + /* Get data from the buffer. */ + bcopy (od->bsector + off, addr, len); + } else { + /* Put data into the buffer. */ + bcopy (addr, od->bsector + off, len); + + /* Write it. */ + if (od_sector_io (sc_link, od, B_WRITE, blkno, 1, od->bsector) != 0) + return 0; + } + bcount -= len; + addr += len; + off = 0; + ++blkno; + } + return 1; +} + /* * Actually translate the requested transfer into one the physical driver * can understand. The transfer is described by a buf and will include @@ -423,10 +452,10 @@ static void od_strategy(struct buf *bp, struct scsi_link *sc_link) { - struct buf *dp; u_int32_t opri; struct scsi_data *od; u_int32_t unit; + int lbs; odstrats++; unit = ODUNIT((bp->b_dev)); @@ -435,7 +464,7 @@ /* * If the device has been made invalid, error out */ - if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { + if (! (sc_link->flags & SDEV_MEDIA_LOADED)) { bp->b_error = EIO; goto bad; } @@ -447,14 +476,10 @@ bp->b_error = EINVAL; goto bad; } - /* - * Do bounds checking, adjust transfer, set b_cylin and b_pbklno. - */ - if (dscheck(bp, od->dk_slices) <= 0) - goto done; /* XXX check b_resid */ + lbs = od->params.secsiz / SECSIZE; + bp->b_pblkno = bp->b_blkno + od_off[PARTITION(bp->b_dev)] * lbs; opri = SPLOD(); - dp = &od->buf_queue; /* * Use a bounce buffer if necessary @@ -463,24 +488,43 @@ if (sc_link->flags & SDEV_BOUNCE) vm_bounce_alloc(bp); #endif - - /* - * Place it in the queue of disk activities for this disk - */ - disksort(dp, bp); - - /* - * Tell the device to get going on the transfer if it's - * not doing anything, otherwise just wait for completion - */ - odstart(unit, 0); + dk_busy |= 1 << od->dkunit; + if (bp->b_bcount % od->params.secsiz || bp->b_blkno % lbs) { + if (! od_io (sc_link, bp)) { + printf("od%ld: short %s error, blkno=%d, bcount=%d\n", sc_link->dev_unit, + (bp->b_flags & B_READ) ? "read" : "write", bp->b_blkno, bp->b_bcount); + bp->b_error = EIO; + splx(opri); + goto bad; + } + } else { + if (od_sector_io (sc_link, od, (bp->b_flags & B_READ), + bp->b_pblkno / lbs, bp->b_bcount / od->params.secsiz, + bp->b_un.b_addr) != 0) { + printf("od%ld: %s error, blkno=%d, bcount=%d\n", sc_link->dev_unit, + (bp->b_flags & B_READ) ? "read" : "write", bp->b_blkno, bp->b_bcount); + bp->b_error = EIO; + splx(opri); + goto bad; + } + } + dk_busy &= ~(1 << od->dkunit); + odqueues++; + if (od->dkunit >= 0) { + dk_xfer[od->dkunit]++; + dk_seek[od->dkunit]++; + dk_wds[od->dkunit] += bp->b_bcount >> 6; + } + bp->b_error = 0; + bp->b_resid = 0; + biodone (bp); splx(opri); return /*0*/; bad: + dk_busy &= ~(1 << od->dkunit); bp->b_flags |= B_ERROR; done: - /* * Correctly set the buf to indicate a completed xfer */ @@ -489,123 +533,43 @@ return /*0*/; } -static void -odstrategy1(struct buf *bp) -{ - /* - * XXX - do something to make odstrategy() but not this block while - * we're doing dsinit() and dsioctl(). - */ - odstrategy(bp); -} - /* - * odstart looks to see if there is a buf waiting for the device - * and that the device is not already busy. If both are true, - * It dequeues the buf and creates a scsi command to perform the - * transfer in the buf. The transfer request will call scsi_done - * on completion, which will in turn call this routine again - * so that the next queued transfer is performed. - * The bufs are queued by the strategy routine (odstrategy) - * - * This routine is also called after other non-queued requests - * have been made of the scsi driver, to ensure that the queue - * continues to be drained. - * - * must be called at the correct (highish) spl level - * odstart() is called at SPLOD from odstrategy and scsi_done + * Fabricate a disklabel */ -static void -odstart(u_int32_t unit, u_int32_t flags) +static errval od_getdisklabel (struct scsi_data *od, struct disklabel *dl) { - register struct scsi_link *sc_link = SCSI_LINK(&od_switch, unit); - register struct scsi_data *od = sc_link->sd; - struct buf *bp = 0; - struct buf *dp; - struct scsi_rw_big cmd; - u_int32_t blkno, nblk; - u_int32_t secsize; - - SC_DEBUG(sc_link, SDEV_DB2, ("odstart ")); - /* - * Check if the device has room for another command - */ - while (sc_link->opennings) { + int i, lbs; - /* - * there is excess capacity, but a special waits - * It'll need the adapter as soon as we clear out of the - * way and let it run (user level wait). - */ - if (sc_link->flags & SDEV_WAITING) { - return; - } - /* - * See if there is a buf with work for us to do.. - */ - dp = &od->buf_queue; - if ((bp = dp->b_actf) == NULL) { /* yes, an assign */ - return; - } - dp->b_actf = bp->b_actf; + lbs = od->params.secsiz / SECSIZE; + bzero (dl, sizeof (struct disklabel)); - /* - * If the device has become invalid, abort all the - * reads and writes until all files have been closed and - * re-openned - */ - if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { - goto bad; - } - /* - * We have a buf, now we know we are going to go through - * With this thing.. - */ - secsize = od->params.secsiz; - blkno = bp->b_pblkno / (secsize / DEV_BSIZE); - if (bp->b_bcount & (secsize - 1)) - { - goto bad; - } - nblk = (bp->b_bcount + (secsize - 1)) / secsize; - - /* - * Fill out the scsi command - */ - cmd.op_code = (bp->b_flags & B_READ) - ? READ_BIG : WRITE_BIG; - scsi_uto4b(blkno, &cmd.addr_3); - scsi_uto2b(nblk, &cmd.length2); - cmd.byte2 = cmd.reserved = cmd.control = 0; - /* - * Call the routine that chats with the adapter. - * Note: we cannot sleep as we may be an interrupt - */ - if (scsi_scsi_cmd(sc_link, - (struct scsi_generic *) &cmd, - sizeof(cmd), - (u_char *) bp->b_un.b_addr, - bp->b_bcount, - OD_RETRIES, - 100000, - bp, - flags | ((bp->b_flags & B_READ) ? - SCSI_DATA_IN : SCSI_DATA_OUT)) - == SUCCESSFULLY_QUEUED) { - odqueues++; - if(od->dkunit >= 0) { - dk_xfer[od->dkunit]++; - dk_seek[od->dkunit]++; /* don't know */ - dk_wds[od->dkunit] += bp->b_bcount >> 6; - } - } else { -bad: - printf("od%ld: oops not queued\n", unit); - bp->b_error = EIO; - bp->b_flags |= B_ERROR; - biodone(bp); - } - } + strncpy (dl->d_typename, "magneto-optical", 16); + strncpy (dl->d_packname, "ficticious", 16); + + dl->d_type = DTYPE_SCSI; + dl->d_flags = D_REMOVABLE; + dl->d_interleave = 1; + dl->d_secsize = SECSIZE; + dl->d_nsectors = od->params.sectors; + dl->d_ntracks = od->params.heads; + dl->d_secpercyl = od->params.sectors * od->params.heads; + dl->d_ncylinders = od->params.disksize * lbs / dl->d_secpercyl; + dl->d_secperunit = dl->d_ncylinders * dl->d_secpercyl; + dl->d_rpm = od->params.rpm; + if (! dl->d_rpm) + dl->d_rpm = 3600; + + dl->d_npartitions = 8; + for (i=0; i<dl->d_npartitions; ++i) { + dl->d_partitions[i].p_offset = od_off[i] * lbs; + dl->d_partitions[i].p_size = dl->d_secperunit - od_off[i] * lbs; + } + dl->d_partitions[1].p_size = 0; + + dl->d_magic = DISKMAGIC; + dl->d_magic2 = DISKMAGIC; + dl->d_checksum = dkcksum (dl); + return 0; } /* @@ -636,6 +600,9 @@ case DIOCSBAD: error = EINVAL; break; + case DIOCGDINFO: + error = od_getdisklabel (od, (struct disklabel*) addr); + break; case CDIOCEJECT: error = scsi_stop_unit(sc_link, 1, 0); sc_link->flags &= ~SDEV_MEDIA_LOADED; @@ -647,16 +614,7 @@ error = scsi_prevent(sc_link, PR_PREVENT, 0); break; default: - error = dsioctl("od", dev, cmd, addr, flag, &od->dk_slices, - odstrategy1, (ds_setgeom_t *)NULL); - if (error == -1) { - if (PARTITION(dev) != RAW_PART) { - error = ENOTTY; - } else { - error = scsi_do_ioctl(dev, cmd, addr, - flag, p, sc_link); - } - } + error = scsi_do_ioctl(dev, cmd, addr, flag, p, sc_link); break; } return error; @@ -838,14 +796,6 @@ */ sectors = od_size(unit, flags); /* od_size() sets secsiz, disksize, sectors, and heads */ - - /* ficticous number of cylinders, so that C*H*S <= total */ - if (disk_parms->sectors != 0 && disk_parms->heads != 0) { - disk_parms->cyls = - sectors / (disk_parms->sectors * disk_parms->heads); - } else { - disk_parms->cyls = 0; - } if (sectors != 0) { sc_link->flags |= SDEV_MEDIA_LOADED; >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199702121801.VAA02043>