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>
