aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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