📄 st.c
字号:
{ struct completion *waiting; /* if async, make sure there's no command outstanding */ if (!do_wait && ((STp->buffer)->last_SRpnt)) { printk(KERN_ERR "%s: Async command already active.\n", tape_name(STp)); if (signal_pending(current)) (STp->buffer)->syscall_result = (-EINTR); else (STp->buffer)->syscall_result = (-EBUSY); return NULL; } if (SRpnt == NULL) { SRpnt = st_allocate_request(); if (SRpnt == NULL) { DEBC( printk(KERN_ERR "%s: Can't get SCSI request.\n", tape_name(STp)); ); if (signal_pending(current)) (STp->buffer)->syscall_result = (-EINTR); else (STp->buffer)->syscall_result = (-EBUSY); return NULL; } SRpnt->stp = STp; } /* If async IO, set last_SRpnt. This ptr tells write_behind_check which IO is outstanding. It's nulled out when the IO completes. */ if (!do_wait) (STp->buffer)->last_SRpnt = SRpnt; waiting = &STp->wait; init_completion(waiting); SRpnt->waiting = waiting; if (!STp->buffer->do_dio) buf_to_sg(STp->buffer, bytes); memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd)); STp->buffer->cmdstat.have_sense = 0; STp->buffer->syscall_result = 0; if (scsi_execute_async(STp->device, cmd, COMMAND_SIZE(cmd[0]), direction, &((STp->buffer)->sg[0]), bytes, (STp->buffer)->sg_segs, timeout, retries, SRpnt, st_sleep_done, GFP_KERNEL)) { /* could not allocate the buffer or request was too large */ (STp->buffer)->syscall_result = (-EBUSY); (STp->buffer)->last_SRpnt = NULL; } else if (do_wait) { wait_for_completion(waiting); SRpnt->waiting = NULL; (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); } return SRpnt;}/* Handle the write-behind checking (waits for completion). Returns -ENOSPC if write has been correct but EOM early warning reached, -EIO if write ended in error or zero if write successful. Asynchronous writes are used only in variable block mode. */static int write_behind_check(struct scsi_tape * STp){ int retval = 0; struct st_buffer *STbuffer; struct st_partstat *STps; struct st_cmdstatus *cmdstatp; struct st_request *SRpnt; STbuffer = STp->buffer; if (!STbuffer->writing) return 0; DEB( if (STp->write_pending) STp->nbr_waits++; else STp->nbr_finished++; ) /* end DEB */ wait_for_completion(&(STp->wait)); SRpnt = STbuffer->last_SRpnt; STbuffer->last_SRpnt = NULL; SRpnt->waiting = NULL; (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); st_release_request(SRpnt); STbuffer->buffer_bytes -= STbuffer->writing; STps = &(STp->ps[STp->partition]); if (STps->drv_block >= 0) { if (STp->block_size == 0) STps->drv_block++; else STps->drv_block += STbuffer->writing / STp->block_size; } cmdstatp = &STbuffer->cmdstat; if (STbuffer->syscall_result) { retval = -EIO; if (cmdstatp->have_sense && !cmdstatp->deferred && (cmdstatp->flags & SENSE_EOM) && (cmdstatp->sense_hdr.sense_key == NO_SENSE || cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR)) { /* EOM at write-behind, has all data been written? */ if (!cmdstatp->remainder_valid || cmdstatp->uremainder64 == 0) retval = -ENOSPC; } if (retval == -EIO) STps->drv_block = -1; } STbuffer->writing = 0; DEB(if (debugging && retval) printk(ST_DEB_MSG "%s: Async write error %x, return value %d.\n", tape_name(STp), STbuffer->cmdstat.midlevel_result, retval);) /* end DEB */ return retval;}/* Step over EOF if it has been inadvertently crossed (ioctl not used because it messes up the block number). */static int cross_eof(struct scsi_tape * STp, int forward){ struct st_request *SRpnt; unsigned char cmd[MAX_COMMAND_SIZE]; cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ if (forward) { cmd[2] = cmd[3] = 0; cmd[4] = 1; } else cmd[2] = cmd[3] = cmd[4] = 0xff; /* -1 filemarks */ cmd[5] = 0; DEBC(printk(ST_DEB_MSG "%s: Stepping over filemark %s.\n", tape_name(STp), forward ? "forward" : "backward")); SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE, STp->device->timeout, MAX_RETRIES, 1); if (!SRpnt) return (STp->buffer)->syscall_result; st_release_request(SRpnt); SRpnt = NULL; if ((STp->buffer)->cmdstat.midlevel_result != 0) printk(KERN_ERR "%s: Stepping over filemark %s failed.\n", tape_name(STp), forward ? "forward" : "backward"); return (STp->buffer)->syscall_result;}/* Flush the write buffer (never need to write if variable blocksize). */static int flush_write_buffer(struct scsi_tape * STp){ int offset, transfer, blks; int result; unsigned char cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt; struct st_partstat *STps; result = write_behind_check(STp); if (result) return result; result = 0; if (STp->dirty == 1) { offset = (STp->buffer)->buffer_bytes; transfer = ((offset + STp->block_size - 1) / STp->block_size) * STp->block_size; DEBC(printk(ST_DEB_MSG "%s: Flushing %d bytes.\n", tape_name(STp), transfer)); memset((STp->buffer)->b_data + offset, 0, transfer - offset); memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = WRITE_6; cmd[1] = 1; blks = transfer / STp->block_size; cmd[2] = blks >> 16; cmd[3] = blks >> 8; cmd[4] = blks; SRpnt = st_do_scsi(NULL, STp, cmd, transfer, DMA_TO_DEVICE, STp->device->timeout, MAX_WRITE_RETRIES, 1); if (!SRpnt) return (STp->buffer)->syscall_result; STps = &(STp->ps[STp->partition]); if ((STp->buffer)->syscall_result != 0) { struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat; if (cmdstatp->have_sense && !cmdstatp->deferred && (cmdstatp->flags & SENSE_EOM) && (cmdstatp->sense_hdr.sense_key == NO_SENSE || cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) && (!cmdstatp->remainder_valid || cmdstatp->uremainder64 == 0)) { /* All written at EOM early warning */ STp->dirty = 0; (STp->buffer)->buffer_bytes = 0; if (STps->drv_block >= 0) STps->drv_block += blks; result = (-ENOSPC); } else { printk(KERN_ERR "%s: Error on flush.\n", tape_name(STp)); STps->drv_block = (-1); result = (-EIO); } } else { if (STps->drv_block >= 0) STps->drv_block += blks; STp->dirty = 0; (STp->buffer)->buffer_bytes = 0; } st_release_request(SRpnt); SRpnt = NULL; } return result;}/* Flush the tape buffer. The tape will be positioned correctly unless seek_next is true. */static int flush_buffer(struct scsi_tape *STp, int seek_next){ int backspace, result; struct st_buffer *STbuffer; struct st_partstat *STps; STbuffer = STp->buffer; /* * If there was a bus reset, block further access * to this device. */ if (STp->pos_unknown) return (-EIO); if (STp->ready != ST_READY) return 0; STps = &(STp->ps[STp->partition]); if (STps->rw == ST_WRITING) /* Writing */ return flush_write_buffer(STp); if (STp->block_size == 0) return 0; backspace = ((STp->buffer)->buffer_bytes + (STp->buffer)->read_pointer) / STp->block_size - ((STp->buffer)->read_pointer + STp->block_size - 1) / STp->block_size; (STp->buffer)->buffer_bytes = 0; (STp->buffer)->read_pointer = 0; result = 0; if (!seek_next) { if (STps->eof == ST_FM_HIT) { result = cross_eof(STp, 0); /* Back over the EOF hit */ if (!result) STps->eof = ST_NOEOF; else { if (STps->drv_file >= 0) STps->drv_file++; STps->drv_block = 0; } } if (!result && backspace > 0) result = st_int_ioctl(STp, MTBSR, backspace); } else if (STps->eof == ST_FM_HIT) { if (STps->drv_file >= 0) STps->drv_file++; STps->drv_block = 0; STps->eof = ST_NOEOF; } return result;}/* Set the mode parameters */static int set_mode_densblk(struct scsi_tape * STp, struct st_modedef * STm){ int set_it = 0; unsigned long arg; char *name = tape_name(STp); if (!STp->density_changed && STm->default_density >= 0 && STm->default_density != STp->density) { arg = STm->default_density; set_it = 1; } else arg = STp->density; arg <<= MT_ST_DENSITY_SHIFT; if (!STp->blksize_changed && STm->default_blksize >= 0 && STm->default_blksize != STp->block_size) { arg |= STm->default_blksize; set_it = 1; } else arg |= STp->block_size; if (set_it && st_int_ioctl(STp, SET_DENS_AND_BLK, arg)) { printk(KERN_WARNING "%s: Can't set default block size to %d bytes and density %x.\n", name, STm->default_blksize, STm->default_density); if (modes_defined) return (-EINVAL); } return 0;}/* Lock or unlock the drive door. Don't use when st_request allocated. */static int do_door_lock(struct scsi_tape * STp, int do_lock){ int retval, cmd; DEB(char *name = tape_name(STp);) cmd = do_lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK; DEBC(printk(ST_DEB_MSG "%s: %socking drive door.\n", name, do_lock ? "L" : "Unl")); retval = scsi_ioctl(STp->device, cmd, NULL); if (!retval) { STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED; } else { STp->door_locked = ST_LOCK_FAILS; } return retval;}/* Set the internal state after reset */static void reset_state(struct scsi_tape *STp){ int i; struct st_partstat *STps; STp->pos_unknown = 0; for (i = 0; i < ST_NBR_PARTITIONS; i++) { STps = &(STp->ps[i]); STps->rw = ST_IDLE; STps->eof = ST_NOEOF; STps->at_sm = 0; STps->last_block_valid = 0; STps->drv_block = -1; STps->drv_file = -1; } if (STp->can_partitions) { STp->partition = find_partition(STp); if (STp->partition < 0) STp->partition = 0; STp->new_partition = STp->partition; }}/* Test if the drive is ready. Returns either one of the codes below or a negative system error code. */#define CHKRES_READY 0#define CHKRES_NEW_SESSION 1#define CHKRES_NOT_READY 2#define CHKRES_NO_TAPE 3#define MAX_ATTENTIONS 10static int test_ready(struct scsi_tape *STp, int do_wait){ int attentions, waits, max_wait, scode; int retval = CHKRES_READY, new_session = 0; unsigned char cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt = NULL; struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat; max_wait = do_wait ? ST_BLOCK_SECONDS : 0; for (attentions=waits=0; ; ) { memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = TEST_UNIT_READY; SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, STp->long_timeout, MAX_READY_RETRIES, 1); if (!SRpnt) { retval = (STp->buffer)->syscall_result; break; } if (cmdstatp->have_sense) { scode = cmdstatp->sense_hdr.sense_key; if (scode == UNIT_ATTENTION) { /* New media? */ new_session = 1; if (attentions < MAX_ATTENTIONS) { attentions++; continue; } else { retval = (-EIO); break; } } if (scode == NOT_READY) { if (waits < max_wait) { if (msleep_interruptible(1000)) { retval = (-EINTR); break; } waits++; continue; } else { if ((STp->device)->scsi_level >= SCSI_2 && cmdstatp->sense_hdr.asc == 0x3a) /* Check ASC */ retval = CHKRES_NO_TAPE; else retval = CHKRES_NOT_READY; break; } } } retval = (STp->buffer)->syscall_result; if (!retval) retval = new_session ? CHKRES_NEW_SESSION : CHKRES_READY; break; } if (SRpnt != NULL) st_release_request(SRpnt); return retval;}/* See if the drive is ready and gather information about the tape. Return values: < 0 negative error code from errno.h 0 drive ready 1 drive not ready (possibly no tape)*/static int check_tape(struct scsi_tape *STp, struct file *filp){ int i, retval, new_session = 0, do_wait; unsigned char cmd[MAX_COMMAND_SIZE], saved_cleaning; unsigned short st_flags = filp->f_flags; struct st_request *SRpnt = NULL; struct st_modedef *STm; struct st_partstat *STps; char *name = tape_name(STp); struct inode *inode = filp->f_path.dentry->d_inode; int mode = TAPE_MODE(inode); STp->ready = ST_READY; if (mode != STp->current_mode) { DEBC(printk(ST_DEB_MSG "%s: Mode change from %d to %d.\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -