diff options
Diffstat (limited to 'scsi/rz_disk.c')
-rw-r--r-- | scsi/rz_disk.c | 1222 |
1 files changed, 1222 insertions, 0 deletions
diff --git a/scsi/rz_disk.c b/scsi/rz_disk.c new file mode 100644 index 0000000..87a992b --- /dev/null +++ b/scsi/rz_disk.c @@ -0,0 +1,1222 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * File: rz_disk.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Top layer of the SCSI driver: interface with the MI. + * This file contains operations specific to disk-like devices. + * + * Modified by Kevin T. Van Maren to use a common partition code + * with the ide driver, and support 'slices'. + */ + + +#include <scsi/scsi.h> +#if (NSCSI > 0) + +#include <device/buf.h> +#include <device/disk_status.h> +#include <device/device_types.h> +#include <device/param.h> +#include <device/errno.h> + +#include <kern/time_out.h> +#include <machine/machspl.h> /* spl definitions */ +#include <mach/std_types.h> +#include <platforms.h> + +#include <scsi/compat_30.h> +#include <scsi/scsi.h> +#include <scsi/scsi_defs.h> +#include <scsi/rz.h> +#include <scsi/rz_labels.h> + +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ +#include <sys/kernel.h> /* for hz */ +#endif /*MACH_KERNEL*/ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include "vm_param.h" +#include <vm/vm_kern.h> +#include <vm/pmap.h> + +extern void scdisk_read(), scdisk_write(), + scsi_long_read(), scsi_long_write(); + +void scdisk_start(); /* forwards */ +void scdisk_start_rw(); +unsigned dkcksum(); + +#if 0 +struct diskpart scsi_array[8*64]; +#endif 0 + + +/* THIS IS THE BOTTOM LAYER FOR THE SCSI PARTITION CODE */ +typedef struct scsi_driver_info { + target_info_t *tgt; + io_req_t ior; + void (*readfun)(); + int sectorsize; +} scsi_driver_info; + +int scsi_read_fun(scsi_driver_info *param, int sectornum, void *buff) +{ + char *psave; + int result = TRUE; /* SUCCESS */ + psave=param->ior->io_data; /* is this necessary ? */ + + param->ior->io_data=buff; + param->ior->io_count = param->sectorsize; + param->ior->io_op = IO_READ; + param->ior->io_error = 0; + param->tgt->ior = param->ior; + + (*param->readfun)( param->tgt, sectornum, param->ior); + iowait(param->ior); + + param->ior->io_data=psave; /* restore the io_data pointer ?? */ + return(result); +} + + + +/* + * Specialized side of the open routine for disks + */ +scsi_ret_t scdisk_open(tgt, req) + target_info_t *tgt; + io_req_t req; +{ + register int i, dev_bsize; + scsi_ret_t ret = /* SCSI_RET_SUCCESS; */ -1; + unsigned int disk_size, secs_per_cyl, sector_size; + scsi_rcap_data_t *cap; + struct disklabel *label; + io_req_t ior; + void (*readfun)() = scdisk_read; + char *data = (char *)0; + + int numpart; + + scsi_driver_info scsi_info; + char drive_name[10]; /* used for disklabel strings */ + + if (tgt->flags & TGT_ONLINE) + return SCSI_RET_SUCCESS; + + /* + * Dummy ior for proper sync purposes + */ + io_req_alloc(ior,0); + ior->io_next = 0; + ior->io_count = 0; + + /* + * Set the LBN to DEV_BSIZE with a MODE SELECT. + * If this fails we try a couple other tricks. + */ + dev_bsize = 0; + for (i = 0; i < 5; i++) { + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ret = scdisk_mode_select(tgt, DEV_BSIZE, ior, 0, 0, 0); + if (ret == SCSI_RET_SUCCESS) { + dev_bsize = DEV_BSIZE; + break; + } + if (ret == SCSI_RET_RETRY) { + timeout(wakeup, tgt, 2*hz); + await(tgt); + } + if (ret == SCSI_RET_DEVICE_DOWN) + goto done; + } +#if 0 + if (ret != SCSI_RET_SUCCESS) { + scsi_error( tgt, SCSI_ERR_MSEL, ret, 0); + ret = D_INVALID_SIZE; + goto done; + } +#endif + /* + * Do a READ CAPACITY to get max size. Check LBN too. + */ + for (i = 0; i < 5; i++) { + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ret = scsi_read_capacity(tgt, 0, ior); + if (ret == SCSI_RET_SUCCESS) + break; + } + if (ret == SCSI_RET_SUCCESS) { + int val; + + cap = (scsi_rcap_data_t*) tgt->cmd_ptr; + disk_size = (cap->lba1<<24) | + (cap->lba2<<16) | + (cap->lba3<< 8) | + cap->lba4; + if (scsi_debug) + printf("rz%d holds %d blocks\n", tgt->unit_no, disk_size); + val = (cap->blen1<<24) | + (cap->blen2<<16) | + (cap->blen3<<8 ) | + cap->blen4; + if (dev_bsize == 0) + dev_bsize = val; + else + if (val != dev_bsize) panic("read capacity bad"); + + if (disk_size > SCSI_CMD_READ_MAX_LBA) + tgt->flags |= TGT_BIG; + + } else { + printf("Unknown disk capacity??\n"); + disk_size = -1; + } + /* + * Mandatory long-form commands ? + */ + if (BGET(scsi_use_long_form,(unsigned char)tgt->masterno,tgt->target_id)) + tgt->flags |= TGT_BIG; + if (tgt->flags & TGT_BIG) + readfun = scsi_long_read; + + /* + * Some CDROMS truly dislike 512 as LBN. + * Use a MODE_SENSE to cover for this case. + */ + if (dev_bsize == 0) { + scsi_mode_sense_data_t *m; + + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ret = scsi_mode_sense(tgt, 0/*pagecode*/, 32/*?*/, ior); + if (ret == SCSI_RET_SUCCESS) { + m = (scsi_mode_sense_data_t *) tgt->cmd_ptr; + dev_bsize = + m->bdesc[0].blen_msb << 16 | + m->bdesc[0].blen << 8 | + m->bdesc[0].blen_lsb; + } + } + + /* + * Find out about the phys disk geometry -- scsi specific + */ + + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ret = scsi_read_capacity( tgt, 1, ior); + if (ret == SCSI_RET_SUCCESS) { + cap = (scsi_rcap_data_t*) tgt->cmd_ptr; + secs_per_cyl = (cap->lba1<<24) | (cap->lba2<<16) | + (cap->lba3<< 8) | cap->lba4; + secs_per_cyl += 1; + sector_size = (cap->blen1<<24) | (cap->blen2<<16) | + (cap->blen3<<8 ) | cap->blen4; + } else { + sector_size = dev_bsize ? dev_bsize : DEV_BSIZE; + secs_per_cyl = disk_size; + } + if (dev_bsize == 0) + dev_bsize = sector_size; + + if (scsi_debug) + printf("rz%d: %d sect/cyl %d bytes/sec\n", tgt->unit_no, + secs_per_cyl, sector_size); + + /* + * At this point, one way or other, we are committed to + * a given disk capacity and sector size. + */ + tgt->block_size = dev_bsize; + + /* + * Get partition table off pack + */ + +#ifdef MACH_KERNEL + ior->io_data = (char *)kalloc(sector_size); +#endif /*MACH_KERNEL*/ + + scsi_info.tgt=tgt; + scsi_info.ior=ior; + scsi_info.readfun=readfun; + scsi_info.sectorsize=sector_size; + + /* label has NOT been allocated space yet! set to the tgt disklabel */ + label=&scsi_info.tgt->dev_info.disk.l; + + sprintf(drive_name, "sd%d:", tgt->unit_no); + + if (scsi_debug) + printf("Using bogus geometry: 32 sectors/track, 64 heads\n"); + + fudge_bsd_label(label, DTYPE_SCSI, disk_size /* /(32*64)*/ , + 64, 32, sector_size, 8); + + numpart=get_only_partition(&scsi_info, (*scsi_read_fun), + tgt->dev_info.disk.scsi_array, MAX_SCSI_PARTS, disk_size, drive_name); + + printf("%s %d partitions found\n",drive_name,numpart); + + ret=SCSI_RET_SUCCESS; /* if 0, return SCSI_RET_SUCCESS */ + + +done: + io_req_free(ior); + + return(ret); +} + + +/* + * Disk strategy routine + */ +int scdisk_strategy(ior) + register io_req_t ior; +{ + target_info_t *tgt; + register scsi_softc_t *sc; + register int i = ior->io_unit, part; + register unsigned rec, max; + spl_t s; + struct diskpart *label; + + sc = scsi_softc[rzcontroller(i)]; + tgt = sc->target[rzslave(i)]; + part = rzpartition(i); + + /* + * Validate request + */ + + /* readonly ? */ + if ((tgt->flags & TGT_READONLY) && + (ior->io_op & (IO_READ|IO_INTERNAL) == 0)) { + ior->io_error = D_READ_ONLY; + ior->io_op |= IO_ERROR; + ior->io_residual = ior->io_count; + iodone(ior); + return ior->io_error; + } + + rec = ior->io_recnum; + + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + if (!label) { + if (scsi_debug) + printf("sc strategy -- bad partition\n"); + ior->io_error = D_INVALID_SIZE; + ior->io_op |= IO_ERROR; + ior->io_residual = ior->io_count; + iodone(ior); + return ior->io_error; + } + else max=label->size; + if (max == -1) /* what about 0? */ + max = tgt->dev_info.disk.l.d_secperunit - + + label->start; + + i = (ior->io_count + tgt->block_size - 1) / tgt->block_size; + if (((rec + i) > max) || (ior->io_count < 0) || +#if later + ((rec <= LABELSECTOR) && ((tgt->flags & TGT_WRITE_LABEL) == 0)) +#else + FALSE +#endif + ) { + ior->io_error = D_INVALID_SIZE; + ior->io_op |= IO_ERROR; + ior->io_residual = ior->io_count; + iodone(ior); + return ior->io_error; + } + /* + * Find location on disk: secno and cyl (for disksort) + */ + rec += label->start; + ior->io_residual = rec / tgt->dev_info.disk.l.d_secpercyl; + + /* + * Enqueue operation + */ + s = splbio(); + simple_lock(&tgt->target_lock); + if (tgt->ior) { + disksort(tgt->ior, ior); + simple_unlock(&tgt->target_lock); + splx(s); + } else { + ior->io_next = 0; + tgt->ior = ior; + simple_unlock(&tgt->target_lock); + splx(s); + + scdisk_start(tgt,FALSE); + } + + return D_SUCCESS; +} + +/*#define CHECKSUM*/ +#ifdef CHECKSUM +int max_checksum_size = 0x2000; +#endif CHECKSUM + +/* + * Start/completion routine for disks + */ +void scdisk_start(tgt, done) + target_info_t *tgt; + boolean_t done; +{ + register io_req_t ior = tgt->ior; + register unsigned part; +#ifdef CHECKSUM + register unsigned secno; +#endif + struct diskpart *label; + + if (ior == 0) + return; + + if (tgt->flags & TGT_BBR_ACTIVE) + { + scdisk_bbr_start(tgt, done); + return; + } + + if (done) { + register unsigned int xferred; + unsigned int max_dma_data; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + /* see if we must retry */ + if ((tgt->done == SCSI_RET_RETRY) && + ((ior->io_op & IO_INTERNAL) == 0)) { + delay(1000000);/*XXX*/ + goto start; + } else + /* got a bus reset ? pifff.. */ + if ((tgt->done == (SCSI_RET_ABORTED|SCSI_RET_RETRY)) && + ((ior->io_op & IO_INTERNAL) == 0)) { + if (xferred = ior->io_residual) { + ior->io_data -= xferred; + ior->io_count += xferred; + ior->io_recnum -= xferred / tgt->block_size; + ior->io_residual = 0; + } + goto start; + } else + /* + * Quickly check for errors: if anything goes wrong + * we do a request sense, see if that is what we did. + */ + if (tgt->cur_cmd == SCSI_CMD_REQUEST_SENSE) { + scsi_sense_data_t *sns; + unsigned int blockno; + char *outcome; + + ior->io_op = ior->io_temporary; + + sns = (scsi_sense_data_t *)tgt->cmd_ptr; + if (sns->addr_valid) + blockno = sns->u.xtended.info0 << 24 | + sns->u.xtended.info1 << 16 | + sns->u.xtended.info2 << 8 | + sns->u.xtended.info3; + else { + part = rzpartition(ior->io_unit); + label = lookup_part(tgt->dev_info.disk.scsi_array, part); + blockno = label->start; + blockno += ior->io_recnum; + if (!label) blockno=-1; + } + + if (scsi_check_sense_data(tgt, sns)) { + ior->io_error = 0; + if ((tgt->done == SCSI_RET_RETRY) && + ((ior->io_op & IO_INTERNAL) == 0)) { + delay(1000000);/*XXX*/ + goto start; + } + outcome = "Recovered"; + } else { + outcome = "Unrecoverable"; + ior->io_error = D_IO_ERROR; + ior->io_op |= IO_ERROR; + } + if ((tgt->flags & TGT_OPTIONAL_CMD) == 0) { + printf("%s Error, rz%d: %s%s%d\n", outcome, + tgt->target_id + (tgt->masterno * 8), + (ior->io_op & IO_READ) ? "Read" : + ((ior->io_op & IO_INTERNAL) ? "(command)" : "Write"), + " disk error, phys block no. ", blockno); + + scsi_print_sense_data(sns); + + /* + * On fatal read/write errors try replacing the bad block + * The bbr routine will return TRUE iff it took control + * over the target for all subsequent operations. In this + * event, the queue of requests is effectively frozen. + */ + if (ior->io_error && + ((sns->error_class == SCSI_SNS_XTENDED_SENSE_DATA) && + ((sns->u.xtended.sense_key == SCSI_SNS_HW_ERR) || + (sns->u.xtended.sense_key == SCSI_SNS_MEDIUM_ERR))) && + scdisk_bad_block_repl(tgt, blockno)) + return; + } + } + + /* + * See if we had errors + */ + else if (tgt->done != SCSI_RET_SUCCESS) { + + if (tgt->done == SCSI_RET_NEED_SENSE) { + + ior->io_temporary = ior->io_op; + ior->io_op = IO_INTERNAL; + scsi_request_sense(tgt, ior, 0); + return; + + } else if (tgt->done == SCSI_RET_DEVICE_DOWN) { + ior->io_error = D_DEVICE_DOWN; + ior->io_op |= IO_ERROR; + } else { + printf("%s%x\n", "?rz_disk Disk error, ret=x", tgt->done); + ior->io_error = D_IO_ERROR; + ior->io_op |= IO_ERROR; + } + } + /* + * No errors. + * See if we requested more than the max + * (We use io_residual in a flip-side way here) + */ + else if (ior->io_count > (xferred = max_dma_data)) { + ior->io_residual += xferred; + ior->io_count -= xferred; + ior->io_data += xferred; + ior->io_recnum += xferred / tgt->block_size; + goto start; + } + else if (xferred = ior->io_residual) { + ior->io_data -= xferred; + ior->io_count += xferred; + ior->io_recnum -= xferred / tgt->block_size; + ior->io_residual = 0; + } /* that's it */ + +#ifdef CHECKSUM + if ((ior->io_op & IO_READ) && (ior->io_count < max_checksum_size)) { + part = rzpartition(ior->io_unit); + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + if (!label) printf("NOT FOUND!\n"); + secno = ior->io_recnum + label->start; + scdisk_bcheck(secno, ior->io_data, ior->io_count); + } +#endif CHECKSUM + + /* dequeue next one */ + { + io_req_t next; + + simple_lock(&tgt->target_lock); + next = ior->io_next; + tgt->ior = next; + simple_unlock(&tgt->target_lock); + + iodone(ior); + if (next == 0) + return; + + ior = next; + } + +#ifdef CHECKSUM + if (((ior->io_op & IO_READ) == 0) && (ior->io_count < max_checksum_size)) { + part = rzpartition(ior->io_unit); + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + secno = ior->io_recnum + label->start; + scdisk_checksum(secno, ior->io_data, ior->io_count); + } +#endif CHECKSUM + } + ior->io_residual = 0; +start: + scdisk_start_rw( tgt, ior); +} + +void scdisk_start_rw( tgt, ior) + target_info_t *tgt; + register io_req_t ior; +{ + unsigned int part, secno; + register boolean_t long_form; + struct diskpart *label; + + part = rzpartition(ior->io_unit); + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + if (!label) + printf("NOT FOUND!\n"); + secno = ior->io_recnum + label->start; + + /* Use long form if either big block addresses or + the size is more than we can fit in one byte */ + long_form = (tgt->flags & TGT_BIG) || + (ior->io_count > (256 * tgt->block_size)); + if (ior->io_op & IO_READ) + (long_form ? scsi_long_read : scdisk_read)(tgt, secno, ior); + else if ((ior->io_op & IO_INTERNAL) == 0) + (long_form ? scsi_long_write : scdisk_write)(tgt, secno, ior); +} + +#include <sys/ioctl.h> +#ifdef ULTRIX_COMPAT +#include <mips/PMAX/rzdisk.h> +#endif /*ULTRIX_COMPAT*/ + +io_return_t +scdisk_get_status(dev, tgt, flavor, status, status_count) + int dev; + target_info_t *tgt; + dev_flavor_t flavor; + dev_status_t status; + natural_t *status_count; +{ + struct disklabel *lp; + struct diskpart *label; + + lp = &tgt->dev_info.disk.l; + + switch (flavor) { +#ifdef MACH_KERNEL + case DEV_GET_SIZE: + + label=lookup_part(tgt->dev_info.disk.scsi_array, rzpartition(dev)); + status[DEV_GET_SIZE_DEVICE_SIZE] = label->size * lp->d_secsize; + status[DEV_GET_SIZE_RECORD_SIZE] = tgt->block_size; + *status_count = DEV_GET_SIZE_COUNT; + break; +#endif + + case DIOCGDINFO: + *(struct disklabel *)status = *lp; +#ifdef MACH_KERNEL + *status_count = sizeof(struct disklabel)/sizeof(int); +#endif MACH_KERNEL + break; + + case DIOCGDINFO - (0x10<<16): + *(struct disklabel *)status = *lp; +#ifdef MACH_KERNEL + *status_count = sizeof(struct disklabel)/sizeof(int) - 4; +#endif MACH_KERNEL + break; + +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ +#if ULTRIX_COMPAT + case SCSI_MODE_SENSE: /*_IOWR(p, 9, struct mode_sel_sns_params) */ + break; + case DIOCGETPT: /*_IOR(p, 1, struct pt) */ + case SCSI_GET_SENSE: /*_IOR(p, 10, struct extended_sense) */ + return ul_disk_ioctl(tgt, flavor, status, status_count); +#endif /*ULTRIX_COMPAT*/ +#endif /*!MACH_KERNEL*/ + +#if 0 + case DIOCRFORMAT: + break; +#endif + default: +#ifdef i386 + return(scsi_i386_get_status(dev, tgt, flavor, status, status_count)); +#else i386 + return(D_INVALID_OPERATION); +#endif i386 + } + return D_SUCCESS; +} + +io_return_t +scdisk_set_status(dev, tgt, flavor, status, status_count) + int dev; + target_info_t *tgt; + dev_flavor_t flavor; + dev_status_t status; + natural_t status_count; +{ + io_return_t error = D_SUCCESS; + struct disklabel *lp; + + lp = &tgt->dev_info.disk.l; + + + switch (flavor) { + case DIOCSRETRIES: +#ifdef MACH_KERNEL + if (status_count != sizeof(int)) + return D_INVALID_SIZE; +#endif /* MACH_KERNEL */ + scsi_bbr_retries = *(int *)status; + break; + + case DIOCWLABEL: + case DIOCWLABEL - (0x10<<16): + if (*(int*)status) + tgt->flags |= TGT_WRITE_LABEL; + else + tgt->flags &= ~TGT_WRITE_LABEL; + break; + case DIOCSDINFO: + case DIOCSDINFO - (0x10<<16): + case DIOCWDINFO: + case DIOCWDINFO - (0x10<<16): +#ifdef MACH_KERNEL + if (status_count != sizeof(struct disklabel) / sizeof(int)) + return D_INVALID_SIZE; +#endif /* MACH_KERNEL */ + error = setdisklabel(lp, (struct disklabel*) status); + if (error || (flavor == DIOCSDINFO) || (flavor == DIOCSDINFO - (0x10<<16))) + return error; + error = scdisk_writelabel(tgt); + break; + +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ +#if ULTRIX_COMPAT + case SCSI_FORMAT_UNIT: /*_IOW(p, 4, struct format_params) */ + case SCSI_REASSIGN_BLOCK: /*_IOW(p, 5, struct reassign_params) */ + case SCSI_READ_DEFECT_DATA: /*_IOW(p, 6, struct read_defect_params) */ + case SCSI_VERIFY_DATA: /*_IOW(p, 7, struct verify_params) */ + case SCSI_MODE_SELECT: /*_IOW(p, 8, struct mode_sel_sns_params) */ + case SCSI_MODE_SENSE: /*_IOW(p, 9, struct mode_sel_sns_params) */ + case SCSI_GET_INQUIRY_DATA: /*_IOW(p, 11, struct inquiry_info) */ + return ul_disk_ioctl(tgt, flavor, status, status_count); +#endif /*ULTRIX_COMPAT*/ +#endif /*!MACH_KERNEL*/ + +#if notyet + case DIOCWFORMAT: + case DIOCSBAD: /* ?? how ? */ +#endif + default: +#ifdef i386 + error = scsi_i386_set_status(dev, tgt, flavor, status, status_count); +#else i386 + error = D_INVALID_OPERATION; +#endif i386 + } + return error; +} + +static int grab_it(tgt, ior) + target_info_t *tgt; + io_req_t ior; +{ + spl_t s; + + s = splbio(); + simple_lock(&tgt->target_lock); + if (!tgt->ior) + tgt->ior = ior; + simple_unlock(&tgt->target_lock); + splx(s); + + if (tgt->ior != ior) + return D_ALREADY_OPEN; + return D_SUCCESS; +} + +/* Write back a label to the disk */ +io_return_t scdisk_writelabel(tgt) + target_info_t *tgt; +{ + +printf("scdisk_writelabel: NO LONGER IMPLEMENTED\n"); +#if 0 +/* Taken out at Bryan's suggestion until 'fixed' for slices */ + + io_req_t ior; + char *data = (char *)0; + struct disklabel *label; + io_return_t error; + int dev_bsize = tgt->block_size; + + io_req_alloc(ior,0); +#ifdef MACH_KERNEL + data = (char *)kalloc(dev_bsize); +#else /*MACH_KERNEL*/ + data = (char *)ior->io_data; +#endif /*MACH_KERNEL*/ + ior->io_next = 0; + ior->io_prev = 0; + ior->io_data = data; + ior->io_count = dev_bsize; + ior->io_op = IO_READ; + ior->io_error = 0; + + if (grab_it(tgt, ior) != D_SUCCESS) { + error = D_ALREADY_OPEN; + goto ret; + } + + scdisk_read( tgt, tgt->dev_info.disk.labelsector, ior); + iowait(ior); + if (error = ior->io_error) + goto ret; + + label = (struct disklabel *) &data[tgt->dev_info.disk.labeloffset]; + *label = tgt->dev_info.disk.l; + + ior->io_next = 0; + ior->io_prev = 0; + ior->io_data = data; + ior->io_count = dev_bsize; + ior->io_op = IO_WRITE; + + while (grab_it(tgt, ior) != D_SUCCESS) ; /* ahem */ + + scdisk_write( tgt, tgt->dev_info.disk.labelsector, ior); + iowait(ior); + + error = ior->io_error; +ret: +#ifdef MACH_KERNEL + if (data) kfree((int)data, dev_bsize); +#endif /*MACH_KERNEL*/ + io_req_free(ior); + return error; + +#endif 0 scdisk_writelabel +return -1; /* FAILURE ? */ +} + +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ +#if ULTRIX_COMPAT + +io_return_t ul_disk_ioctl(tgt, flavor, status, status_count) + target_info_t *tgt; + dev_flavor_t flavor; + dev_status_t status; + natural_t status_count; +{ + io_return_t ret; + scsi_ret_t err = SCSI_RET_ABORTED;/*xxx*/ + io_req_t ior; + + if (!suser()) + return EACCES; + + ior = geteblk(sizeof(struct defect_descriptors)); + ior->io_next = 0; + ior->io_count = 0; + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ior->io_recnum = 0; + ior->io_residual = 0; + + switch (flavor) { + + case DIOCGETPT: { /*_IOR(p, 1, struct pt) */ + scsi_dec_label_t *p; + struct disklabel *lp; + int i; + + lp = &tgt->dev_info.disk.l; + p = (scsi_dec_label_t *)status; + + p->magic = DEC_PARTITION_MAGIC; + p->in_use = 1; + for (i = 0; i < 8; i++) { + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + p->partitions[i].n_sectors = label->size; + p->partitions[i].offset = label->start; + } + err = SCSI_RET_SUCCESS; + } + break; + + case SCSI_GET_SENSE: { /*_IOR(p, 10, struct extended_sense) */ + scsi_sense_data_t *s; + + s = (scsi_sense_data_t*)tgt->cmd_ptr; + bcopy(s, status, sizeof(*s) + s->u.xtended.add_len - 1); + err = SCSI_RET_SUCCESS; + /* only once */ + bzero(tgt->cmd_ptr, sizeof(scsi_sense_data_t)); + } + break; + + case SCSI_GET_INQUIRY_DATA: { /*_IOR(p, 11, struct inquiry_info) */ + struct mode_sel_sns_params *ms; + + ms = (struct mode_sel_sns_params*)status; + err = scsi_inquiry( tgt, SCSI_INQ_STD_DATA); + if (copyout(tgt->cmd_ptr, ms->msp_addr, sizeof(struct inquiry_info))){ + ret = EFAULT; + goto out; + } + } + break; + + case SCSI_FORMAT_UNIT: { /*_IOW(p, 4, struct format_params) */ + struct format_params *fp; + struct defect_descriptors *df; + unsigned char mode; + unsigned int old_timeout; + + fp = (struct format_params *)status; + df = (struct defect_descriptors*)ior->io_data; + if (fp->fp_length != 0) { + if (copyin(fp->fp_addr, df, sizeof(*df))) { + ret = EFAULT; + goto out; + } + ior->io_count = sizeof(*df); + } else + ior->io_count = 0; + mode = fp->fp_format & SCSI_CMD_FMT_LIST_TYPE; + switch (fp->fp_defects) { + case VENDOR_DEFECTS: + mode |= SCSI_CMD_FMT_FMTDATA|SCSI_CMD_FMT_CMPLIST; + break; + case KNOWN_DEFECTS: + mode |= SCSI_CMD_FMT_FMTDATA; + break; + case NO_DEFECTS: + default: + break; + } + old_timeout = scsi_watchdog_period; + scsi_watchdog_period = 60*60; /* 1 hour should be enough, I hope */ + err = scsi_format_unit( tgt, mode, fp->fp_pattern, + fp->fp_interleave, ior); + scsi_watchdog_period = old_timeout; + /* Make sure we re-read all info afresh */ + tgt->flags = TGT_ALIVE | + (tgt->flags & (TGT_REMOVABLE_MEDIA|TGT_FULLY_PROBED)); + } + break; + + case SCSI_REASSIGN_BLOCK: { /*_IOW(p, 5, struct reassign_params) */ + struct reassign_params *r; + int ndef; + + r = (struct reassign_params*) status; + ndef = r->rp_header.defect_len0 | (r->rp_header.defect_len1 >> 8); + ndef >>= 2; + tgt->ior = ior; + (void) scsi_reassign_blocks( tgt, &r->rp_lbn3, ndef, ior); + iowait(ior); + err = tgt->done; + } + break; + + case SCSI_READ_DEFECT_DATA: { /*_IOW(p, 6, struct read_defect_params) */ + struct read_defect_params *dp; + + dp = (struct read_defect_params *)status; + ior->io_count = ior->io_alloc_size; + if (dp->rdp_alclen > ior->io_count) + dp->rdp_alclen = ior->io_count; + else + ior->io_count = dp->rdp_alclen; + ior->io_op |= IO_READ; + tgt->ior = ior; + err = scsi_read_defect(tgt, dp->rdp_format|0x18, ior); + if (copyout(ior->io_data, dp->rdp_addr, dp->rdp_alclen)) { + ret = EFAULT; + goto out; + } + } + break; + + case SCSI_VERIFY_DATA: { /*_IOW(p, 7, struct verify_params) */ + struct verify_params *v; + unsigned int old_timeout; + + old_timeout = scsi_watchdog_period; + scsi_watchdog_period = 5*60; /* 5 mins enough, I hope */ + v = (struct verify_params *)status; + ior->io_count = 0; + err = scdisk_verify( tgt, v->vp_lbn, v->vp_length, ior); + scsi_watchdog_period = old_timeout; + } + break; + + case SCSI_MODE_SELECT: { /*_IOW(p, 8, struct mode_sel_sns_params) */ + struct mode_sel_sns_params *ms; + + ms = (struct mode_sel_sns_params*)status; + if(copyin(ms->msp_addr, ior->io_data, ms->msp_length)) { + ret = EFAULT; + goto out; + } + err = scdisk_mode_select( tgt, DEV_BSIZE, ior, ior->io_data, + ms->msp_length, ms->msp_setps); + } + break; + + case SCSI_MODE_SENSE: { /*_IOWR(p, 9, struct mode_sel_sns_params) */ + struct mode_sel_sns_params *ms; + unsigned char pagecode; + + ms = (struct mode_sel_sns_params*)status; + pagecode = (ms->msp_pgcode & 0x3f) | (ms->msp_pgctrl << 6); + err = scsi_mode_sense( tgt, pagecode, ms->msp_length, ior); + if (copyout(tgt->cmd_ptr, ms->msp_addr, ms->msp_length)){ + ret = EFAULT; + goto out; + } + } + break; + } + + ret = (err == SCSI_RET_SUCCESS) ? D_SUCCESS : D_IO_ERROR; + if (ior->io_op & IO_ERROR) + ret = D_IO_ERROR; +out: + brelse(ior); + return ret; +} +#endif /*ULTRIX_COMPAT*/ +#endif /*!MACH_KERNEL*/ + +#ifdef CHECKSUM + +#define SUMSIZE 0x10000 +#define SUMHASH(b) (((b)>>1) & (SUMSIZE - 1)) +struct { + long blockno; + long sum; +} scdisk_checksums[SUMSIZE]; + +void scdisk_checksum(bno, addr, size) + long bno; + register unsigned int *addr; +{ + register int i = size/sizeof(int); + register unsigned int sum = -1; + + while (i-- > 0) + sum ^= *addr++; + scdisk_checksums[SUMHASH(bno)].blockno = bno; + scdisk_checksums[SUMHASH(bno)].sum = sum; +} + +void scdisk_bcheck(bno, addr, size) + long bno; + register unsigned int *addr; +{ + register int i = size/sizeof(int); + register unsigned int sum = -1; + unsigned int *start = addr; + + if (scdisk_checksums[SUMHASH(bno)].blockno != bno) { +if (scsi_debug) printf("No checksum for block x%x\n", bno); + return; + } + + while (i-- > 0) + sum ^= *addr++; + + if (scdisk_checksums[SUMHASH(bno)].sum != sum) { + printf("Bad checksum (x%x != x%x), bno x%x size x%x at x%x\n", + sum, + scdisk_checksums[bno & (SUMSIZE - 1)].sum, + bno, size, start); + gimmeabreak(); + scdisk_checksums[SUMHASH(bno)].sum = sum; + } +} + + +#endif CHECKSUM + +/*#define PERF */ +#ifdef PERF +int test_read_size = 512; +int test_read_skew = 12; +int test_read_skew_min = 0; +int test_read_nreads = 1000; +int test_read_bdev = 0; + +#include <sys/time.h> + +void test_read(max) +{ + int i, ssk, usecs; + struct timeval start, stop; + + if (max == 0) + max = test_read_skew + 1; + ssk = test_read_skew; + for (i = test_read_skew_min; i < max; i++){ + test_read_skew = i; + + start = time; + read_test(); + stop = time; + + usecs = stop.tv_usec - start.tv_usec; + if (usecs < 0) { + stop.tv_sec -= 1; + usecs += 1000000; + } + printf("Skew %3d size %d count %d time %3d sec %d us\n", + i, test_read_size, test_read_nreads, + stop.tv_sec - start.tv_sec, usecs); + } + test_read_skew = ssk; +} + +void read_test() +{ + static int buffer[(8192*2)/sizeof(int)]; + struct io_req io; + register int i, rec; + + bzero(&io, sizeof(io)); + io.io_unit = test_read_bdev; + io.io_op = IO_READ; + io.io_count = test_read_size; + io.io_data = (char*) buffer; + + for (rec = 0, i = 0; i < test_read_nreads; i++) { + io.io_op = IO_READ; + io.io_recnum = rec; + scdisk_strategy(&io); + rec += test_read_skew; + iowait(&io); + } +} + +void tur_test() +{ + struct io_req io; + register int i; + char *a, *b; + struct timeval start, stop; + + bzero(&io, sizeof(io)); + io.io_unit = test_read_bdev; + io.io_data = (char*)&io;/*unused but kernel space*/ + + start = time; + for (i = 0; i < test_read_nreads; i++) { + io.io_op = IO_INTERNAL; + rz_check(io.io_unit, &a, &b); + scsi_test_unit_ready(b,&io); + } + stop = time; + i = stop.tv_usec - start.tv_usec; + if (i < 0) { + stop.tv_sec -= 1; + i += 1000000; + } + printf("%d test-unit-ready took %3d sec %d us\n", + test_read_nreads, + stop.tv_sec - start.tv_sec, i); +} + +#endif PERF + +/*#define WDEBUG*/ +#ifdef WDEBUG + +int buggo_write_size = 8192; +int buggo_dev = 2; /* rz0b */ /* changed by KTVM from 1 (still b) */ +int buggo_out_buffer[8192/2]; +int buggo_in_buffer[8192/2]; +int buggotest(n, pattern, verbose) +{ + struct io_req io; + register int i, rec; + + if (n <= 0) + n = 1; + + if(pattern) + for (i = 0; i < buggo_write_size/4; i++) + buggo_out_buffer[i] = i + pattern; + + for (i = 0; i < n; i++) { + register int j; + + buggo_out_buffer[0] = i + pattern; + buggo_out_buffer[(buggo_write_size/4)-1] = i + pattern; + bzero(&io, sizeof(io)); + io.io_unit = buggo_dev; + io.io_data = (char*)buggo_out_buffer; + io.io_op = IO_WRITE; + io.io_count = buggo_write_size; + io.io_recnum = i % 1024; + scdisk_strategy(&io); + + bzero(buggo_in_buffer, sizeof(buggo_in_buffer)); + iowait(&io); + + if (verbose) + printf("Done write with %x", io.io_error); + + bzero(&io, sizeof(io)); + io.io_unit = buggo_dev; + io.io_data = (char*)buggo_in_buffer; + io.io_op = IO_READ; + io.io_count = buggo_write_size; + io.io_recnum = i % 1024; + scdisk_strategy(&io); + iowait(&io); + + if (verbose) + printf("Done read with %x", io.io_error); + + for (j = 0; j < buggo_write_size/4; j++) + if (buggo_out_buffer[j] != buggo_in_buffer[j]){ + printf("Difference at %d-th word: %x %x\n", + buggo_out_buffer[j], buggo_in_buffer[j]); + return i; + } + } + printf("Test ok\n"); + return n; +} +#endif WDEBUG +#endif /* NSCSI > 0 */ |