diff options
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_common.h | 19 | ||||
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_diag.c | 39 |
2 files changed, 52 insertions, 6 deletions
diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h index 12e1349cd03e..f70788c25ea6 100644 --- a/drivers/infiniband/hw/ipath/ipath_common.h +++ b/drivers/infiniband/hw/ipath/ipath_common.h | |||
@@ -501,13 +501,30 @@ struct __ipath_sendpkt { | |||
501 | struct ipath_iovec sps_iov[4]; | 501 | struct ipath_iovec sps_iov[4]; |
502 | }; | 502 | }; |
503 | 503 | ||
504 | /* Passed into diag data special file's ->write method. */ | 504 | /* |
505 | * diagnostics can send a packet by "writing" one of the following | ||
506 | * two structs to diag data special file | ||
507 | * The first is the legacy version for backward compatibility | ||
508 | */ | ||
505 | struct ipath_diag_pkt { | 509 | struct ipath_diag_pkt { |
506 | __u32 unit; | 510 | __u32 unit; |
507 | __u64 data; | 511 | __u64 data; |
508 | __u32 len; | 512 | __u32 len; |
509 | }; | 513 | }; |
510 | 514 | ||
515 | /* The second diag_pkt struct is the expanded version that allows | ||
516 | * more control over the packet, specifically, by allowing a custom | ||
517 | * pbc (+ extra) qword, so that special modes and deliberate | ||
518 | * changes to CRCs can be used. The elements were also re-ordered | ||
519 | * for better alignment and to avoid padding issues. | ||
520 | */ | ||
521 | struct ipath_diag_xpkt { | ||
522 | __u64 data; | ||
523 | __u64 pbc_wd; | ||
524 | __u32 unit; | ||
525 | __u32 len; | ||
526 | }; | ||
527 | |||
511 | /* | 528 | /* |
512 | * Data layout in I2C flash (for GUID, etc.) | 529 | * Data layout in I2C flash (for GUID, etc.) |
513 | * All fields are little-endian binary unless otherwise stated | 530 | * All fields are little-endian binary unless otherwise stated |
diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c index 63e8368b0e95..aab21c1b822d 100644 --- a/drivers/infiniband/hw/ipath/ipath_diag.c +++ b/drivers/infiniband/hw/ipath/ipath_diag.c | |||
@@ -323,13 +323,14 @@ static ssize_t ipath_diagpkt_write(struct file *fp, | |||
323 | { | 323 | { |
324 | u32 __iomem *piobuf; | 324 | u32 __iomem *piobuf; |
325 | u32 plen, clen, pbufn; | 325 | u32 plen, clen, pbufn; |
326 | struct ipath_diag_pkt dp; | 326 | struct ipath_diag_pkt odp; |
327 | struct ipath_diag_xpkt dp; | ||
327 | u32 *tmpbuf = NULL; | 328 | u32 *tmpbuf = NULL; |
328 | struct ipath_devdata *dd; | 329 | struct ipath_devdata *dd; |
329 | ssize_t ret = 0; | 330 | ssize_t ret = 0; |
330 | u64 val; | 331 | u64 val; |
331 | 332 | ||
332 | if (count < sizeof(dp)) { | 333 | if (count != sizeof(dp)) { |
333 | ret = -EINVAL; | 334 | ret = -EINVAL; |
334 | goto bail; | 335 | goto bail; |
335 | } | 336 | } |
@@ -339,6 +340,29 @@ static ssize_t ipath_diagpkt_write(struct file *fp, | |||
339 | goto bail; | 340 | goto bail; |
340 | } | 341 | } |
341 | 342 | ||
343 | /* | ||
344 | * Due to padding/alignment issues (lessened with new struct) | ||
345 | * the old and new structs are the same length. We need to | ||
346 | * disambiguate them, which we can do because odp.len has never | ||
347 | * been less than the total of LRH+BTH+DETH so far, while | ||
348 | * dp.unit (same offset) unit is unlikely to get that high. | ||
349 | * Similarly, dp.data, the pointer to user at the same offset | ||
350 | * as odp.unit, is almost certainly at least one (512byte)page | ||
351 | * "above" NULL. The if-block below can be omitted if compatibility | ||
352 | * between a new driver and older diagnostic code is unimportant. | ||
353 | * compatibility the other direction (new diags, old driver) is | ||
354 | * handled in the diagnostic code, with a warning. | ||
355 | */ | ||
356 | if (dp.unit >= 20 && dp.data < 512) { | ||
357 | /* very probable version mismatch. Fix it up */ | ||
358 | memcpy(&odp, &dp, sizeof(odp)); | ||
359 | /* We got a legacy dp, copy elements to dp */ | ||
360 | dp.unit = odp.unit; | ||
361 | dp.data = odp.data; | ||
362 | dp.len = odp.len; | ||
363 | dp.pbc_wd = 0; /* Indicate we need to compute PBC wd */ | ||
364 | } | ||
365 | |||
342 | /* send count must be an exact number of dwords */ | 366 | /* send count must be an exact number of dwords */ |
343 | if (dp.len & 3) { | 367 | if (dp.len & 3) { |
344 | ret = -EINVAL; | 368 | ret = -EINVAL; |
@@ -371,9 +395,10 @@ static ssize_t ipath_diagpkt_write(struct file *fp, | |||
371 | ret = -ENODEV; | 395 | ret = -ENODEV; |
372 | goto bail; | 396 | goto bail; |
373 | } | 397 | } |
398 | /* Check link state, but not if we have custom PBC */ | ||
374 | val = dd->ipath_lastibcstat & IPATH_IBSTATE_MASK; | 399 | val = dd->ipath_lastibcstat & IPATH_IBSTATE_MASK; |
375 | if (val != IPATH_IBSTATE_INIT && val != IPATH_IBSTATE_ARM && | 400 | if (!dp.pbc_wd && val != IPATH_IBSTATE_INIT && |
376 | val != IPATH_IBSTATE_ACTIVE) { | 401 | val != IPATH_IBSTATE_ARM && val != IPATH_IBSTATE_ACTIVE) { |
377 | ipath_cdbg(VERBOSE, "unit %u not ready (state %llx)\n", | 402 | ipath_cdbg(VERBOSE, "unit %u not ready (state %llx)\n", |
378 | dd->ipath_unit, (unsigned long long) val); | 403 | dd->ipath_unit, (unsigned long long) val); |
379 | ret = -EINVAL; | 404 | ret = -EINVAL; |
@@ -419,9 +444,13 @@ static ssize_t ipath_diagpkt_write(struct file *fp, | |||
419 | ipath_cdbg(VERBOSE, "unit %u 0x%x+1w pio%d\n", | 444 | ipath_cdbg(VERBOSE, "unit %u 0x%x+1w pio%d\n", |
420 | dd->ipath_unit, plen - 1, pbufn); | 445 | dd->ipath_unit, plen - 1, pbufn); |
421 | 446 | ||
447 | if (dp.pbc_wd == 0) | ||
448 | /* Legacy operation, use computed pbc_wd */ | ||
449 | dp.pbc_wd = plen; | ||
450 | |||
422 | /* we have to flush after the PBC for correctness on some cpus | 451 | /* we have to flush after the PBC for correctness on some cpus |
423 | * or WC buffer can be written out of order */ | 452 | * or WC buffer can be written out of order */ |
424 | writeq(plen, piobuf); | 453 | writeq(dp.pbc_wd, piobuf); |
425 | ipath_flush_wc(); | 454 | ipath_flush_wc(); |
426 | /* copy all by the trigger word, then flush, so it's written | 455 | /* copy all by the trigger word, then flush, so it's written |
427 | * to chip before trigger word, then write trigger word, then | 456 | * to chip before trigger word, then write trigger word, then |