mirror of
https://github.com/torvalds/linux.git
synced 2026-01-25 07:47:50 +00:00
s390/tape: Add support for bigger block sizes
The tape device type 3590/3592 and emulated 3490 VTS can handle a block size of up to 256K bytes. Currently the tape device driver is limited to a block size of 65535 bytes (64K-1). This limitation stems from the maximum of 65535 bytes of data that can be transferred with one Channel-Command Word (CCW). To work around this limitation data chaining is used which uses several CCW to transfer an entire 256K block of data. A single CCW holds a maximum of 65535 bytes of data. Set MAX_BLOCKSIZE to 262144 (= 256K) to allow for data transfers with larger block sizes. The read_block() and write_block() discipline functions calculate the number of CCWs required based on the IDAL buffer array size that was created for a given block size. If there is more than one CCW required for the data transfer, the new helper function tape_ccw_dc_idal() is used to build the data chain accordingly. The Interruption-Repsonse Block (irb) is added to the tape_request struct so that the tapechar_read/write() functions can analyze what data was read or written accordingly. Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com> Reviewed-by: Jens Remus <jremus@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
This commit is contained in:
committed by
Heiko Carstens
parent
574817d6c0
commit
319d3d6653
@@ -130,6 +130,7 @@ struct tape_request {
|
||||
int retries; /* retry counter for error recovery. */
|
||||
int rescnt; /* residual count from devstat. */
|
||||
struct timer_list timer; /* timer for std_assign_timeout(). */
|
||||
struct irb irb; /* device status */
|
||||
|
||||
/* Callback for delivering final status. */
|
||||
void (*callback)(struct tape_request *, void *);
|
||||
@@ -347,6 +348,15 @@ tape_ccw_repeat(struct ccw1 *ccw, __u8 cmd_code, int count)
|
||||
return ccw;
|
||||
}
|
||||
|
||||
static inline struct ccw1 *
|
||||
tape_ccw_dc_idal(struct ccw1 *ccw, __u8 cmd_code, struct idal_buffer *idal)
|
||||
{
|
||||
ccw->cmd_code = cmd_code;
|
||||
ccw->flags = CCW_FLAG_DC;
|
||||
idal_buffer_set_cda(idal, ccw);
|
||||
return ccw + 1;
|
||||
}
|
||||
|
||||
static inline struct ccw1 *
|
||||
tape_ccw_cc_idal(struct ccw1 *ccw, __u8 cmd_code, struct idal_buffer *idal)
|
||||
{
|
||||
|
||||
@@ -100,9 +100,12 @@ tapechar_cleanup_device(struct tape_device *device)
|
||||
static ssize_t
|
||||
tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct tape_device *device;
|
||||
struct tape_request *request;
|
||||
struct ccw1 *ccw, *last_ccw;
|
||||
struct tape_device *device;
|
||||
struct idal_buffer **ibs;
|
||||
size_t block_size;
|
||||
size_t read = 0;
|
||||
int rc;
|
||||
|
||||
DBF_EVENT(6, "TCHAR:read\n");
|
||||
@@ -141,12 +144,25 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
|
||||
/* Execute it. */
|
||||
rc = tape_do_io(device, request);
|
||||
if (rc == 0) {
|
||||
rc = block_size - request->rescnt;
|
||||
DBF_EVENT(6, "TCHAR:rbytes: %x\n", rc);
|
||||
/* Copy data from idal buffer to user space. */
|
||||
if (idal_buffer_to_user(*device->char_data.ibs,
|
||||
data, rc) != 0)
|
||||
rc = -EFAULT;
|
||||
/* Channel Program Address (cpa) points to last CCW + 8 */
|
||||
last_ccw = dma32_to_virt(request->irb.scsw.cmd.cpa);
|
||||
ccw = request->cpaddr;
|
||||
ibs = device->char_data.ibs;
|
||||
while (++ccw < last_ccw) {
|
||||
/* Copy data from idal buffer to user space. */
|
||||
if (idal_buffer_to_user(*ibs++, data, ccw->count) != 0) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
read += ccw->count;
|
||||
data += ccw->count;
|
||||
}
|
||||
if (&last_ccw[-1] == &request->cpaddr[1] &&
|
||||
request->rescnt == last_ccw[-1].count)
|
||||
rc = 0;
|
||||
else
|
||||
rc = read - request->rescnt;
|
||||
}
|
||||
tape_free_request(request);
|
||||
return rc;
|
||||
@@ -158,10 +174,12 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
|
||||
static ssize_t
|
||||
tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct tape_device *device;
|
||||
struct tape_request *request;
|
||||
struct ccw1 *ccw, *last_ccw;
|
||||
struct tape_device *device;
|
||||
struct idal_buffer **ibs;
|
||||
size_t written = 0;
|
||||
size_t block_size;
|
||||
size_t written;
|
||||
int nblocks;
|
||||
int i, rc;
|
||||
|
||||
@@ -185,31 +203,41 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
DBF_EVENT(6,"TCHAR:nbytes: %lx\n", block_size);
|
||||
DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size);
|
||||
DBF_EVENT(6, "TCHAR:nblocks: %x\n", nblocks);
|
||||
/* Let the discipline build the ccw chain. */
|
||||
request = device->discipline->write_block(device);
|
||||
if (IS_ERR(request))
|
||||
return PTR_ERR(request);
|
||||
rc = 0;
|
||||
written = 0;
|
||||
|
||||
for (i = 0; i < nblocks; i++) {
|
||||
/* Copy data from user space to idal buffer. */
|
||||
if (idal_buffer_from_user(*device->char_data.ibs,
|
||||
data, block_size)) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
size_t wbytes = 0; /* Used to trace written data in dbf */
|
||||
|
||||
ibs = device->char_data.ibs;
|
||||
while (ibs && *ibs) {
|
||||
if (idal_buffer_from_user(*ibs, data, (*ibs)->size)) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
data += (*ibs)->size;
|
||||
ibs++;
|
||||
}
|
||||
rc = tape_do_io(device, request);
|
||||
if (rc)
|
||||
break;
|
||||
DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
|
||||
block_size - request->rescnt);
|
||||
written += block_size - request->rescnt;
|
||||
goto out;
|
||||
|
||||
/* Channel Program Address (cpa) points to last CCW + 8 */
|
||||
last_ccw = dma32_to_virt(request->irb.scsw.cmd.cpa);
|
||||
ccw = request->cpaddr;
|
||||
while (++ccw < last_ccw)
|
||||
wbytes += ccw->count;
|
||||
DBF_EVENT(6, "TCHAR:wbytes: %lx\n", wbytes - request->rescnt);
|
||||
written += wbytes - request->rescnt;
|
||||
if (request->rescnt != 0)
|
||||
break;
|
||||
data += block_size;
|
||||
}
|
||||
|
||||
out:
|
||||
tape_free_request(request);
|
||||
if (rc == -ENOSPC) {
|
||||
/*
|
||||
|
||||
@@ -1129,9 +1129,10 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
|
||||
}
|
||||
|
||||
/* May be an unsolicited irq */
|
||||
if(request != NULL)
|
||||
if (request != NULL) {
|
||||
request->rescnt = irb->scsw.cmd.count;
|
||||
else if ((irb->scsw.cmd.dstat == 0x85 || irb->scsw.cmd.dstat == 0x80) &&
|
||||
memcpy(&request->irb, irb, sizeof(*irb));
|
||||
} else if ((irb->scsw.cmd.dstat == 0x85 || irb->scsw.cmd.dstat == 0x80) &&
|
||||
!list_empty(&device->req_queue)) {
|
||||
/* Not Ready to Ready after long busy ? */
|
||||
struct tape_request *req;
|
||||
|
||||
@@ -630,16 +630,23 @@ struct tape_request *
|
||||
tape_std_read_block(struct tape_device *device)
|
||||
{
|
||||
struct tape_request *request;
|
||||
struct idal_buffer **ibs;
|
||||
struct ccw1 *ccw;
|
||||
size_t count;
|
||||
|
||||
request = tape_alloc_request(2, 0);
|
||||
ibs = device->char_data.ibs;
|
||||
count = idal_buffer_array_size(ibs);
|
||||
request = tape_alloc_request(count + 1 /* MODE_SET_DB */, 0);
|
||||
if (IS_ERR(request)) {
|
||||
DBF_EXCEPTION(6, "xrbl fail");
|
||||
return request;
|
||||
}
|
||||
request->op = TO_RFO;
|
||||
tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
|
||||
tape_ccw_end_idal(request->cpaddr + 1, READ_FORWARD,
|
||||
*device->char_data.ibs);
|
||||
ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
|
||||
while (count-- > 1)
|
||||
ccw = tape_ccw_dc_idal(ccw, READ_FORWARD, *ibs++);
|
||||
tape_ccw_end_idal(ccw, READ_FORWARD, *ibs);
|
||||
|
||||
DBF_EVENT(6, "xrbl ccwg\n");
|
||||
return request;
|
||||
}
|
||||
@@ -651,16 +658,23 @@ struct tape_request *
|
||||
tape_std_write_block(struct tape_device *device)
|
||||
{
|
||||
struct tape_request *request;
|
||||
struct idal_buffer **ibs;
|
||||
struct ccw1 *ccw;
|
||||
size_t count;
|
||||
|
||||
request = tape_alloc_request(2, 0);
|
||||
count = idal_buffer_array_size(device->char_data.ibs);
|
||||
request = tape_alloc_request(count + 1 /* MODE_SET_DB */, 0);
|
||||
if (IS_ERR(request)) {
|
||||
DBF_EXCEPTION(6, "xwbl fail\n");
|
||||
return request;
|
||||
}
|
||||
request->op = TO_WRI;
|
||||
tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
|
||||
tape_ccw_end_idal(request->cpaddr + 1, WRITE_CMD,
|
||||
*device->char_data.ibs);
|
||||
ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
|
||||
ibs = device->char_data.ibs;
|
||||
while (count-- > 1)
|
||||
ccw = tape_ccw_dc_idal(ccw, WRITE_CMD, *ibs++);
|
||||
tape_ccw_end_idal(ccw, WRITE_CMD, *ibs);
|
||||
|
||||
DBF_EVENT(6, "xwbl ccwg\n");
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
#include <asm/tape390.h>
|
||||
|
||||
/*
|
||||
* Biggest block size to handle. Currently 64K because we only build
|
||||
* channel programs without data chaining.
|
||||
* Biggest block size of 256K to handle.
|
||||
*/
|
||||
#define MAX_BLOCKSIZE 65535
|
||||
#define MAX_BLOCKSIZE 262144
|
||||
|
||||
/*
|
||||
* The CCW commands for the Tape type of command.
|
||||
|
||||
Reference in New Issue
Block a user