#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
+#include <linux/slab.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
#include <asm/system.h>
static int notyetprinted = 1;
printk(KERN_WARNING
- "%s:W: Warning %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
- name, result, suggestion(result), driver_byte(result) & DRIVER_MASK,
+ "%s:W: Warning %x (driver bt 0x%x, host bt 0x%x).\n",
+ name, result, driver_byte(result),
host_byte(result));
if (notyetprinted) {
notyetprinted = 0;
/* Wakeup from interrupt */
-static void osst_sleep_done(void *data, char *sense, int result, int resid)
+static void osst_end_async(struct request *req, int update)
{
- struct osst_request *SRpnt = data;
+ struct osst_request *SRpnt = req->end_io_data;
struct osst_tape *STp = SRpnt->stp;
+ struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
- memcpy(SRpnt->sense, sense, SCSI_SENSE_BUFFERSIZE);
- STp->buffer->cmdstat.midlevel_result = SRpnt->result = result;
+ STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors;
#if DEBUG
STp->write_pending = 0;
#endif
if (SRpnt->waiting)
complete(SRpnt->waiting);
+
+ if (SRpnt->bio) {
+ kfree(mdata->pages);
+ blk_rq_unmap_user(SRpnt->bio);
+ }
+
+ __blk_put_request(req->q, req);
}
/* osst_request memory management */
kfree(streq);
}
+static int osst_execute(struct osst_request *SRpnt, const unsigned char *cmd,
+ int cmd_len, int data_direction, void *buffer, unsigned bufflen,
+ int use_sg, int timeout, int retries)
+{
+ struct request *req;
+ struct page **pages = NULL;
+ struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
+
+ int err = 0;
+ int write = (data_direction == DMA_TO_DEVICE);
+
+ req = blk_get_request(SRpnt->stp->device->request_queue, write, GFP_KERNEL);
+ if (!req)
+ return DRIVER_ERROR << 24;
+
+ req->cmd_type = REQ_TYPE_BLOCK_PC;
+ req->cmd_flags |= REQ_QUIET;
+
+ SRpnt->bio = NULL;
+
+ if (use_sg) {
+ struct scatterlist *sg, *sgl = (struct scatterlist *)buffer;
+ int i;
+
+ pages = kzalloc(use_sg * sizeof(struct page *), GFP_KERNEL);
+ if (!pages)
+ goto free_req;
+
+ for_each_sg(sgl, sg, use_sg, i)
+ pages[i] = sg_page(sg);
+
+ mdata->null_mapped = 1;
+
+ mdata->page_order = get_order(sgl[0].length);
+ mdata->nr_entries =
+ DIV_ROUND_UP(bufflen, PAGE_SIZE << mdata->page_order);
+ mdata->offset = 0;
+
+ err = blk_rq_map_user(req->q, req, mdata, NULL, bufflen, GFP_KERNEL);
+ if (err) {
+ kfree(pages);
+ goto free_req;
+ }
+ SRpnt->bio = req->bio;
+ mdata->pages = pages;
+
+ } else if (bufflen) {
+ err = blk_rq_map_kern(req->q, req, buffer, bufflen, GFP_KERNEL);
+ if (err)
+ goto free_req;
+ }
+
+ req->cmd_len = cmd_len;
+ memset(req->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */
+ memcpy(req->cmd, cmd, req->cmd_len);
+ req->sense = SRpnt->sense;
+ req->sense_len = 0;
+ req->timeout = timeout;
+ req->retries = retries;
+ req->end_io_data = SRpnt;
+
+ blk_execute_rq_nowait(req->q, NULL, req, 1, osst_end_async);
+ return 0;
+free_req:
+ blk_put_request(req);
+ return DRIVER_ERROR << 24;
+}
+
/* Do the scsi command. Waits until command performed if do_wait is true.
Otherwise osst_write_behind_check() is used to check that the command
has finished. */
STp->buffer->cmdstat.have_sense = 0;
STp->buffer->syscall_result = 0;
- if (scsi_execute_async(STp->device, cmd, COMMAND_SIZE(cmd[0]), direction, bp, bytes,
- use_sg, timeout, retries, SRpnt, osst_sleep_done, GFP_KERNEL))
+ if (osst_execute(SRpnt, cmd, COMMAND_SIZE(cmd[0]), direction, bp, bytes,
+ use_sg, timeout, retries))
/* could not allocate the buffer or request was too large */
(STp->buffer)->syscall_result = (-EBUSY);
else if (do_wait) {
if (STp->raw) {
if (STp->buffer->syscall_result) {
for (i=0; i < STp->buffer->sg_segs; i++)
- memset(page_address(STp->buffer->sg[i].page),
+ memset(page_address(sg_page(&STp->buffer->sg[i])),
0, STp->buffer->sg[i].length);
strcpy(STp->buffer->b_data, "READ ERROR ON FRAME");
} else
if (aux->frame_type != OS_FRAME_TYPE_DATA &&
aux->frame_type != OS_FRAME_TYPE_EOD &&
aux->frame_type != OS_FRAME_TYPE_MARKER) {
- if (!quiet)
+ if (!quiet) {
#if DEBUG
printk(OSST_DEB_MSG "%s:D: Skipping frame, frame type %x\n", name, aux->frame_type);
#endif
+ }
goto err_out;
}
if (aux->frame_type == OS_FRAME_TYPE_EOD &&
goto err_out;
}
if (frame_seq_number != -1 && ntohl(aux->frame_seq_num) != frame_seq_number) {
- if (!quiet)
+ if (!quiet) {
#if DEBUG
printk(OSST_DEB_MSG "%s:D: Skipping frame, sequence number %u (expected %d)\n",
name, ntohl(aux->frame_seq_num), frame_seq_number);
#endif
+ }
goto err_out;
}
if (aux->frame_type == OS_FRAME_TYPE_MARKER) {
if (i == (-ENOSPC)) {
transfer = STp->buffer->writing; /* FIXME -- check this logic */
if (transfer <= do_count) {
- filp->f_pos += do_count - transfer;
+ *ppos += do_count - transfer;
count -= do_count - transfer;
if (STps->drv_block >= 0) {
STps->drv_block += (do_count - transfer) / STp->block_size;
goto out;
}
- filp->f_pos += do_count;
+ *ppos += do_count;
b_point += do_count;
count -= do_count;
if (STps->drv_block >= 0) {
if (STps->drv_block >= 0) {
STps->drv_block += blks;
}
- filp->f_pos += count;
+ *ppos += count;
count = 0;
}
}
STp->logical_blk_num += transfer / STp->block_size;
STps->drv_block += transfer / STp->block_size;
- filp->f_pos += transfer;
+ *ppos += transfer;
buf += transfer;
total += transfer;
}
/* Open the device */
-static int os_scsi_tape_open(struct inode * inode, struct file * filp)
+static int __os_scsi_tape_open(struct inode * inode, struct file * filp)
{
unsigned short flags;
int i, b_size, new_session = 0, retval = 0;
for (i = 0, b_size = 0;
(i < STp->buffer->sg_segs) && ((b_size + STp->buffer->sg[i].length) <= OS_DATA_SIZE);
b_size += STp->buffer->sg[i++].length);
- STp->buffer->aux = (os_aux_t *) (page_address(STp->buffer->sg[i].page) + OS_DATA_SIZE - b_size);
+ STp->buffer->aux = (os_aux_t *) (page_address(sg_page(&STp->buffer->sg[i])) + OS_DATA_SIZE - b_size);
#if DEBUG
printk(OSST_DEB_MSG "%s:D: b_data points to %p in segment 0 at %p\n", name,
STp->buffer->b_data, page_address(STp->buffer->sg[0].page));
return retval;
}
+/* BKL pushdown: spaghetti avoidance wrapper */
+static int os_scsi_tape_open(struct inode * inode, struct file * filp)
+{
+ int ret;
+
+ lock_kernel();
+ ret = __os_scsi_tape_open(inode, filp);
+ unlock_kernel();
+ return ret;
+}
+
+
/* Flush the tape buffer before close */
static int os_scsi_tape_flush(struct file * filp, fl_owner_t id)
/* The ioctl command */
-static int osst_ioctl(struct inode * inode,struct file * file,
+static long osst_ioctl(struct file * file,
unsigned int cmd_in, unsigned long arg)
{
int i, cmd_nr, cmd_type, blk, retval = 0;
char * name = tape_name(STp);
void __user * p = (void __user *)arg;
- if (mutex_lock_interruptible(&STp->lock))
+ lock_kernel();
+ if (mutex_lock_interruptible(&STp->lock)) {
+ unlock_kernel();
return -ERESTARTSYS;
+ }
#if DEBUG
if (debugging && !STp->in_use) {
mutex_unlock(&STp->lock);
- return scsi_ioctl(STp->device, cmd_in, p);
+ retval = scsi_ioctl(STp->device, cmd_in, p);
+ unlock_kernel();
+ return retval;
out:
if (SRpnt) osst_release_request(SRpnt);
mutex_unlock(&STp->lock);
+ unlock_kernel();
return retval;
}
/* Try to allocate the first segment up to OS_DATA_SIZE and the others
big enough to reach the goal (code assumes no segments in place) */
for (b_size = OS_DATA_SIZE, order = OSST_FIRST_ORDER; b_size >= PAGE_SIZE; order--, b_size /= 2) {
- STbuffer->sg[0].page = alloc_pages(priority, order);
+ struct page *page = alloc_pages(priority, order);
+
STbuffer->sg[0].offset = 0;
- if (STbuffer->sg[0].page != NULL) {
- STbuffer->sg[0].length = b_size;
- STbuffer->b_data = page_address(STbuffer->sg[0].page);
+ if (page != NULL) {
+ sg_set_page(&STbuffer->sg[0], page, b_size, 0);
+ STbuffer->b_data = page_address(page);
break;
}
}
- if (STbuffer->sg[0].page == NULL) {
+ if (sg_page(&STbuffer->sg[0]) == NULL) {
printk(KERN_NOTICE "osst :I: Can't allocate tape buffer main segment.\n");
return 0;
}
/* Got initial segment of 'bsize,order', continue with same size if possible, except for AUX */
for (segs=STbuffer->sg_segs=1, got=b_size;
segs < max_segs && got < OS_FRAME_SIZE; ) {
- STbuffer->sg[segs].page =
- alloc_pages(priority, (OS_FRAME_SIZE - got <= PAGE_SIZE) ? 0 : order);
+ struct page *page = alloc_pages(priority, (OS_FRAME_SIZE - got <= PAGE_SIZE) ? 0 : order);
STbuffer->sg[segs].offset = 0;
- if (STbuffer->sg[segs].page == NULL) {
- if (OS_FRAME_SIZE - got <= (max_segs - segs) * b_size / 2 && order) {
- b_size /= 2; /* Large enough for the rest of the buffers */
- order--;
- continue;
- }
+ if (page == NULL) {
printk(KERN_WARNING "osst :W: Failed to enlarge buffer to %d bytes.\n",
OS_FRAME_SIZE);
#if DEBUG
normalize_buffer(STbuffer);
return 0;
}
- STbuffer->sg[segs].length = (OS_FRAME_SIZE - got <= PAGE_SIZE / 2) ? (OS_FRAME_SIZE - got) : b_size;
+ sg_set_page(&STbuffer->sg[segs], page, (OS_FRAME_SIZE - got <= PAGE_SIZE / 2) ? (OS_FRAME_SIZE - got) : b_size, 0);
got += STbuffer->sg[segs].length;
STbuffer->buffer_size = got;
STbuffer->sg_segs = ++segs;
b_size < STbuffer->sg[i].length;
b_size *= 2, order++);
- __free_pages(STbuffer->sg[i].page, order);
+ __free_pages(sg_page(&STbuffer->sg[i]), order);
STbuffer->buffer_size -= STbuffer->sg[i].length;
}
#if DEBUG
for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
cnt = st_bp->sg[i].length - offset < do_count ?
st_bp->sg[i].length - offset : do_count;
- res = copy_from_user(page_address(st_bp->sg[i].page) + offset, ubp, cnt);
+ res = copy_from_user(page_address(sg_page(&st_bp->sg[i])) + offset, ubp, cnt);
if (res)
return (-EFAULT);
do_count -= cnt;
for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
cnt = st_bp->sg[i].length - offset < do_count ?
st_bp->sg[i].length - offset : do_count;
- res = copy_to_user(ubp, page_address(st_bp->sg[i].page) + offset, cnt);
+ res = copy_to_user(ubp, page_address(sg_page(&st_bp->sg[i])) + offset, cnt);
if (res)
return (-EFAULT);
do_count -= cnt;
i < st_bp->sg_segs && do_count > 0; i++) {
cnt = st_bp->sg[i].length - offset < do_count ?
st_bp->sg[i].length - offset : do_count ;
- memset(page_address(st_bp->sg[i].page) + offset, 0, cnt);
+ memset(page_address(sg_page(&st_bp->sg[i])) + offset, 0, cnt);
do_count -= cnt;
offset = 0;
}
for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
cnt = st_bp->sg[i].length < do_count ?
st_bp->sg[i].length : do_count ;
- memcpy(page_address(st_bp->sg[i].page), ptr, cnt);
+ memcpy(page_address(sg_page(&st_bp->sg[i])), ptr, cnt);
do_count -= cnt;
ptr += cnt;
}
for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
cnt = st_bp->sg[i].length < do_count ?
st_bp->sg[i].length : do_count ;
- memcpy(ptr, page_address(st_bp->sg[i].page), cnt);
+ memcpy(ptr, page_address(sg_page(&st_bp->sg[i])), cnt);
do_count -= cnt;
ptr += cnt;
}
.owner = THIS_MODULE,
.read = osst_read,
.write = osst_write,
- .ioctl = osst_ioctl,
+ .unlocked_ioctl = osst_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = osst_compat_ioctl,
#endif
.open = os_scsi_tape_open,
.flush = os_scsi_tape_flush,
.release = os_scsi_tape_close,
+ .llseek = noop_llseek,
};
static int osst_supports(struct scsi_device * SDp)
* sysfs support for accessing ADR header information
*/
-static ssize_t osst_adr_rev_show(struct class_device *class_dev, char *buf)
+static ssize_t osst_adr_rev_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
ssize_t l = 0;
if (STp && STp->header_ok && STp->linux_media)
return l;
}
-CLASS_DEVICE_ATTR(ADR_rev, S_IRUGO, osst_adr_rev_show, NULL);
+DEVICE_ATTR(ADR_rev, S_IRUGO, osst_adr_rev_show, NULL);
-static ssize_t osst_linux_media_version_show(struct class_device *class_dev, char *buf)
+static ssize_t osst_linux_media_version_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
ssize_t l = 0;
if (STp && STp->header_ok && STp->linux_media)
return l;
}
-CLASS_DEVICE_ATTR(media_version, S_IRUGO, osst_linux_media_version_show, NULL);
+DEVICE_ATTR(media_version, S_IRUGO, osst_linux_media_version_show, NULL);
-static ssize_t osst_capacity_show(struct class_device *class_dev, char *buf)
+static ssize_t osst_capacity_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
ssize_t l = 0;
if (STp && STp->header_ok && STp->linux_media)
return l;
}
-CLASS_DEVICE_ATTR(capacity, S_IRUGO, osst_capacity_show, NULL);
+DEVICE_ATTR(capacity, S_IRUGO, osst_capacity_show, NULL);
-static ssize_t osst_first_data_ppos_show(struct class_device *class_dev, char *buf)
+static ssize_t osst_first_data_ppos_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
ssize_t l = 0;
if (STp && STp->header_ok && STp->linux_media)
return l;
}
-CLASS_DEVICE_ATTR(BOT_frame, S_IRUGO, osst_first_data_ppos_show, NULL);
+DEVICE_ATTR(BOT_frame, S_IRUGO, osst_first_data_ppos_show, NULL);
-static ssize_t osst_eod_frame_ppos_show(struct class_device *class_dev, char *buf)
+static ssize_t osst_eod_frame_ppos_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
ssize_t l = 0;
if (STp && STp->header_ok && STp->linux_media)
return l;
}
-CLASS_DEVICE_ATTR(EOD_frame, S_IRUGO, osst_eod_frame_ppos_show, NULL);
+DEVICE_ATTR(EOD_frame, S_IRUGO, osst_eod_frame_ppos_show, NULL);
-static ssize_t osst_filemark_cnt_show(struct class_device *class_dev, char *buf)
+static ssize_t osst_filemark_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
ssize_t l = 0;
if (STp && STp->header_ok && STp->linux_media)
return l;
}
-CLASS_DEVICE_ATTR(file_count, S_IRUGO, osst_filemark_cnt_show, NULL);
+DEVICE_ATTR(file_count, S_IRUGO, osst_filemark_cnt_show, NULL);
static struct class *osst_sysfs_class;
static void osst_sysfs_destroy(dev_t dev)
{
- class_device_destroy(osst_sysfs_class, dev);
+ device_destroy(osst_sysfs_class, dev);
}
static int osst_sysfs_add(dev_t dev, struct device *device, struct osst_tape * STp, char * name)
{
- struct class_device *osst_class_member;
+ struct device *osst_member;
int err;
- osst_class_member = class_device_create(osst_sysfs_class, NULL, dev,
- device, "%s", name);
- if (IS_ERR(osst_class_member)) {
+ osst_member = device_create(osst_sysfs_class, device, dev, STp,
+ "%s", name);
+ if (IS_ERR(osst_member)) {
printk(KERN_WARNING "osst :W: Unable to add sysfs class member %s\n", name);
- return PTR_ERR(osst_class_member);
+ return PTR_ERR(osst_member);
}
- class_set_devdata(osst_class_member, STp);
- err = class_device_create_file(osst_class_member,
- &class_device_attr_ADR_rev);
+ err = device_create_file(osst_member, &dev_attr_ADR_rev);
if (err)
goto err_out;
- err = class_device_create_file(osst_class_member,
- &class_device_attr_media_version);
+ err = device_create_file(osst_member, &dev_attr_media_version);
if (err)
goto err_out;
- err = class_device_create_file(osst_class_member,
- &class_device_attr_capacity);
+ err = device_create_file(osst_member, &dev_attr_capacity);
if (err)
goto err_out;
- err = class_device_create_file(osst_class_member,
- &class_device_attr_BOT_frame);
+ err = device_create_file(osst_member, &dev_attr_BOT_frame);
if (err)
goto err_out;
- err = class_device_create_file(osst_class_member,
- &class_device_attr_EOD_frame);
+ err = device_create_file(osst_member, &dev_attr_EOD_frame);
if (err)
goto err_out;
- err = class_device_create_file(osst_class_member,
- &class_device_attr_file_count);
+ err = device_create_file(osst_member, &dev_attr_file_count);
if (err)
goto err_out;