aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/char/vmur.c
diff options
context:
space:
mode:
authorMichael Holzheu <holzheu@de.ibm.com>2007-08-10 08:32:30 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2007-08-10 08:32:37 -0400
commit1eade380c5f3e69348531ade5e9f9c5ae6485874 (patch)
tree485445bd0f7ed912cd0dee346c776ab0387795c2 /drivers/s390/char/vmur.c
parent6d740a438fcb8775008dfd3fc18df7f7a0ca2e12 (diff)
[S390] vmur: allocate single record buffers instead of one big data buffer
vmur allocates one contiguous kernel buffer to copy user data when creating ccw programs for punch or printer. If big block sizes are used, under memory pressure it can happen, that we do not get memory in one chunk. Now we allocate memory for each single record to avoid high order allocations. Signed-off-by: Michael Holzheu <holzheu@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/char/vmur.c')
-rw-r--r--drivers/s390/char/vmur.c75
1 files changed, 37 insertions, 38 deletions
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 161867cebd8c..1b758b51d7ed 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -119,10 +119,12 @@ static void urdev_put(struct urdev *urd)
119/* 119/*
120 * Low-level functions to do I/O to a ur device. 120 * Low-level functions to do I/O to a ur device.
121 * alloc_chan_prog 121 * alloc_chan_prog
122 * free_chan_prog
122 * do_ur_io 123 * do_ur_io
123 * ur_int_handler 124 * ur_int_handler
124 * 125 *
125 * alloc_chan_prog allocates and builds the channel program 126 * alloc_chan_prog allocates and builds the channel program
127 * free_chan_prog frees memory of the channel program
126 * 128 *
127 * do_ur_io issues the channel program to the device and blocks waiting 129 * do_ur_io issues the channel program to the device and blocks waiting
128 * on a completion event it publishes at urd->io_done. The function 130 * on a completion event it publishes at urd->io_done. The function
@@ -137,6 +139,16 @@ static void urdev_put(struct urdev *urd)
137 * address pointer that alloc_chan_prog returned. 139 * address pointer that alloc_chan_prog returned.
138 */ 140 */
139 141
142static void free_chan_prog(struct ccw1 *cpa)
143{
144 struct ccw1 *ptr = cpa;
145
146 while (ptr->cda) {
147 kfree((void *)(addr_t) ptr->cda);
148 ptr++;
149 }
150 kfree(cpa);
151}
140 152
141/* 153/*
142 * alloc_chan_prog 154 * alloc_chan_prog
@@ -144,44 +156,45 @@ static void urdev_put(struct urdev *urd)
144 * with a final NOP CCW command-chained on (which ensures that CE and DE 156 * with a final NOP CCW command-chained on (which ensures that CE and DE
145 * are presented together in a single interrupt instead of as separate 157 * are presented together in a single interrupt instead of as separate
146 * interrupts unless an incorrect length indication kicks in first). The 158 * interrupts unless an incorrect length indication kicks in first). The
147 * data length in each CCW is reclen. The caller must ensure that count 159 * data length in each CCW is reclen.
148 * is an integral multiple of reclen.
149 * The channel program pointer returned by this function must be freed
150 * with kfree. The caller is responsible for checking that
151 * count/reclen is not ridiculously large.
152 */ 160 */
153static struct ccw1 *alloc_chan_prog(char *buf, size_t count, size_t reclen) 161static struct ccw1 *alloc_chan_prog(const char __user *ubuf, int rec_count,
162 int reclen)
154{ 163{
155 size_t num_ccws;
156 struct ccw1 *cpa; 164 struct ccw1 *cpa;
165 void *kbuf;
157 int i; 166 int i;
158 167
159 TRACE("alloc_chan_prog(%p, %zu, %zu)\n", buf, count, reclen); 168 TRACE("alloc_chan_prog(%p, %i, %i)\n", ubuf, rec_count, reclen);
160 169
161 /* 170 /*
162 * We chain a NOP onto the writes to force CE+DE together. 171 * We chain a NOP onto the writes to force CE+DE together.
163 * That means we allocate room for CCWs to cover count/reclen 172 * That means we allocate room for CCWs to cover count/reclen
164 * records plus a NOP. 173 * records plus a NOP.
165 */ 174 */
166 num_ccws = count / reclen + 1; 175 cpa = kzalloc((rec_count + 1) * sizeof(struct ccw1),
167 cpa = kmalloc(num_ccws * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); 176 GFP_KERNEL | GFP_DMA);
168 if (!cpa) 177 if (!cpa)
169 return NULL; 178 return ERR_PTR(-ENOMEM);
170 179
171 for (i = 0; count; i++) { 180 for (i = 0; i < rec_count; i++) {
172 cpa[i].cmd_code = WRITE_CCW_CMD; 181 cpa[i].cmd_code = WRITE_CCW_CMD;
173 cpa[i].flags = CCW_FLAG_CC | CCW_FLAG_SLI; 182 cpa[i].flags = CCW_FLAG_CC | CCW_FLAG_SLI;
174 cpa[i].count = reclen; 183 cpa[i].count = reclen;
175 cpa[i].cda = __pa(buf); 184 kbuf = kmalloc(reclen, GFP_KERNEL | GFP_DMA);
176 buf += reclen; 185 if (!kbuf) {
177 count -= reclen; 186 free_chan_prog(cpa);
187 return ERR_PTR(-ENOMEM);
188 }
189 cpa[i].cda = (u32)(addr_t) kbuf;
190 if (copy_from_user(kbuf, ubuf, reclen)) {
191 free_chan_prog(cpa);
192 return ERR_PTR(-EFAULT);
193 }
194 ubuf += reclen;
178 } 195 }
179 /* The following NOP CCW forces CE+DE to be presented together */ 196 /* The following NOP CCW forces CE+DE to be presented together */
180 cpa[i].cmd_code = CCW_CMD_NOOP; 197 cpa[i].cmd_code = CCW_CMD_NOOP;
181 cpa[i].flags = 0;
182 cpa[i].count = 0;
183 cpa[i].cda = 0;
184
185 return cpa; 198 return cpa;
186} 199}
187 200
@@ -325,24 +338,11 @@ static ssize_t do_write(struct urdev *urd, const char __user *udata,
325 size_t count, size_t reclen, loff_t *ppos) 338 size_t count, size_t reclen, loff_t *ppos)
326{ 339{
327 struct ccw1 *cpa; 340 struct ccw1 *cpa;
328 char *buf;
329 int rc; 341 int rc;
330 342
331 /* Data buffer must be under 2GB line for fmt1 CCWs: hence GFP_DMA */ 343 cpa = alloc_chan_prog(udata, count / reclen, reclen);
332 buf = kmalloc(count, GFP_KERNEL | GFP_DMA); 344 if (IS_ERR(cpa))
333 if (!buf) 345 return PTR_ERR(cpa);
334 return -ENOMEM;
335
336 if (copy_from_user(buf, udata, count)) {
337 rc = -EFAULT;
338 goto fail_kfree_buf;
339 }
340
341 cpa = alloc_chan_prog(buf, count, reclen);
342 if (!cpa) {
343 rc = -ENOMEM;
344 goto fail_kfree_buf;
345 }
346 346
347 rc = do_ur_io(urd, cpa); 347 rc = do_ur_io(urd, cpa);
348 if (rc) 348 if (rc)
@@ -354,10 +354,9 @@ static ssize_t do_write(struct urdev *urd, const char __user *udata,
354 } 354 }
355 *ppos += count; 355 *ppos += count;
356 rc = count; 356 rc = count;
357
357fail_kfree_cpa: 358fail_kfree_cpa:
358 kfree(cpa); 359 free_chan_prog(cpa);
359fail_kfree_buf:
360 kfree(buf);
361 return rc; 360 return rc;
362} 361}
363 362