Date: Thu, 29 Jan 1998 13:35:56 +0300 (MSK) From: vak@crox.net.kiae.su (Serge V.Vakulenko) To: FreeBSD-gnats-submit@FreeBSD.ORG Subject: kern/5598: [patch] od - support for MO devices with 2k sector size (like Fujitsu M2513 with 640M disks) Message-ID: <199801291035.NAA01877@crox.net.kiae.su>
index | next in thread | raw e-mail
>Number: 5598
>Category: kern
>Synopsis: Support for magneto-optic SCSI devices with 2k sector size added (like Fujitsu M2513 with 640M disks)
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Thu Jan 29 02:40:01 PST 1998
>Last-Modified:
>Originator: Serge V.Vakulenko
>Organization:
Cronyx Engineering Ltd.
>Release: FreeBSD 3.0-971225-SNAP i386
>Environment:
Fujitsu M2513 magneto-optic device, and 640M disks.
>Description:
The current implementation of UFS file system, disk label
and partition routines require the driver to handle
any 512-byte-aligned i/o request. Most SCSI HDD, CD-ROM
and MO devices have 512 byte sectors, and cause no problem.
But the 640M MO disks have 2k sector size, and do not work
as appropriate.
>How-To-Repeat:
Try to disklabel the 640M disk, for example:
disklabel -r -w od0 auto
>Fix:
After applying the following patch, the disklabel, newfs
and the UFS filesystem starts working fine, with a good
data throughput (tested copying 32-megabyte file):
reading - 1.1 Mb/sec, writing - 0.5 Mb/sec.
--- od30.c Fri Jan 23 21:09:48 1998
+++ od.c Tue Jan 27 18:43:16 1998
@@ -66,6 +66,7 @@
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/buf.h>
+#include <sys/malloc.h>
#include <sys/cdio.h>
#include <sys/dkstat.h>
#include <sys/disklabel.h>
@@ -105,6 +106,13 @@
struct diskslices *dk_slices; /* virtual drives */
struct buf_queue_head buf_queue;
int dkunit; /* disk stats unit number */
+ u_char buf [2048]; /* partial i/o intermediate buffer */
+ u_int32_t blkno; /* start i/o block number (logical 512) */
+ u_int32_t nblk; /* total i/o length (logical blocks 512) */
+ u_int32_t ionsec; /* partial i/o length (physical sectors) */
+ u_int32_t ioflags;
+ int partial_write; /* partial write flag */
+ u_char *data; /* i/o buffer pointer */
#ifdef DEVFS
/* Eventually move all these to common disk struct. */
void *b_devfs_token;
@@ -120,6 +128,7 @@
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 int od_done __P((struct scsi_xfer *));
static void odstrategy1 __P((struct buf *));
static dev_t odsetunit(dev_t dev, int unit) { return ODSETUNIT(dev, unit); }
@@ -156,7 +165,7 @@
od_sense_handler,
odstart, /* have a queue, served by this */
NULL, /* have no async handler */
- NULL, /* Use default 'done' routine */
+ od_done, /* have a post-i/o processing routine */
"od",
0,
{0, 0},
@@ -376,7 +385,7 @@
/* XXX as long as it's not 0
* - readdisklabel divides by it (?)
*/
- label.d_secperunit = od->params.disksize;
+ label.d_secperunit = label.d_secpercyl * od->params.cyls;
/* Initialize slice tables. */
errcode = dsopen("od", dev, fmt, &od->dk_slices, &label, odstrategy1,
@@ -460,18 +469,10 @@
goto bad;
}
-
secsize = od->params.secsiz;
- /* make sure the blkno is scalable */
- if( (bp->b_blkno % (secsize/DEV_BSIZE)) != 0 ) {
- bp->b_error = EINVAL;
- printf("od_strategy: Block number is not multiple of sector size (2): 0x%x\n", bp->b_blkno);
- goto bad;
- }
-
/* make sure that the transfer size is a multiple of the sector size */
- if( (bp->b_bcount % secsize) != 0 ) {
+ if( (bp->b_bcount % DEV_BSIZE) != 0 ) {
bp->b_error = EINVAL;
printf("od_strategy: Invalid b_bcount %d at block number: 0x%x\n", bp->b_bcount, bp->b_blkno);
goto bad;
@@ -552,6 +553,157 @@
odstrategy(bp);
}
+static int
+od_io (struct scsi_link *sc_link, struct buf *bp)
+{
+ struct scsi_data *od = sc_link->sd;
+ int blocks_per_sector = od->params.secsiz / DEV_BSIZE;
+ int op = (bp->b_flags & B_READ) ? READ_BIG : WRITE_BIG;
+ u_char *data = od->data;
+ struct scsi_rw_big cmd;
+
+ od->partial_write = 0;
+ od->ionsec = od->nblk / blocks_per_sector;
+ if (od->blkno % blocks_per_sector || od->nblk < blocks_per_sector) {
+ /* partial i/o, read the whole sector */
+ op = READ_BIG;
+ od->ionsec = 1;
+ data = od->buf;
+ }
+
+ /*
+ * Fill out the scsi command
+ */
+ cmd.op_code = op;
+ scsi_uto4b (od->blkno / blocks_per_sector, &cmd.addr_3);
+ scsi_uto2b (od->ionsec, &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),
+ data, od->ionsec * od->params.secsiz, OD_RETRIES, 100000, bp,
+ od->ioflags | (op==READ_BIG ? SCSI_DATA_IN : SCSI_DATA_OUT))
+ == SUCCESSFULLY_QUEUED)
+ return 0;
+ return -1;
+}
+
+/*
+ * This routine is called by the scsi interrupt when the transfer is complete.
+ */
+static int
+od_done (struct scsi_xfer *xs)
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ struct scsi_data *od = sc_link->sd;
+ struct buf *bp = xs->bp;
+ int blocks_per_sector = od->params.secsiz / DEV_BSIZE;
+ struct scsi_rw_big cmd;
+
+ if (! bp || xs->error == XS_TIMEOUT)
+ return 0;
+
+ if (od->blkno % blocks_per_sector) {
+ int offset = od->blkno % blocks_per_sector * DEV_BSIZE;
+ int len = blocks_per_sector * DEV_BSIZE - offset;
+
+ if (len > bp->b_bcount)
+ len = bp->b_bcount;
+ od->nblk -= len / DEV_BSIZE;
+ od->data += len;
+
+ if (bp->b_flags & B_READ) {
+ /* partial read */
+ od->blkno += len / DEV_BSIZE;
+ bcopy (od->buf + offset, bp->b_data, len);
+ if (od->nblk == 0)
+ return 0;
+ goto doio;
+ }
+
+ /* partial write */
+ bcopy (bp->b_data, od->buf + offset, len);
+ od->partial_write = 1;
+
+ free (xs, M_TEMP);
+ sc_link->active--;
+ sc_link->opennings++;
+
+ /* write sector back */
+ cmd.op_code = WRITE_BIG;
+ scsi_uto4b (od->blkno / blocks_per_sector, &cmd.addr_3);
+ scsi_uto2b (1, &cmd.length2);
+ cmd.byte2 = cmd.reserved = cmd.control = 0;
+ if (scsi_scsi_cmd (sc_link, (struct scsi_generic*) &cmd,
+ sizeof(cmd), od->buf, od->params.secsiz, OD_RETRIES,
+ 100000, bp, od->ioflags | SCSI_DATA_OUT)
+ != SUCCESSFULLY_QUEUED) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ }
+ od->blkno += len / DEV_BSIZE;
+ return -2;
+ }
+
+ if (od->partial_write)
+ goto doio;
+
+ if (od->nblk % blocks_per_sector) {
+ if (od->nblk >= blocks_per_sector) {
+ int len = od->nblk / blocks_per_sector *
+ blocks_per_sector * DEV_BSIZE;
+ od->blkno += len / DEV_BSIZE;
+ od->nblk -= len / DEV_BSIZE;
+ od->data += len;
+ goto doio;
+ }
+ if (bp->b_flags & B_READ) {
+ /* partial read */
+ bcopy (od->buf, od->data, od->nblk * DEV_BSIZE);
+ return 0;
+ }
+
+ /* partial write */
+ bcopy (od->data, od->buf, od->nblk * DEV_BSIZE);
+ od->nblk = 0;
+
+ free (xs, M_TEMP);
+ sc_link->active--;
+ sc_link->opennings++;
+
+ /* write sector back */
+ cmd.op_code = WRITE_BIG;
+ scsi_uto4b (od->blkno / blocks_per_sector, &cmd.addr_3);
+ scsi_uto2b (1, &cmd.length2);
+ cmd.byte2 = cmd.reserved = cmd.control = 0;
+ if (scsi_scsi_cmd (sc_link, (struct scsi_generic*) &cmd,
+ sizeof(cmd), od->buf, od->params.secsiz, OD_RETRIES,
+ 100000, bp, od->ioflags | SCSI_DATA_OUT)
+ != SUCCESSFULLY_QUEUED) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ }
+ return -2;
+ }
+ return 0;
+doio:
+ if (od->nblk == 0)
+ return 0;
+
+ free (xs, M_TEMP);
+ sc_link->active--;
+ sc_link->opennings++;
+
+ if (od_io (sc_link, bp) < 0) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ }
+ return -2;
+}
+
/*
* 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,
@@ -574,8 +726,6 @@
register struct scsi_link *sc_link = SCSI_LINK(&od_switch, unit);
register struct scsi_data *od = sc_link->sd;
struct buf *bp = 0;
- struct scsi_rw_big cmd;
- u_int32_t blkno, nblk;
u_int32_t secsize;
SC_DEBUG(sc_link, SDEV_DB2, ("odstart "));
@@ -607,55 +757,33 @@
* re-openned
*/
if (!(sc_link->flags & SDEV_MEDIA_LOADED)) {
- goto bad;
+ printf("od%ld: media not loaded\n", unit);
+bad: bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ continue;
}
+
/*
* We have a buf, now we know we are going to go through
* With this thing..
*/
secsize = od->params.secsiz;
- blkno = bp->b_pblkno;
- if (bp->b_bcount & (secsize - 1))
- {
- goto bad;
- }
- nblk = bp->b_bcount / 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_data,
- 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:
+ od->blkno = bp->b_pblkno * (secsize/DEV_BSIZE) +
+ bp->b_blkno % (secsize/DEV_BSIZE);
+ od->nblk = bp->b_bcount / DEV_BSIZE;
+ od->data = bp->b_data;
+ od->ioflags = flags;
+ if (od_io (sc_link, bp) < 0) {
printf("od%ld: oops not queued\n", unit);
- bp->b_error = EIO;
- bp->b_flags |= B_ERROR;
- biodone(bp);
+ goto bad;
+ }
+
+ odqueues++;
+ if(od->dkunit >= 0) {
+ dk_xfer[od->dkunit]++;
+ dk_seek[od->dkunit]++; /* don't know */
+ dk_wds[od->dkunit] += bp->b_bcount >> 6;
}
}
}
>Audit-Trail:
>Unformatted:
help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199801291035.NAA01877>
