/* * 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 #if (NSCSI > 0) #include #include #include #include #include #include #include /* spl definitions */ #include #include #include #include #include #include #include #ifdef MACH_KERNEL #else /*MACH_KERNEL*/ #include /* for hz */ #endif /*MACH_KERNEL*/ #include #include #include "vm_param.h" #include #include 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 #ifdef ULTRIX_COMPAT #include #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 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 */