diff options
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_diag.c | 66 |
1 files changed, 25 insertions, 41 deletions
diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c index 714293b78518..e2f9a51f4a38 100644 --- a/drivers/infiniband/hw/ipath/ipath_diag.c +++ b/drivers/infiniband/hw/ipath/ipath_diag.c | |||
@@ -326,7 +326,7 @@ static ssize_t ipath_diagpkt_write(struct file *fp, | |||
326 | size_t count, loff_t *off) | 326 | size_t count, loff_t *off) |
327 | { | 327 | { |
328 | u32 __iomem *piobuf; | 328 | u32 __iomem *piobuf; |
329 | u32 plen, clen, pbufn; | 329 | u32 plen, pbufn, maxlen_reserve; |
330 | struct ipath_diag_pkt odp; | 330 | struct ipath_diag_pkt odp; |
331 | struct ipath_diag_xpkt dp; | 331 | struct ipath_diag_xpkt dp; |
332 | u32 *tmpbuf = NULL; | 332 | u32 *tmpbuf = NULL; |
@@ -335,51 +335,29 @@ static ssize_t ipath_diagpkt_write(struct file *fp, | |||
335 | u64 val; | 335 | u64 val; |
336 | u32 l_state, lt_state; /* LinkState, LinkTrainingState */ | 336 | u32 l_state, lt_state; /* LinkState, LinkTrainingState */ |
337 | 337 | ||
338 | if (count < sizeof(odp)) { | ||
339 | ret = -EINVAL; | ||
340 | goto bail; | ||
341 | } | ||
342 | 338 | ||
343 | if (count == sizeof(dp)) { | 339 | if (count == sizeof(dp)) { |
344 | if (copy_from_user(&dp, data, sizeof(dp))) { | 340 | if (copy_from_user(&dp, data, sizeof(dp))) { |
345 | ret = -EFAULT; | 341 | ret = -EFAULT; |
346 | goto bail; | 342 | goto bail; |
347 | } | 343 | } |
348 | } else if (copy_from_user(&odp, data, sizeof(odp))) { | 344 | } else if (count == sizeof(odp)) { |
349 | ret = -EFAULT; | 345 | if (copy_from_user(&odp, data, sizeof(odp))) { |
346 | ret = -EFAULT; | ||
347 | goto bail; | ||
348 | } | ||
349 | } else { | ||
350 | ret = -EINVAL; | ||
350 | goto bail; | 351 | goto bail; |
351 | } | 352 | } |
352 | 353 | ||
353 | /* | ||
354 | * Due to padding/alignment issues (lessened with new struct) | ||
355 | * the old and new structs are the same length. We need to | ||
356 | * disambiguate them, which we can do because odp.len has never | ||
357 | * been less than the total of LRH+BTH+DETH so far, while | ||
358 | * dp.unit (same offset) unit is unlikely to get that high. | ||
359 | * Similarly, dp.data, the pointer to user at the same offset | ||
360 | * as odp.unit, is almost certainly at least one (512byte)page | ||
361 | * "above" NULL. The if-block below can be omitted if compatibility | ||
362 | * between a new driver and older diagnostic code is unimportant. | ||
363 | * compatibility the other direction (new diags, old driver) is | ||
364 | * handled in the diagnostic code, with a warning. | ||
365 | */ | ||
366 | if (dp.unit >= 20 && dp.data < 512) { | ||
367 | /* very probable version mismatch. Fix it up */ | ||
368 | memcpy(&odp, &dp, sizeof(odp)); | ||
369 | /* We got a legacy dp, copy elements to dp */ | ||
370 | dp.unit = odp.unit; | ||
371 | dp.data = odp.data; | ||
372 | dp.len = odp.len; | ||
373 | dp.pbc_wd = 0; /* Indicate we need to compute PBC wd */ | ||
374 | } | ||
375 | |||
376 | /* send count must be an exact number of dwords */ | 354 | /* send count must be an exact number of dwords */ |
377 | if (dp.len & 3) { | 355 | if (dp.len & 3) { |
378 | ret = -EINVAL; | 356 | ret = -EINVAL; |
379 | goto bail; | 357 | goto bail; |
380 | } | 358 | } |
381 | 359 | ||
382 | clen = dp.len >> 2; | 360 | plen = dp.len >> 2; |
383 | 361 | ||
384 | dd = ipath_lookup(dp.unit); | 362 | dd = ipath_lookup(dp.unit); |
385 | if (!dd || !(dd->ipath_flags & IPATH_PRESENT) || | 363 | if (!dd || !(dd->ipath_flags & IPATH_PRESENT) || |
@@ -422,16 +400,22 @@ static ssize_t ipath_diagpkt_write(struct file *fp, | |||
422 | goto bail; | 400 | goto bail; |
423 | } | 401 | } |
424 | 402 | ||
425 | /* need total length before first word written */ | 403 | /* |
426 | /* +1 word is for the qword padding */ | 404 | * need total length before first word written, plus 2 Dwords. One Dword |
427 | plen = sizeof(u32) + dp.len; | 405 | * is for padding so we get the full user data when not aligned on |
428 | 406 | * a word boundary. The other Dword is to make sure we have room for the | |
429 | if ((plen + 4) > dd->ipath_ibmaxlen) { | 407 | * ICRC which gets tacked on later. |
408 | */ | ||
409 | maxlen_reserve = 2 * sizeof(u32); | ||
410 | if (dp.len > dd->ipath_ibmaxlen - maxlen_reserve) { | ||
430 | ipath_dbg("Pkt len 0x%x > ibmaxlen %x\n", | 411 | ipath_dbg("Pkt len 0x%x > ibmaxlen %x\n", |
431 | plen - 4, dd->ipath_ibmaxlen); | 412 | dp.len, dd->ipath_ibmaxlen); |
432 | ret = -EINVAL; | 413 | ret = -EINVAL; |
433 | goto bail; /* before writing pbc */ | 414 | goto bail; |
434 | } | 415 | } |
416 | |||
417 | plen = sizeof(u32) + dp.len; | ||
418 | |||
435 | tmpbuf = vmalloc(plen); | 419 | tmpbuf = vmalloc(plen); |
436 | if (!tmpbuf) { | 420 | if (!tmpbuf) { |
437 | dev_info(&dd->pcidev->dev, "Unable to allocate tmp buffer, " | 421 | dev_info(&dd->pcidev->dev, "Unable to allocate tmp buffer, " |
@@ -473,11 +457,11 @@ static ssize_t ipath_diagpkt_write(struct file *fp, | |||
473 | */ | 457 | */ |
474 | if (dd->ipath_flags & IPATH_PIO_FLUSH_WC) { | 458 | if (dd->ipath_flags & IPATH_PIO_FLUSH_WC) { |
475 | ipath_flush_wc(); | 459 | ipath_flush_wc(); |
476 | __iowrite32_copy(piobuf + 2, tmpbuf, clen - 1); | 460 | __iowrite32_copy(piobuf + 2, tmpbuf, plen - 1); |
477 | ipath_flush_wc(); | 461 | ipath_flush_wc(); |
478 | __raw_writel(tmpbuf[clen - 1], piobuf + clen + 1); | 462 | __raw_writel(tmpbuf[plen - 1], piobuf + plen + 1); |
479 | } else | 463 | } else |
480 | __iowrite32_copy(piobuf + 2, tmpbuf, clen); | 464 | __iowrite32_copy(piobuf + 2, tmpbuf, plen); |
481 | 465 | ||
482 | ipath_flush_wc(); | 466 | ipath_flush_wc(); |
483 | 467 | ||