static inline bool scsi_cmd_prevent_allow_medium_removal(SCSI* scsi) { bool res = false; if (scsi->cmd.cmd_type == SCSI_CMD_6) { switch (scsi->cmd.len) { case 0: case 1: storage_enable_media_removal(scsi->storage); res = true; break; case 2: case 3: storage_disable_media_removal(scsi->storage); res = true; break; } } else scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR); #if (SCSI_DEBUG_FLOW) printf("SCSI prevent/allow medium removal: %d\n\r", scsi->cmd.len); #endif return res; }
int do_rtpg(int fd, void* resp, long resplen, unsigned int timeout) { struct rtpg_command cmd; struct sg_io_hdr hdr; unsigned char sense[SENSE_BUFF_LEN]; memset(&cmd, 0, sizeof(cmd)); cmd.op = OPERATION_CODE_RTPG; rtpg_command_set_service_action(&cmd); set_uint32(cmd.length, resplen); PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); memset(&hdr, 0, sizeof(hdr)); hdr.interface_id = 'S'; hdr.cmdp = (unsigned char *) &cmd; hdr.cmd_len = sizeof(cmd); hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.dxferp = resp; hdr.dxfer_len = resplen; hdr.mx_sb_len = sizeof(sense); hdr.sbp = sense; hdr.timeout = get_prio_timeout(timeout, SGIO_TIMEOUT); if (ioctl(fd, SG_IO, &hdr) < 0) return -RTPG_RTPG_FAILED; if (scsi_error(&hdr)) { PRINT_DEBUG("do_rtpg: SCSI error!\n"); return -RTPG_RTPG_FAILED; } PRINT_HEX(resp, resplen); return 0; }
static inline bool scsi_cmd_request_sense(SCSI* scsi) { bool res = false; //f**ing microsoft suppose, that is SCSI 12 command. WTF??? if (scsi->cmd.cmd_type == SCSI_CMD_6 || scsi->cmd.cmd_type == SCSI_CMD_12) { uint8_t code; uint16_t asq; if (scsi->error_head != scsi->error_tail) { code = scsi->error_queue[scsi->error_tail].code; asq = scsi->error_queue[scsi->error_tail].asq; scsi->error_tail = (scsi->error_tail + 1) & SCSI_ERROR_BUF_MASK; } //empty queue else { code = SENSE_KEY_NO_SENSE; asq = ASQ_NO_ADDITIONAL_SENSE_INFORMATION; } scsi_fill_error_page(scsi, code, asq); res = true; #if (SCSI_DEBUG_FLOW) printf("SCSI request sense 0x%02X, ASQ: 0x%04X\n\r", code, asq); #endif } else scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR); return res; }
static inline bool scsi_cmd_inquiry(SCSI* scsi) { bool res = false; if (scsi->cmd.cmd_type == SCSI_CMD_6) { //vital data page request if (scsi->cmd.flags & SCSI_INQUIRY_EVPD) { switch (scsi->cmd.address) { case INQUIRY_VITAL_PAGE_SUPPORTED_PAGES: scsi_fill_evpd_page_00(scsi); res = true; break; case INQUIRY_VITAL_PAGE_DEVICE_INFO: scsi_fill_evpd_page_83(scsi); res = true; break; case INQUIRY_VITAL_PAGE_SERIAL_NUM: scsi_fill_evpd_page_80(scsi); res = true; break; default: break; } #if (SCSI_DEBUG_FLOW) printf("SCSI: inquiry vital data page %02X\n\r", scsi->cmd.address); #endif } //standart inquiry else { scsi_fill_standart_inquiry_page(scsi); #if (SCSI_DEBUG_FLOW) printf("SCSI: standart inquiry\n\r"); #endif res = true; } if (!res) scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_INVALID_FIELD_IN_CDB); } else scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR); return res; }
static inline bool scsi_cmd_read_capacity(SCSI* scsi) { bool res = false; if (scsi->cmd.cmd_type == SCSI_CMD_10) { if (storage_check_media(scsi->storage)) { res = true; scsi_fill_capacity_page(scsi); #if (SCSI_DEBUG_FLOW) printf("SCSI: read capacity 0x%08X sectors, sector size: %d\n\r", storage_get_media_descriptor(scsi->storage)->num_sectors, storage_get_device_descriptor(scsi->storage)->sector_size); #endif } else scsi_error(scsi, SENSE_KEY_NOT_READY, ASQ_MEDIUM_NOT_PRESENT); } else scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR); return res; }
static inline bool scsi_cmd_verify16(SCSI* scsi) { bool res = false; if (scsi->cmd.cmd_type == SCSI_CMD_16) res = scsi_verify(scsi, scsi->cmd.additional_data, scsi->cmd.len); else scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR); #if (SCSI_DEBUG_FLOW) printf("SCSI verify16 0x%08X, %x sector(s)\n\r", scsi->cmd.address, scsi->cmd.len); #endif return res; }
static inline bool scsi_cmd_write12(SCSI* scsi) { bool res = false; if (scsi->cmd.cmd_type == SCSI_CMD_12) res = scsi_write(scsi, scsi->cmd.address, scsi->cmd.len); else scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR); #if (SCSI_DEBUG_FLOW) printf("SCSI write12 0x%08X, %d sector(s)\n\r", scsi->cmd.address, scsi->cmd.len); #endif //SCSI_DEBUG_FLOW return res; }
static inline bool scsi_cmd_read6(SCSI* scsi) { bool res = false; if (scsi->cmd.cmd_type == SCSI_CMD_6) res = scsi_read(scsi, scsi->cmd.address, scsi->cmd.len ? scsi->cmd.len : 256); else scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR); #if (SCSI_DEBUG_FLOW) printf("SCSI read6 0x%08X, %x sector(s)\n\r", scsi->cmd.address, scsi->cmd.len ? scsi->cmd.len : 256); #endif return res; }
static inline bool scsi_cmd_synchronize_cache(SCSI* scsi) { bool res = false; if (scsi->cmd.cmd_type == SCSI_CMD_10) { storage_write_cache(scsi->storage); res = true; #if (SCSI_DEBUG_FLOW) printf("SCSI: synchronize cache\n\r"); #endif } else scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR); return res; }
static inline bool scsi_cmd_mode_select6(SCSI* scsi) { bool res = false; if (scsi->cmd.cmd_type == SCSI_CMD_6) { switch((scsi->cmd.address >> 8) & 0x3f) { case 0x1c: res = true; break; default: scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_INVALID_FIELD_IN_CDB); #if (SCSI_DEBUG_UNSUPPORTED) printf("SCSI: Mode select: unsupported page 0x%02X\n\r", (scsi->cmd.address >> 8) & 0x3f); #endif } } else
/* * Helper function to setup and run a SCSI inquiry command. */ int do_inquiry(int fd, int evpd, unsigned int codepage, void *resp, int resplen, unsigned int timeout) { struct inquiry_command cmd; struct sg_io_hdr hdr; unsigned char sense[SENSE_BUFF_LEN]; memset(&cmd, 0, sizeof(cmd)); cmd.op = OPERATION_CODE_INQUIRY; if (evpd) { inquiry_command_set_evpd(&cmd); cmd.page = codepage; } set_uint16(cmd.length, resplen); PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); memset(&hdr, 0, sizeof(hdr)); hdr.interface_id = 'S'; hdr.cmdp = (unsigned char *) &cmd; hdr.cmd_len = sizeof(cmd); hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.dxferp = resp; hdr.dxfer_len = resplen; hdr.sbp = sense; hdr.mx_sb_len = sizeof(sense); hdr.timeout = get_prio_timeout(timeout, SGIO_TIMEOUT); if (ioctl(fd, SG_IO, &hdr) < 0) { PRINT_DEBUG("do_inquiry: IOCTL failed!\n"); return -RTPG_INQUIRY_FAILED; } if (scsi_error(&hdr)) { PRINT_DEBUG("do_inquiry: SCSI error!\n"); return -RTPG_INQUIRY_FAILED; } PRINT_HEX((unsigned char *) resp, resplen); return 0; }
/** Sends a SCSI command to the drive, receives reply and evaluates wether the command succeeded or shall be retried or finally failed. Returned SCSI errors shall not lead to a return value indicating failure. The callers get notified by c->error. An SCSI failure which leads not to a retry shall be notified via scsi_notify_error(). The Libburn_log_sg_commandS facility might be of help when problems with a drive have to be examined. It shall stay disabled for normal use. @return: 1 success , <=0 failure */ int sg_issue_command(struct burn_drive *d, struct command *c) { int done = 0; int err; union ccb *ccb; if (d->cam == NULL) { c->error = 0; return 0; } c->error = 0; ccb = cam_getccb(d->cam); cam_fill_csio(&ccb->csio, 1, /* retries */ NULL, /* cbfncp */ CAM_DEV_QFRZDIS, /* flags */ MSG_SIMPLE_Q_TAG, /* tag_action */ NULL, /* data_ptr */ 0, /* dxfer_len */ sizeof (ccb->csio.sense_data), /* sense_len */ 0, /* cdb_len */ 30*1000); /* timeout */ switch (c->dir) { case TO_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_OUT; break; case FROM_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_IN; break; case NO_TRANSFER: ccb->csio.ccb_h.flags |= CAM_DIR_NONE; break; } ccb->csio.cdb_len = c->oplen; memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen); if (c->page) { ccb->csio.data_ptr = c->page->data; if (c->dir == FROM_DRIVE) { ccb->csio.dxfer_len = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { /* ts A61115: removed a ssert() */ if(c->page->bytes <= 0) return 0; ccb->csio.dxfer_len = c->page->bytes; } } else { ccb->csio.data_ptr = NULL; ccb->csio.dxfer_len = 0; } do { memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); err = cam_send_ccb(d->cam, ccb); if (err == -1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Failed to transfer command to drive", errno, 0); cam_freeccb(ccb); sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; c->error = 1; return -1; } /* XXX */ memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len); if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (!c->retry) { c->error = 1; cam_freeccb(ccb); return 1; } switch (scsi_error(d, c->sense, 0)) { case RETRY: done = 0; break; case FAIL: done = 1; c->error = 1; break; } } else { done = 1; } } while (!done); cam_freeccb(ccb); return 1; }
/*ARGSUSED*/ scsi_ret_t scdisk_open( target_info_t *tgt, io_req_t req) { register int i; register scsi_ret_t ret = SCSI_RET_SUCCESS; unsigned int disk_size, secs_per_cyl, sector_size; unsigned int nsectors, ntracks, ncylinders; scsi_rcap_data_t *cap; struct disklabel *label = 0; io_req_t ior; void (*readfun)( target_info_t *tgt, unsigned int secno, io_req_t ior) = scdisk_read; char *data, *rdata = 0; boolean_t look_for_label; if (tgt->flags & TGT_ONLINE) return SCSI_RET_SUCCESS; tgt->lun = rzlun(req->io_unit); /* * Dummy ior for proper sync purposes */ io_req_alloc(ior); bzero((char *)ior, sizeof(*ior)); simple_lock_init(&(ior)->io_req_lock, ETAP_IO_REQ); #if 0 /* * Set the LBN to tgt->block_size with a MODE SELECT. * xxx do a MODE SENSE instead ? */ /* * Ugh. Can't use tgt->block_size here -- not set up * yet. Also can't use DEV_BSIZE. So...since MK6 * dispenses with this command entirely, let's do * so as well. */ for (i = 0; i < 5; i++) { ior->io_op = IO_INTERNAL; ior->io_error = 0; ret = scdisk_mode_select(tgt, tgt->block_size, ior,0,0,FALSE); if (ret == SCSI_RET_SUCCESS) break; if (ret == SCSI_RET_RETRY) { timeout((timeout_fcn_t)wakeup, tgt, 2*hz); await(tgt); } if (ret == SCSI_RET_DEVICE_DOWN) goto done; } if (ret != SCSI_RET_SUCCESS) { scsi_error( tgt, SCSI_ERR_MSEL, ret, 0); ret = D_INVALID_SIZE; goto done; } #endif #ifdef hp_pa if (tgt->flags & TGT_REMOVABLE_MEDIA) { scsi_mode_sense_page5_t *page5; unsigned char mode_save[0xff]; scsi_mode_select_param_t *parm; int length; if((ret = scsi_mode_sense(tgt, 0x05, 0xff, 0)) != SCSI_RET_SUCCESS) goto done; length = *tgt->cmd_ptr + 1; bcopy(tgt->cmd_ptr, mode_save, length); page5 = (scsi_mode_sense_page5_t *)(mode_save + 4 + mode_save[3]); #if 1 /* force sector size to 512 */ parm = (scsi_mode_select_param_t *)mode_save; parm->reserved1 = 0; parm->medium_type = 2; parm->device_spec &= ~0x90; parm->descs[0].density_code = 2; parm->descs[0].nblocks1 = 0; parm->descs[0].nblocks2 = 0; parm->descs[0].nblocks3 = 0; parm->descs[0].reclen1 = 0x2; parm->descs[0].reclen2 = 0; parm->descs[0].reclen3 = 0; page5->ps = 0; page5->page_code &= ~0x80; page5->sectors_per_track = page5->sectors_per_track * (page5->bytes_per_sector_msb << 8 | page5->bytes_per_sector_lsb) / 512; page5->bytes_per_sector_msb = 2; page5->bytes_per_sector_lsb = 0; length -= parm->desc_len; parm->desc_len = 0; bcopy((const char*)page5, mode_save+4, sizeof(*page5)); if((ret = scdisk_mode_select(tgt, 0, 0, mode_save, length, 0)) != SCSI_RET_SUCCESS) goto done; if((ret = scsi_mode_sense(tgt, 0x05, 0xff, 0)) != SCSI_RET_SUCCESS) goto done; length = *tgt->cmd_ptr + 1; bcopy(tgt->cmd_ptr, mode_save, length); #endif ntracks = page5->number_of_heads; nsectors = page5->sectors_per_track; sector_size = page5->bytes_per_sector_msb << 8 | page5->bytes_per_sector_lsb; ncylinders = page5->number_of_cylinders_msb << 8 | page5->number_of_cylinders_lsb; secs_per_cyl = nsectors * ntracks; look_for_label = FALSE; geom_done = TRUE; } #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_RETRY) { timeout((timeout_fcn_t)wakeup, tgt, 2*hz); await(tgt); } if (ret == SCSI_RET_DEVICE_DOWN) goto done; } if (ret != SCSI_RET_SUCCESS) { scsi_error( tgt, SCSI_ERR_MSEL, ret, 0); ret = D_INVALID_SIZE; goto done; } cap = (scsi_rcap_data_t*) tgt->cmd_ptr; disk_size = (cap->lba1<<24) | (cap->lba2<<16) | (cap->lba3<< 8) | cap->lba4 + 1; if (scsi_debug) printf("rz%d holds %d blocks\n", tgt->unit_no, disk_size); tgt->block_size = (cap->blen1<<24) | (cap->blen2<<16) | (cap->blen3<<8 ) | cap->blen4; if (scsi_debug) { printf("rz%d block size is %d bytes/block\n", tgt->unit_no, tgt->block_size); } if (tgt->block_size > RZDISK_MAX_SECTOR || tgt->block_size <= 0) { ret = D_INVALID_SIZE; goto done; } rdata = (char *) kalloc(2*tgt->block_size); if (round_page(rdata) == round_page(rdata + tgt->block_size)) data = rdata; else data = (char *)round_page(rdata); #ifdef POWERMAC /* XXX TODO NMGS remove! must be cache aligned for now */ if ((unsigned long)data & 0x1f) data = (char*)((unsigned long)(data + 0x1f) & ~0x1f); if (round_page(data) != round_page(data + tgt->block_size)) data = (char *)round_page(data); #endif /* POWERMAC */ if (disk_size > SCSI_CMD_READ_MAX_LBA) tgt->flags |= TGT_BIG; /* * 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; ior->io_op = IO_INTERNAL; ior->io_error = 0; #ifdef hp_pa if(geom_done) goto setup_label; #endif /* * Find out about the phys disk geometry */ #if PARAGON860 /* * The NCR RAID controller does not support a read capacity command * with the Partial Medium Indicator (PMI) bit set. Therefore we * have to calculate the number of sectors per cylinder from data * in the mode select pages. This method should work for standalone * disks as well. */ /* * Read page 3 to find the number of sectors/track and bytes/sector */ ret = scsi_mode_sense(tgt, 0x03, 0xff, ior); /* scsi_error(...) */ { scsi_mode_sense_page3_t *page3; page3 = (scsi_mode_sense_page3_t *) (((scsi_mode_sense_data_t *)tgt->cmd_ptr) + 1); nsectors = (page3->sectors_per_track_msb << 8) | page3->sectors_per_track_lsb; sector_size = (page3->bytes_per_sector_msb << 8) | page3->bytes_per_sector_lsb; } ior->io_op = IO_INTERNAL; ior->io_error = 0; /* * Read page 4 to find the number of cylinders and tracks/cylinder */ ret = scsi_mode_sense(tgt, 0x04, 0xff, ior); /* scsi_error(...) */ { scsi_mode_sense_page4_t *page4; page4 = (scsi_mode_sense_page4_t *) (((scsi_mode_sense_data_t *)tgt->cmd_ptr) + 1); ncylinders = (page4->number_of_cylinders_msb << 16) | (page4->number_of_cylinders << 8) | page4->number_of_cylinders_lsb; ntracks = page4->number_of_heads; } /* * Calculate the sectors per cylinder (sec/track * tracks/cyl) */ secs_per_cyl = nsectors * ntracks; if (scsi_debug) { printf("rz%d: %d bytes/sec %d sec/track\n", tgt->unit_no, sector_size, nsectors); printf(" %d tracks/cyl %d cyl/unit\n", ntracks, ncylinders); } #else /* PARAGON860 */ /* Read page one to get read / write error recovery info */ ret = scsi_mode_sense(tgt, 0x01, 0xff, ior); if(ret == SCSI_RET_SUCCESS) { scsi_mode_sense_page1_t *page1; unsigned char mode_save[0xff]; int length; length = *tgt->cmd_ptr + 1; bcopy(tgt->cmd_ptr, mode_save, length); page1 = (scsi_mode_sense_page1_t *)(mode_save + 4 + mode_save[3]); *mode_save = 0; /* mode data length */ page1->ps = 0; page1->flags = PAGE1_AWRE | PAGE1_ARRE | PAGE1_TB | PAGE1_PER; /* * Enable automatic reallocation of bad blocks, * Report any recovered errors. */ ior->io_op = IO_INTERNAL; ior->io_error = 0; ret = scdisk_mode_select(tgt, 0, ior, mode_save, length, 0); if(ret != SCSI_RET_SUCCESS) { if (scsi_debug) printf("rz%d: Can't change error recovery parameters\n", tgt->unit_no); } } ior->io_op = IO_INTERNAL; ior->io_error = 0; #ifdef POWERMAC tgt->flags |= TGT_OPTIONAL_CMD; #endif ret = scsi_read_capacity( tgt, 1, ior); #ifdef POWERMAC tgt->flags &= ~TGT_OPTIONAL_CMD; #endif /* scsi_error(...) */ if (ret) { secs_per_cyl = 16; sector_size = tgt->block_size; } else { 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; } if (scsi_debug) printf("rz%d: %d sect/cyl %d bytes/sec\n", tgt->unit_no, secs_per_cyl, sector_size); #endif /* PARAGON860 */ #if NSCSI2 > 0 /* * ... and a bunch of other things for scsi2 */ #endif /* NSCSI2 > 0 */ /* * Get partition table off pack */ if (tgt->dev_ops == &scsi_devsw[SCSI_CDROM]) { /* no label on a CD-ROM */ look_for_label = FALSE; } else { look_for_label = TRUE; } setup_label: if (look_for_label) { /* first look for a BSD label */ ior->io_data = data; ior->io_count = tgt->block_size; ior->io_op = IO_READ; ior->io_error = 0; tgt->ior = ior; (*readfun)( tgt, LABELOFFSET/tgt->block_size, ior); iowait(ior); if (!ior->io_error) { /* Search for BSD label, might be a bit further along */ register int j; for (i = LABELOFFSET % tgt->block_size; i < (tgt->block_size-sizeof(struct disklabel)); i += sizeof(int)) { label = (struct disklabel *) &data[i]; if (label->d_magic == DISKMAGIC && label->d_magic2 == DISKMAGIC) { break; } else label = (struct disklabel *) 0; } } } else { label = (struct disklabel *) 0; } if (label) { if (scsi_debug) printf("{Using BSD label}"); tgt->dev_info.disk.l = *label; } else { /* then look for a vendor's label, but first fill in defaults and what we found */ label = &tgt->dev_info.disk.l; *label = scsi_default_label; label->d_secsize = sector_size; label->d_nsectors = nsectors; label->d_ntracks = ntracks; label->d_ncylinders = ncylinders; label->d_secpercyl = secs_per_cyl; label->d_secperunit = disk_size; ior->io_data = data; if (!look_for_label || !rz_vendor_label(tgt, label, ior)) { if (look_for_label) { printf("%s rz%d, %s\n", "WARNING: No valid disk label on", tgt->unit_no, "using defaults"); } /* Validate partitions a and c for initialization */ tgt->dev_info.disk.l.d_partitions[0].p_offset = 0; tgt->dev_info.disk.l.d_partitions[0].p_size = disk_size; tgt->dev_info.disk.l.d_partitions[2].p_offset = 0; tgt->dev_info.disk.l.d_partitions[2].p_size = disk_size; tgt->dev_info.disk.l.d_partitions[MAXPARTITIONS].p_offset = 0; tgt->dev_info.disk.l.d_partitions[MAXPARTITIONS].p_size = -1; } label->d_checksum = 0; label->d_checksum = dkcksum(label); } ret = SCSI_RET_SUCCESS; done: if (rdata) kfree((vm_offset_t) rdata, 2 * tgt->block_size); io_req_free(ior); return ret; }