Skip site navigation (1)Skip section navigation (2)
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>