mirror of https://github.com/torvalds/linux.git
s390/tape: Introduce idal buffer array
The tape device driver uses a single idal_buffer for I/O. While the buffer itself can be arbitrary big, the limit for data transfer for a single Channel-Command Word is at 65535 bytes (64K-1) since the count field specifying the amount of data designated by the CCW is a 16-bit unsigned value. Provide functionality that allocates an array of multiple IDAL buffer with the limitation mentioned above in mind. A call to idal_buffer_array_alloc() allocates an array with a certain amount of IDAL buffers which is determined based on the total size of @size. Each individual buffer is limited to a size of CCW_MAX_BYTE_COUNT (65535 bytes). Add helper functions that determine the size (# of elements) and the total data size covered by the array as well. Current users of the single IDAL buffer are adapted to use the new functions with one buffer to allocate. The single IDAL buffer is removed from the tape_char_data struct. 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:
parent
a5e2ca22c1
commit
574817d6c0
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include <asm/scsw.h>
|
||||
|
||||
#define CCW_MAX_BYTE_COUNT 65535
|
||||
|
||||
/**
|
||||
* struct ccw1 - channel command word
|
||||
* @cmd_code: command code
|
||||
|
|
|
|||
|
|
@ -180,6 +180,82 @@ static inline void idal_buffer_free(struct idal_buffer *ib)
|
|||
kfree(ib);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an array of IDAL buffers to cover a total data size of @size. The
|
||||
* resulting array is null-terminated.
|
||||
*
|
||||
* The amount of individual IDAL buffers is determined based on @size.
|
||||
* Each IDAL buffer can have a maximum size of @CCW_MAX_BYTE_COUNT.
|
||||
*/
|
||||
static inline struct idal_buffer **idal_buffer_array_alloc(size_t size, int page_order)
|
||||
{
|
||||
struct idal_buffer **ibs;
|
||||
size_t ib_size; /* Size of a single idal buffer */
|
||||
int count; /* Amount of individual idal buffers */
|
||||
int i;
|
||||
|
||||
count = (size + CCW_MAX_BYTE_COUNT - 1) / CCW_MAX_BYTE_COUNT;
|
||||
ibs = kmalloc_array(count + 1, sizeof(*ibs), GFP_KERNEL);
|
||||
for (i = 0; i < count; i++) {
|
||||
/* Determine size for the current idal buffer */
|
||||
ib_size = min(size, CCW_MAX_BYTE_COUNT);
|
||||
size -= ib_size;
|
||||
ibs[i] = idal_buffer_alloc(ib_size, page_order);
|
||||
if (IS_ERR(ibs[i])) {
|
||||
while (i--)
|
||||
idal_buffer_free(ibs[i]);
|
||||
kfree(ibs);
|
||||
ibs = NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
ibs[i] = NULL;
|
||||
return ibs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free array of IDAL buffers
|
||||
*/
|
||||
static inline void idal_buffer_array_free(struct idal_buffer ***ibs)
|
||||
{
|
||||
struct idal_buffer **p;
|
||||
|
||||
if (!ibs || !*ibs)
|
||||
return;
|
||||
for (p = *ibs; *p; p++)
|
||||
idal_buffer_free(*p);
|
||||
kfree(*ibs);
|
||||
*ibs = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine size of IDAL buffer array
|
||||
*/
|
||||
static inline int idal_buffer_array_size(struct idal_buffer **ibs)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
while (ibs && *ibs) {
|
||||
size++;
|
||||
ibs++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine total data size covered by IDAL buffer array
|
||||
*/
|
||||
static inline size_t idal_buffer_array_datasize(struct idal_buffer **ibs)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
while (ibs && *ibs) {
|
||||
size += (*ibs)->size;
|
||||
ibs++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if a idal list is really needed.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ struct tape_discipline {
|
|||
|
||||
/* Char Frontend Data */
|
||||
struct tape_char_data {
|
||||
struct idal_buffer *idal_buf; /* idal buffer for user char data */
|
||||
struct idal_buffer **ibs; /* idal buffer array for user char data */
|
||||
int block_size; /* of size block_size. */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
|
|||
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.idal_buf,
|
||||
if (idal_buffer_to_user(*device->char_data.ibs,
|
||||
data, rc) != 0)
|
||||
rc = -EFAULT;
|
||||
}
|
||||
|
|
@ -195,7 +195,7 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t
|
|||
written = 0;
|
||||
for (i = 0; i < nblocks; i++) {
|
||||
/* Copy data from user space to idal buffer. */
|
||||
if (idal_buffer_from_user(device->char_data.idal_buf,
|
||||
if (idal_buffer_from_user(*device->char_data.ibs,
|
||||
data, block_size)) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
|
|
@ -297,10 +297,8 @@ tapechar_release(struct inode *inode, struct file *filp)
|
|||
}
|
||||
}
|
||||
|
||||
if (device->char_data.idal_buf != NULL) {
|
||||
idal_buffer_free(device->char_data.idal_buf);
|
||||
device->char_data.idal_buf = NULL;
|
||||
}
|
||||
if (device->char_data.ibs)
|
||||
idal_buffer_array_free(&device->char_data.ibs);
|
||||
tape_release(device);
|
||||
filp->private_data = NULL;
|
||||
tape_put_device(device);
|
||||
|
|
|
|||
|
|
@ -729,10 +729,11 @@ tape_free_request (struct tape_request * request)
|
|||
int
|
||||
tape_check_idalbuffer(struct tape_device *device, size_t size)
|
||||
{
|
||||
struct idal_buffer *new;
|
||||
struct idal_buffer **new;
|
||||
size_t old_size = 0;
|
||||
|
||||
if (device->char_data.idal_buf != NULL &&
|
||||
device->char_data.idal_buf->size == size)
|
||||
old_size = idal_buffer_array_datasize(device->char_data.ibs);
|
||||
if (old_size == size)
|
||||
return 0;
|
||||
|
||||
if (size > MAX_BLOCKSIZE) {
|
||||
|
|
@ -742,14 +743,15 @@ tape_check_idalbuffer(struct tape_device *device, size_t size)
|
|||
}
|
||||
|
||||
/* The current idal buffer is not correct. Allocate a new one. */
|
||||
new = idal_buffer_alloc(size, 0);
|
||||
new = idal_buffer_array_alloc(size, 0);
|
||||
if (IS_ERR(new))
|
||||
return -ENOMEM;
|
||||
|
||||
if (device->char_data.idal_buf != NULL)
|
||||
idal_buffer_free(device->char_data.idal_buf);
|
||||
/* Free old idal buffer array */
|
||||
if (device->char_data.ibs)
|
||||
idal_buffer_array_free(&device->char_data.ibs);
|
||||
|
||||
device->char_data.idal_buf = new;
|
||||
device->char_data.ibs = new;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -639,7 +639,7 @@ tape_std_read_block(struct tape_device *device)
|
|||
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.idal_buf);
|
||||
*device->char_data.ibs);
|
||||
DBF_EVENT(6, "xrbl ccwg\n");
|
||||
return request;
|
||||
}
|
||||
|
|
@ -660,7 +660,7 @@ tape_std_write_block(struct tape_device *device)
|
|||
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.idal_buf);
|
||||
*device->char_data.ibs);
|
||||
DBF_EVENT(6, "xwbl ccwg\n");
|
||||
return request;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue