diff options
| -rw-r--r-- | arch/s390/include/asm/cio.h | 2 | ||||
| -rw-r--r-- | arch/s390/include/asm/idals.h | 76 | ||||
| -rw-r--r-- | drivers/s390/char/tape.h | 2 | ||||
| -rw-r--r-- | drivers/s390/char/tape_char.c | 10 | ||||
| -rw-r--r-- | drivers/s390/char/tape_core.c | 16 | ||||
| -rw-r--r-- | drivers/s390/char/tape_std.c | 4 |
6 files changed, 94 insertions, 16 deletions
diff --git a/arch/s390/include/asm/cio.h b/arch/s390/include/asm/cio.h index b6b619f340a5..0a82ae2300b6 100644 --- a/arch/s390/include/asm/cio.h +++ b/arch/s390/include/asm/cio.h @@ -18,6 +18,8 @@ #include <asm/scsw.h> +#define CCW_MAX_BYTE_COUNT 65535 + /** * struct ccw1 - channel command word * @cmd_code: command code diff --git a/arch/s390/include/asm/idals.h b/arch/s390/include/asm/idals.h index ac68c657b28c..e5000ee6cdc6 100644 --- a/arch/s390/include/asm/idals.h +++ b/arch/s390/include/asm/idals.h @@ -181,6 +181,82 @@ static inline void idal_buffer_free(struct idal_buffer *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. */ static inline bool __idal_buffer_is_needed(struct idal_buffer *ib) diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 349fa95dcedb..6e97b26d4e70 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -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. */ }; diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index e229585cfb9e..bcc49e9eabb8 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -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); diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 30ace31a3bba..304c6eae139c 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -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; } diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c index 29abbc23f908..5f3646f78bfa 100644 --- a/drivers/s390/char/tape_std.c +++ b/drivers/s390/char/tape_std.c @@ -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; } |
