Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky,
Michael Schaefer, J"org Weule, and Eric Youngdale.
- Copyright 1992 - 2006 Kai Makisara
+ Copyright 1992 - 2008 Kai Makisara
email Kai.Makisara@kolumbus.fi
Some small formal changes - aeb, 950809
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
*/
-static const char *verstr = "20061107";
+static const char *verstr = "20080224";
#include <linux/module.h>
MODULE_DESCRIPTION("SCSI tape (st) driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(SCSI_TAPE_MAJOR);
+MODULE_ALIAS_SCSI_DEVICE(TYPE_TAPE);
/* Set 'perm' (4th argument) to 0 to disable module_param's definition
* of sysfs parameters (which module_param doesn't yet support).
static struct st_buffer *new_tape_buffer(int, int, int);
static int enlarge_buffer(struct st_buffer *, int, int);
+static void clear_buffer(struct st_buffer *);
static void normalize_buffer(struct st_buffer *);
static int append_to_buffer(const char __user *, struct st_buffer *, int);
static int from_buffer(struct st_buffer *, char __user *, int);
static int st_probe(struct device *);
static int st_remove(struct device *);
-static int do_create_driverfs_files(void);
-static void do_remove_driverfs_files(void);
+static int do_create_sysfs_files(void);
+static void do_remove_sysfs_files(void);
static int do_create_class_files(struct scsi_tape *, int, int);
static struct scsi_driver st_template = {
memcpy(SRpnt->sense, sense, SCSI_SENSE_BUFFERSIZE);
(STp->buffer)->cmdstat.midlevel_result = SRpnt->result = result;
+ (STp->buffer)->cmdstat.residual = resid;
DEB( STp->write_pending = 0; )
if (SRpnt->waiting)
struct st_modedef *STm;
struct st_partstat *STps;
char *name = tape_name(STp);
- struct inode *inode = filp->f_dentry->d_inode;
+ struct inode *inode = filp->f_path.dentry->d_inode;
int mode = TAPE_MODE(inode);
STp->ready = ST_READY;
goto err_out;
}
+ (STp->buffer)->cleared = 0;
(STp->buffer)->writing = 0;
(STp->buffer)->syscall_result = 0;
STps = &(STp->ps[i]);
STps->rw = ST_IDLE;
}
+ STp->try_dio_now = STp->try_dio;
STp->recover_count = 0;
DEB( STp->nbr_waits = STp->nbr_finished = 0;
- STp->nbr_requests = STp->nbr_dio = STp->nbr_pages = STp->nbr_combinable = 0; )
+ STp->nbr_requests = STp->nbr_dio = STp->nbr_pages = 0; )
retval = check_tape(STp, filp);
if (retval < 0)
}
DEBC( if (STp->nbr_requests)
- printk(KERN_DEBUG "%s: Number of r/w requests %d, dio used in %d, pages %d (%d).\n",
- name, STp->nbr_requests, STp->nbr_dio, STp->nbr_pages, STp->nbr_combinable));
+ printk(KERN_DEBUG "%s: Number of r/w requests %d, dio used in %d, pages %d.\n",
+ name, STp->nbr_requests, STp->nbr_dio, STp->nbr_pages));
if (STps->rw == ST_WRITING && !STp->pos_unknown) {
struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
struct st_buffer *STbp = STp->buffer;
if (is_read)
- i = STp->try_dio && try_rdio;
+ i = STp->try_dio_now && try_rdio;
else
- i = STp->try_dio && try_wdio;
+ i = STp->try_dio_now && try_wdio;
if (i && ((unsigned long)buf & queue_dma_alignment(
STp->device->request_queue)) == 0) {
if (STbp->do_dio) {
STp->nbr_dio++;
STp->nbr_pages += STbp->do_dio;
- for (i=1; i < STbp->do_dio; i++)
- if (page_to_pfn(STbp->sg[i].page) == page_to_pfn(STbp->sg[i-1].page) + 1)
- STp->nbr_combinable++;
}
)
} else
if (STp->block_size)
bufsize = STp->block_size > st_fixed_buffer_size ?
STp->block_size : st_fixed_buffer_size;
- else
+ else {
bufsize = count;
+ /* Make sure that data from previous user is not leaked even if
+ HBA does not return correct residual */
+ if (is_read && STp->sili && !STbp->cleared)
+ clear_buffer(STbp);
+ }
+
if (bufsize > STbp->buffer_size &&
!enlarge_buffer(STbp, bufsize, STp->restr_dma)) {
printk(KERN_WARNING "%s: Can't allocate %d byte tape buffer.\n",
struct st_buffer *STbp;
char *name = tape_name(STp);
- if (down_interruptible(&STp->lock))
+ if (mutex_lock_interruptible(&STp->lock))
return -ERESTARTSYS;
retval = rw_checks(STp, filp, count);
STm->do_async_writes && STps->eof < ST_EOM_OK;
if (STp->block_size != 0 && STm->do_buffer_writes &&
- !(STp->try_dio && try_wdio) && STps->eof < ST_EOM_OK &&
+ !(STp->try_dio_now && try_wdio) && STps->eof < ST_EOM_OK &&
STbp->buffer_bytes < STbp->buffer_size) {
STp->dirty = 1;
/* Don't write a buffer that is not full enough. */
if (SRpnt != NULL)
st_release_request(SRpnt);
release_buffering(STp, 0);
- up(&STp->lock);
+ mutex_unlock(&STp->lock);
return retval;
}
if (STp->block_size == 0)
blks = bytes = count;
else {
- if (!(STp->try_dio && try_rdio) && STm->do_read_ahead) {
+ if (!(STp->try_dio_now && try_rdio) && STm->do_read_ahead) {
blks = (STp->buffer)->buffer_blocks;
bytes = blks * STp->block_size;
} else {
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = READ_6;
cmd[1] = (STp->block_size != 0);
+ if (!cmd[1] && STp->sili)
+ cmd[1] |= 2;
cmd[2] = blks >> 16;
cmd[3] = blks >> 8;
cmd[4] = blks;
}
/* End of error handling */
- else /* Read successful */
+ else { /* Read successful */
STbp->buffer_bytes = bytes;
+ if (STp->sili) /* In fixed block mode residual is always zero here */
+ STbp->buffer_bytes -= STp->buffer->cmdstat.residual;
+ }
if (STps->drv_block >= 0) {
if (STp->block_size == 0)
struct st_buffer *STbp = STp->buffer;
DEB( char *name = tape_name(STp); )
- if (down_interruptible(&STp->lock))
+ if (mutex_lock_interruptible(&STp->lock))
return -ERESTARTSYS;
retval = rw_checks(STp, filp, count);
goto out;
STm = &(STp->modes[STp->current_mode]);
- if (!(STm->do_read_ahead) && STp->block_size != 0 &&
- (count % STp->block_size) != 0) {
- retval = (-EINVAL); /* Read must be integral number of blocks */
- goto out;
+ if (STp->block_size != 0 && (count % STp->block_size) != 0) {
+ if (!STm->do_read_ahead) {
+ retval = (-EINVAL); /* Read must be integral number of blocks */
+ goto out;
+ }
+ STp->try_dio_now = 0; /* Direct i/o can't handle split blocks */
}
STps = &(STp->ps[STp->partition]);
release_buffering(STp, 1);
STbp->buffer_bytes = 0;
}
- up(&STp->lock);
+ mutex_unlock(&STp->lock);
return retval;
}
name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
STp->scsi2_logical);
printk(KERN_INFO
- "%s: sysv: %d nowait: %d\n", name, STm->sysv, STp->immediate);
+ "%s: sysv: %d nowait: %d sili: %d\n", name, STm->sysv, STp->immediate,
+ STp->sili);
printk(KERN_INFO "%s: debugging: %d\n",
name, debugging);
}
STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
STp->immediate = (options & MT_ST_NOWAIT) != 0;
STm->sysv = (options & MT_ST_SYSV) != 0;
+ STp->sili = (options & MT_ST_SILI) != 0;
DEB( debugging = (options & MT_ST_DEBUGGING) != 0;
st_log_options(STp, STm, name); )
} else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
STp->immediate = value;
if ((options & MT_ST_SYSV) != 0)
STm->sysv = value;
+ if ((options & MT_ST_SILI) != 0)
+ STp->sili = value;
DEB(
if ((options & MT_ST_DEBUGGING) != 0)
debugging = value;
if (cmd_in == MTWEOF &&
cmdstatp->have_sense &&
- (cmdstatp->flags & SENSE_EOM) &&
- (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
- cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
- undone == 0) {
- ioctl_result = 0; /* EOF written successfully at EOM */
- if (fileno >= 0)
- fileno++;
+ (cmdstatp->flags & SENSE_EOM)) {
+ if (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
+ cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) {
+ ioctl_result = 0; /* EOF(s) written successfully at EOM */
+ STps->eof = ST_NOEOF;
+ } else { /* Writing EOF(s) failed */
+ if (fileno >= 0)
+ fileno -= undone;
+ if (undone < arg)
+ STps->eof = ST_NOEOF;
+ }
STps->drv_file = fileno;
- STps->eof = ST_NOEOF;
} else if ((cmd_in == MTFSF) || (cmd_in == MTFSFM)) {
if (fileno >= 0)
STps->drv_file = fileno - undone;
/* The ioctl command */
-static int st_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd_in, unsigned long arg)
+static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
{
int i, cmd_nr, cmd_type, bt;
int retval = 0;
char *name = tape_name(STp);
void __user *p = (void __user *)arg;
- if (down_interruptible(&STp->lock))
+ if (mutex_lock_interruptible(&STp->lock))
return -ERESTARTSYS;
DEB(
retval = (-EFAULT);
goto out;
}
- up(&STp->lock);
+ mutex_unlock(&STp->lock);
switch (cmd_in) {
case SCSI_IOCTL_GET_IDLUN:
case SCSI_IOCTL_GET_BUS_NUMBER:
!capable(CAP_SYS_RAWIO))
i = -EPERM;
else
- i = scsi_cmd_ioctl(file, STp->disk, cmd_in, p);
+ i = scsi_cmd_ioctl(file, STp->disk->queue,
+ STp->disk, cmd_in, p);
if (i != -ENOTTY)
return i;
break;
return retval;
out:
- up(&STp->lock);
+ mutex_unlock(&STp->lock);
return retval;
}
tb->dma = need_dma;
tb->buffer_size = got;
+ sg_init_table(tb->sg, max_sg);
return tb;
}
STbuffer->frp_segs += 1;
got += b_size;
STbuffer->buffer_size = got;
+ if (STbuffer->cleared)
+ memset(page_address(STbuffer->frp[segs].page), 0, b_size);
segs++;
}
STbuffer->b_data = page_address(STbuffer->frp[0].page);
}
+/* Make sure that no data from previous user is in the internal buffer */
+static void clear_buffer(struct st_buffer * st_bp)
+{
+ int i;
+
+ for (i=0; i < st_bp->frp_segs; i++)
+ memset(page_address(st_bp->frp[i].page), 0, st_bp->frp[i].length);
+ st_bp->cleared = 1;
+}
+
+
/* Release the extra buffer */
static void normalize_buffer(struct st_buffer * STbuffer)
{
sg = &(STbp->sg[0]);
frp = STbp->frp;
for (i=count=0; count < length; i++) {
- sg[i].page = frp[i].page;
if (length - count > frp[i].length)
- sg[i].length = frp[i].length;
+ sg_set_page(&sg[i], frp[i].page, frp[i].length, 0);
else
- sg[i].length = length - count;
+ sg_set_page(&sg[i], frp[i].page, length - count, 0);
count += sg[i].length;
- sg[i].offset = 0;
}
STbp->sg_segs = i;
STbp->frp_sg_current = length;
#endif
-static struct file_operations st_fops =
+static const struct file_operations st_fops =
{
.owner = THIS_MODULE,
.read = st_read,
.write = st_write,
- .ioctl = st_ioctl,
+ .unlocked_ioctl = st_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = st_compat_ioctl,
#endif
tpnt->two_fm = ST_TWO_FM;
tpnt->fast_mteom = ST_FAST_MTEOM;
tpnt->scsi2_logical = ST_SCSI2LOGICAL;
+ tpnt->sili = ST_SILI;
tpnt->immediate = ST_NOWAIT;
tpnt->default_drvbuffer = 0xff; /* No forced buffering */
tpnt->partition = 0;
tpnt->density_changed = tpnt->compression_changed =
tpnt->blksize_changed = 0;
- init_MUTEX(&tpnt->lock);
+ mutex_init(&tpnt->lock);
st_nr_dev++;
write_unlock(&st_dev_arr_lock);
if (err)
goto err_chrdev;
- err = do_create_driverfs_files();
+ err = do_create_sysfs_files();
if (err)
goto err_scsidrv;
static void __exit exit_st(void)
{
- do_remove_driverfs_files();
+ do_remove_sysfs_files();
scsi_unregister_driver(&st_template.gendrv);
unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
ST_MAX_TAPE_ENTRIES);
}
static DRIVER_ATTR(version, S_IRUGO, st_version_show, NULL);
-static int do_create_driverfs_files(void)
+static int do_create_sysfs_files(void)
{
- struct device_driver *driverfs = &st_template.gendrv;
+ struct device_driver *sysfs = &st_template.gendrv;
int err;
- err = driver_create_file(driverfs, &driver_attr_try_direct_io);
+ err = driver_create_file(sysfs, &driver_attr_try_direct_io);
if (err)
return err;
- err = driver_create_file(driverfs, &driver_attr_fixed_buffer_size);
+ err = driver_create_file(sysfs, &driver_attr_fixed_buffer_size);
if (err)
goto err_try_direct_io;
- err = driver_create_file(driverfs, &driver_attr_max_sg_segs);
+ err = driver_create_file(sysfs, &driver_attr_max_sg_segs);
if (err)
goto err_attr_fixed_buf;
- err = driver_create_file(driverfs, &driver_attr_version);
+ err = driver_create_file(sysfs, &driver_attr_version);
if (err)
goto err_attr_max_sg;
return 0;
err_attr_max_sg:
- driver_remove_file(driverfs, &driver_attr_max_sg_segs);
+ driver_remove_file(sysfs, &driver_attr_max_sg_segs);
err_attr_fixed_buf:
- driver_remove_file(driverfs, &driver_attr_fixed_buffer_size);
+ driver_remove_file(sysfs, &driver_attr_fixed_buffer_size);
err_try_direct_io:
- driver_remove_file(driverfs, &driver_attr_try_direct_io);
+ driver_remove_file(sysfs, &driver_attr_try_direct_io);
return err;
}
-static void do_remove_driverfs_files(void)
+static void do_remove_sysfs_files(void)
{
- struct device_driver *driverfs = &st_template.gendrv;
+ struct device_driver *sysfs = &st_template.gendrv;
- driver_remove_file(driverfs, &driver_attr_version);
- driver_remove_file(driverfs, &driver_attr_max_sg_segs);
- driver_remove_file(driverfs, &driver_attr_fixed_buffer_size);
- driver_remove_file(driverfs, &driver_attr_try_direct_io);
+ driver_remove_file(sysfs, &driver_attr_version);
+ driver_remove_file(sysfs, &driver_attr_max_sg_segs);
+ driver_remove_file(sysfs, &driver_attr_fixed_buffer_size);
+ driver_remove_file(sysfs, &driver_attr_try_direct_io);
}
CLASS_DEVICE_ATTR(default_compression, S_IRUGO, st_defcompression_show, NULL);
+static ssize_t st_options_show(struct class_device *class_dev, char *buf)
+{
+ struct st_modedef *STm = (struct st_modedef *)class_get_devdata(class_dev);
+ struct scsi_tape *STp;
+ int i, j, options;
+ ssize_t l = 0;
+
+ for (i=0; i < st_dev_max; i++) {
+ for (j=0; j < ST_NBR_MODES; j++)
+ if (&scsi_tapes[i]->modes[j] == STm)
+ break;
+ if (j < ST_NBR_MODES)
+ break;
+ }
+ if (i == st_dev_max)
+ return 0; /* should never happen */
+
+ STp = scsi_tapes[i];
+
+ options = STm->do_buffer_writes ? MT_ST_BUFFER_WRITES : 0;
+ options |= STm->do_async_writes ? MT_ST_ASYNC_WRITES : 0;
+ options |= STm->do_read_ahead ? MT_ST_READ_AHEAD : 0;
+ DEB( options |= debugging ? MT_ST_DEBUGGING : 0 );
+ options |= STp->two_fm ? MT_ST_TWO_FM : 0;
+ options |= STp->fast_mteom ? MT_ST_FAST_MTEOM : 0;
+ options |= STm->defaults_for_writes ? MT_ST_DEF_WRITES : 0;
+ options |= STp->can_bsr ? MT_ST_CAN_BSR : 0;
+ options |= STp->omit_blklims ? MT_ST_NO_BLKLIMS : 0;
+ options |= STp->can_partitions ? MT_ST_CAN_PARTITIONS : 0;
+ options |= STp->scsi2_logical ? MT_ST_SCSI2LOGICAL : 0;
+ options |= STm->sysv ? MT_ST_SYSV : 0;
+ options |= STp->immediate ? MT_ST_NOWAIT : 0;
+ options |= STp->sili ? MT_ST_SILI : 0;
+
+ l = snprintf(buf, PAGE_SIZE, "0x%08x\n", options);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(options, S_IRUGO, st_options_show, NULL);
+
static int do_create_class_files(struct scsi_tape *STp, int dev_num, int mode)
{
int i, rew, error;
error = class_device_create_file(st_class_member,
&class_device_attr_default_compression);
if (error) goto out;
+ error = class_device_create_file(st_class_member,
+ &class_device_attr_options);
+ if (error) goto out;
if (mode == 0 && rew == 0) {
error = sysfs_create_link(&STp->device->sdev_gendev.kobj,
}
/* Populate the scatter/gather list */
- sgl[0].page = pages[0];
- sgl[0].offset = uaddr & ~PAGE_MASK;
+ sg_set_page(&sgl[0], pages[0], 0, uaddr & ~PAGE_MASK);
if (nr_pages > 1) {
sgl[0].length = PAGE_SIZE - sgl[0].offset;
count -= sgl[0].length;
for (i=1; i < nr_pages ; i++) {
- sgl[i].offset = 0;
- sgl[i].page = pages[i];
- sgl[i].length = count < PAGE_SIZE ? count : PAGE_SIZE;
+ sg_set_page(&sgl[i], pages[i],
+ count < PAGE_SIZE ? count : PAGE_SIZE, 0);;
count -= PAGE_SIZE;
}
}
int i;
for (i=0; i < nr_pages; i++) {
- struct page *page = sgl[i].page;
+ struct page *page = sg_page(&sgl[i]);
if (dirtied)
SetPageDirty(page);