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:
Jan Höppner
2025-10-16 09:47:18 +02:00
committed by Heiko Carstens
parent 574817d6c0
commit 319d3d6653
5 changed files with 86 additions and 34 deletions

View File

@@ -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)
{

View File

@@ -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) {
/*

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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.