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>
next in thread | raw e-mail | index | archive | help
>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:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199801291035.NAA01877>