#include <asm/scsw.h>
+#define CCW_MAX_BYTE_COUNT 65535
+
/**
* struct ccw1 - channel command word
* @cmd_code: command code
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.
*/
/* 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. */
};
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;
}
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;
}
}
- 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);
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) {
}
/* 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;
}
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;
}
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;
}