diff options
author | Brian King <brking@us.ibm.com> | 2006-11-21 11:28:35 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-11-22 13:21:44 -0500 |
commit | 49dc6a18185c12bae4980d17512fbe54ca6bae54 (patch) | |
tree | dc3e9dc84647d485622c42b9715dceee00ecab19 /drivers/scsi | |
parent | 9d66bdf81f97673b6e330a26438fcaea38c26cd5 (diff) |
[SCSI] ipr: Add support for logging SAS fabric errors
Adds support for logging SAS fabric errors logged by
the ipr firmware.
Signed-off-by: Brian King <brking@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/ipr.c | 225 | ||||
-rw-r--r-- | drivers/scsi/ipr.h | 72 |
2 files changed, 290 insertions, 7 deletions
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e53cfbd0e076..94345e79ef1e 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c | |||
@@ -1321,6 +1321,219 @@ static void ipr_log_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg, | |||
1321 | offsetof(struct ipr_hostrcb_type_07_error, data))); | 1321 | offsetof(struct ipr_hostrcb_type_07_error, data))); |
1322 | } | 1322 | } |
1323 | 1323 | ||
1324 | static const struct { | ||
1325 | u8 active; | ||
1326 | char *desc; | ||
1327 | } path_active_desc[] = { | ||
1328 | { IPR_PATH_NO_INFO, "Path" }, | ||
1329 | { IPR_PATH_ACTIVE, "Active path" }, | ||
1330 | { IPR_PATH_NOT_ACTIVE, "Inactive path" } | ||
1331 | }; | ||
1332 | |||
1333 | static const struct { | ||
1334 | u8 state; | ||
1335 | char *desc; | ||
1336 | } path_state_desc[] = { | ||
1337 | { IPR_PATH_STATE_NO_INFO, "has no path state information available" }, | ||
1338 | { IPR_PATH_HEALTHY, "is healthy" }, | ||
1339 | { IPR_PATH_DEGRADED, "is degraded" }, | ||
1340 | { IPR_PATH_FAILED, "is failed" } | ||
1341 | }; | ||
1342 | |||
1343 | /** | ||
1344 | * ipr_log_fabric_path - Log a fabric path error | ||
1345 | * @hostrcb: hostrcb struct | ||
1346 | * @fabric: fabric descriptor | ||
1347 | * | ||
1348 | * Return value: | ||
1349 | * none | ||
1350 | **/ | ||
1351 | static void ipr_log_fabric_path(struct ipr_hostrcb *hostrcb, | ||
1352 | struct ipr_hostrcb_fabric_desc *fabric) | ||
1353 | { | ||
1354 | int i, j; | ||
1355 | u8 path_state = fabric->path_state; | ||
1356 | u8 active = path_state & IPR_PATH_ACTIVE_MASK; | ||
1357 | u8 state = path_state & IPR_PATH_STATE_MASK; | ||
1358 | |||
1359 | for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) { | ||
1360 | if (path_active_desc[i].active != active) | ||
1361 | continue; | ||
1362 | |||
1363 | for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) { | ||
1364 | if (path_state_desc[j].state != state) | ||
1365 | continue; | ||
1366 | |||
1367 | if (fabric->cascaded_expander == 0xff && fabric->phy == 0xff) { | ||
1368 | ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d\n", | ||
1369 | path_active_desc[i].desc, path_state_desc[j].desc, | ||
1370 | fabric->ioa_port); | ||
1371 | } else if (fabric->cascaded_expander == 0xff) { | ||
1372 | ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Phy=%d\n", | ||
1373 | path_active_desc[i].desc, path_state_desc[j].desc, | ||
1374 | fabric->ioa_port, fabric->phy); | ||
1375 | } else if (fabric->phy == 0xff) { | ||
1376 | ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d\n", | ||
1377 | path_active_desc[i].desc, path_state_desc[j].desc, | ||
1378 | fabric->ioa_port, fabric->cascaded_expander); | ||
1379 | } else { | ||
1380 | ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d, Phy=%d\n", | ||
1381 | path_active_desc[i].desc, path_state_desc[j].desc, | ||
1382 | fabric->ioa_port, fabric->cascaded_expander, fabric->phy); | ||
1383 | } | ||
1384 | return; | ||
1385 | } | ||
1386 | } | ||
1387 | |||
1388 | ipr_err("Path state=%02X IOA Port=%d Cascade=%d Phy=%d\n", path_state, | ||
1389 | fabric->ioa_port, fabric->cascaded_expander, fabric->phy); | ||
1390 | } | ||
1391 | |||
1392 | static const struct { | ||
1393 | u8 type; | ||
1394 | char *desc; | ||
1395 | } path_type_desc[] = { | ||
1396 | { IPR_PATH_CFG_IOA_PORT, "IOA port" }, | ||
1397 | { IPR_PATH_CFG_EXP_PORT, "Expander port" }, | ||
1398 | { IPR_PATH_CFG_DEVICE_PORT, "Device port" }, | ||
1399 | { IPR_PATH_CFG_DEVICE_LUN, "Device LUN" } | ||
1400 | }; | ||
1401 | |||
1402 | static const struct { | ||
1403 | u8 status; | ||
1404 | char *desc; | ||
1405 | } path_status_desc[] = { | ||
1406 | { IPR_PATH_CFG_NO_PROB, "Functional" }, | ||
1407 | { IPR_PATH_CFG_DEGRADED, "Degraded" }, | ||
1408 | { IPR_PATH_CFG_FAILED, "Failed" }, | ||
1409 | { IPR_PATH_CFG_SUSPECT, "Suspect" }, | ||
1410 | { IPR_PATH_NOT_DETECTED, "Missing" }, | ||
1411 | { IPR_PATH_INCORRECT_CONN, "Incorrectly connected" } | ||
1412 | }; | ||
1413 | |||
1414 | static const char *link_rate[] = { | ||
1415 | "unknown", | ||
1416 | "disabled", | ||
1417 | "phy reset problem", | ||
1418 | "spinup hold", | ||
1419 | "port selector", | ||
1420 | "unknown", | ||
1421 | "unknown", | ||
1422 | "unknown", | ||
1423 | "1.5Gbps", | ||
1424 | "3.0Gbps", | ||
1425 | "unknown", | ||
1426 | "unknown", | ||
1427 | "unknown", | ||
1428 | "unknown", | ||
1429 | "unknown", | ||
1430 | "unknown" | ||
1431 | }; | ||
1432 | |||
1433 | /** | ||
1434 | * ipr_log_path_elem - Log a fabric path element. | ||
1435 | * @hostrcb: hostrcb struct | ||
1436 | * @cfg: fabric path element struct | ||
1437 | * | ||
1438 | * Return value: | ||
1439 | * none | ||
1440 | **/ | ||
1441 | static void ipr_log_path_elem(struct ipr_hostrcb *hostrcb, | ||
1442 | struct ipr_hostrcb_config_element *cfg) | ||
1443 | { | ||
1444 | int i, j; | ||
1445 | u8 type = cfg->type_status & IPR_PATH_CFG_TYPE_MASK; | ||
1446 | u8 status = cfg->type_status & IPR_PATH_CFG_STATUS_MASK; | ||
1447 | |||
1448 | if (type == IPR_PATH_CFG_NOT_EXIST) | ||
1449 | return; | ||
1450 | |||
1451 | for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) { | ||
1452 | if (path_type_desc[i].type != type) | ||
1453 | continue; | ||
1454 | |||
1455 | for (j = 0; j < ARRAY_SIZE(path_status_desc); j++) { | ||
1456 | if (path_status_desc[j].status != status) | ||
1457 | continue; | ||
1458 | |||
1459 | if (type == IPR_PATH_CFG_IOA_PORT) { | ||
1460 | ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, WWN=%08X%08X\n", | ||
1461 | path_status_desc[j].desc, path_type_desc[i].desc, | ||
1462 | cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], | ||
1463 | be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); | ||
1464 | } else { | ||
1465 | if (cfg->cascaded_expander == 0xff && cfg->phy == 0xff) { | ||
1466 | ipr_hcam_err(hostrcb, "%s %s: Link rate=%s, WWN=%08X%08X\n", | ||
1467 | path_status_desc[j].desc, path_type_desc[i].desc, | ||
1468 | link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], | ||
1469 | be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); | ||
1470 | } else if (cfg->cascaded_expander == 0xff) { | ||
1471 | ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, " | ||
1472 | "WWN=%08X%08X\n", path_status_desc[j].desc, | ||
1473 | path_type_desc[i].desc, cfg->phy, | ||
1474 | link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], | ||
1475 | be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); | ||
1476 | } else if (cfg->phy == 0xff) { | ||
1477 | ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Link rate=%s, " | ||
1478 | "WWN=%08X%08X\n", path_status_desc[j].desc, | ||
1479 | path_type_desc[i].desc, cfg->cascaded_expander, | ||
1480 | link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], | ||
1481 | be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); | ||
1482 | } else { | ||
1483 | ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Phy=%d, Link rate=%s " | ||
1484 | "WWN=%08X%08X\n", path_status_desc[j].desc, | ||
1485 | path_type_desc[i].desc, cfg->cascaded_expander, cfg->phy, | ||
1486 | link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], | ||
1487 | be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); | ||
1488 | } | ||
1489 | } | ||
1490 | return; | ||
1491 | } | ||
1492 | } | ||
1493 | |||
1494 | ipr_hcam_err(hostrcb, "Path element=%02X: Cascade=%d Phy=%d Link rate=%s " | ||
1495 | "WWN=%08X%08X\n", cfg->type_status, cfg->cascaded_expander, cfg->phy, | ||
1496 | link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], | ||
1497 | be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); | ||
1498 | } | ||
1499 | |||
1500 | /** | ||
1501 | * ipr_log_fabric_error - Log a fabric error. | ||
1502 | * @ioa_cfg: ioa config struct | ||
1503 | * @hostrcb: hostrcb struct | ||
1504 | * | ||
1505 | * Return value: | ||
1506 | * none | ||
1507 | **/ | ||
1508 | static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg, | ||
1509 | struct ipr_hostrcb *hostrcb) | ||
1510 | { | ||
1511 | struct ipr_hostrcb_type_20_error *error; | ||
1512 | struct ipr_hostrcb_fabric_desc *fabric; | ||
1513 | struct ipr_hostrcb_config_element *cfg; | ||
1514 | int i, add_len; | ||
1515 | |||
1516 | error = &hostrcb->hcam.u.error.u.type_20_error; | ||
1517 | error->failure_reason[sizeof(error->failure_reason) - 1] = '\0'; | ||
1518 | ipr_hcam_err(hostrcb, "%s\n", error->failure_reason); | ||
1519 | |||
1520 | add_len = be32_to_cpu(hostrcb->hcam.length) - | ||
1521 | (offsetof(struct ipr_hostrcb_error, u) + | ||
1522 | offsetof(struct ipr_hostrcb_type_20_error, desc)); | ||
1523 | |||
1524 | for (i = 0, fabric = error->desc; i < error->num_entries; i++) { | ||
1525 | ipr_log_fabric_path(hostrcb, fabric); | ||
1526 | for_each_fabric_cfg(fabric, cfg) | ||
1527 | ipr_log_path_elem(hostrcb, cfg); | ||
1528 | |||
1529 | add_len -= be16_to_cpu(fabric->length); | ||
1530 | fabric = (struct ipr_hostrcb_fabric_desc *) | ||
1531 | ((unsigned long)fabric + be16_to_cpu(fabric->length)); | ||
1532 | } | ||
1533 | |||
1534 | ipr_log_hex_data((u32 *)fabric, add_len); | ||
1535 | } | ||
1536 | |||
1324 | /** | 1537 | /** |
1325 | * ipr_log_generic_error - Log an adapter error. | 1538 | * ipr_log_generic_error - Log an adapter error. |
1326 | * @ioa_cfg: ioa config struct | 1539 | * @ioa_cfg: ioa config struct |
@@ -1394,13 +1607,7 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg, | |||
1394 | if (!ipr_error_table[error_index].log_hcam) | 1607 | if (!ipr_error_table[error_index].log_hcam) |
1395 | return; | 1608 | return; |
1396 | 1609 | ||
1397 | if (ipr_is_device(&hostrcb->hcam.u.error.failing_dev_res_addr)) { | 1610 | ipr_hcam_err(hostrcb, "%s\n", ipr_error_table[error_index].error); |
1398 | ipr_ra_err(ioa_cfg, hostrcb->hcam.u.error.failing_dev_res_addr, | ||
1399 | "%s\n", ipr_error_table[error_index].error); | ||
1400 | } else { | ||
1401 | dev_err(&ioa_cfg->pdev->dev, "%s\n", | ||
1402 | ipr_error_table[error_index].error); | ||
1403 | } | ||
1404 | 1611 | ||
1405 | /* Set indication we have logged an error */ | 1612 | /* Set indication we have logged an error */ |
1406 | ioa_cfg->errors_logged++; | 1613 | ioa_cfg->errors_logged++; |
@@ -1437,6 +1644,9 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg, | |||
1437 | case IPR_HOST_RCB_OVERLAY_ID_17: | 1644 | case IPR_HOST_RCB_OVERLAY_ID_17: |
1438 | ipr_log_enhanced_dual_ioa_error(ioa_cfg, hostrcb); | 1645 | ipr_log_enhanced_dual_ioa_error(ioa_cfg, hostrcb); |
1439 | break; | 1646 | break; |
1647 | case IPR_HOST_RCB_OVERLAY_ID_20: | ||
1648 | ipr_log_fabric_error(ioa_cfg, hostrcb); | ||
1649 | break; | ||
1440 | case IPR_HOST_RCB_OVERLAY_ID_1: | 1650 | case IPR_HOST_RCB_OVERLAY_ID_1: |
1441 | case IPR_HOST_RCB_OVERLAY_ID_DEFAULT: | 1651 | case IPR_HOST_RCB_OVERLAY_ID_DEFAULT: |
1442 | default: | 1652 | default: |
@@ -6812,6 +7022,7 @@ static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) | |||
6812 | 7022 | ||
6813 | ioa_cfg->hostrcb[i]->hostrcb_dma = | 7023 | ioa_cfg->hostrcb[i]->hostrcb_dma = |
6814 | ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam); | 7024 | ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam); |
7025 | ioa_cfg->hostrcb[i]->ioa_cfg = ioa_cfg; | ||
6815 | list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q); | 7026 | list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q); |
6816 | } | 7027 | } |
6817 | 7028 | ||
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 75669251fb63..44b15e4b6edf 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h | |||
@@ -737,6 +737,64 @@ struct ipr_hostrcb_type_17_error { | |||
737 | u32 data[476]; | 737 | u32 data[476]; |
738 | }__attribute__((packed, aligned (4))); | 738 | }__attribute__((packed, aligned (4))); |
739 | 739 | ||
740 | struct ipr_hostrcb_config_element { | ||
741 | u8 type_status; | ||
742 | #define IPR_PATH_CFG_TYPE_MASK 0xF0 | ||
743 | #define IPR_PATH_CFG_NOT_EXIST 0x00 | ||
744 | #define IPR_PATH_CFG_IOA_PORT 0x10 | ||
745 | #define IPR_PATH_CFG_EXP_PORT 0x20 | ||
746 | #define IPR_PATH_CFG_DEVICE_PORT 0x30 | ||
747 | #define IPR_PATH_CFG_DEVICE_LUN 0x40 | ||
748 | |||
749 | #define IPR_PATH_CFG_STATUS_MASK 0x0F | ||
750 | #define IPR_PATH_CFG_NO_PROB 0x00 | ||
751 | #define IPR_PATH_CFG_DEGRADED 0x01 | ||
752 | #define IPR_PATH_CFG_FAILED 0x02 | ||
753 | #define IPR_PATH_CFG_SUSPECT 0x03 | ||
754 | #define IPR_PATH_NOT_DETECTED 0x04 | ||
755 | #define IPR_PATH_INCORRECT_CONN 0x05 | ||
756 | |||
757 | u8 cascaded_expander; | ||
758 | u8 phy; | ||
759 | u8 link_rate; | ||
760 | #define IPR_PHY_LINK_RATE_MASK 0x0F | ||
761 | |||
762 | __be32 wwid[2]; | ||
763 | }__attribute__((packed, aligned (4))); | ||
764 | |||
765 | struct ipr_hostrcb_fabric_desc { | ||
766 | __be16 length; | ||
767 | u8 ioa_port; | ||
768 | u8 cascaded_expander; | ||
769 | u8 phy; | ||
770 | u8 path_state; | ||
771 | #define IPR_PATH_ACTIVE_MASK 0xC0 | ||
772 | #define IPR_PATH_NO_INFO 0x00 | ||
773 | #define IPR_PATH_ACTIVE 0x40 | ||
774 | #define IPR_PATH_NOT_ACTIVE 0x80 | ||
775 | |||
776 | #define IPR_PATH_STATE_MASK 0x0F | ||
777 | #define IPR_PATH_STATE_NO_INFO 0x00 | ||
778 | #define IPR_PATH_HEALTHY 0x01 | ||
779 | #define IPR_PATH_DEGRADED 0x02 | ||
780 | #define IPR_PATH_FAILED 0x03 | ||
781 | |||
782 | __be16 num_entries; | ||
783 | struct ipr_hostrcb_config_element elem[1]; | ||
784 | }__attribute__((packed, aligned (4))); | ||
785 | |||
786 | #define for_each_fabric_cfg(fabric, cfg) \ | ||
787 | for (cfg = (fabric)->elem; \ | ||
788 | cfg < ((fabric)->elem + be16_to_cpu((fabric)->num_entries)); \ | ||
789 | cfg++) | ||
790 | |||
791 | struct ipr_hostrcb_type_20_error { | ||
792 | u8 failure_reason[64]; | ||
793 | u8 reserved[3]; | ||
794 | u8 num_entries; | ||
795 | struct ipr_hostrcb_fabric_desc desc[1]; | ||
796 | }__attribute__((packed, aligned (4))); | ||
797 | |||
740 | struct ipr_hostrcb_error { | 798 | struct ipr_hostrcb_error { |
741 | __be32 failing_dev_ioasc; | 799 | __be32 failing_dev_ioasc; |
742 | struct ipr_res_addr failing_dev_res_addr; | 800 | struct ipr_res_addr failing_dev_res_addr; |
@@ -753,6 +811,7 @@ struct ipr_hostrcb_error { | |||
753 | struct ipr_hostrcb_type_13_error type_13_error; | 811 | struct ipr_hostrcb_type_13_error type_13_error; |
754 | struct ipr_hostrcb_type_14_error type_14_error; | 812 | struct ipr_hostrcb_type_14_error type_14_error; |
755 | struct ipr_hostrcb_type_17_error type_17_error; | 813 | struct ipr_hostrcb_type_17_error type_17_error; |
814 | struct ipr_hostrcb_type_20_error type_20_error; | ||
756 | } u; | 815 | } u; |
757 | }__attribute__((packed, aligned (4))); | 816 | }__attribute__((packed, aligned (4))); |
758 | 817 | ||
@@ -792,6 +851,7 @@ struct ipr_hcam { | |||
792 | #define IPR_HOST_RCB_OVERLAY_ID_14 0x14 | 851 | #define IPR_HOST_RCB_OVERLAY_ID_14 0x14 |
793 | #define IPR_HOST_RCB_OVERLAY_ID_16 0x16 | 852 | #define IPR_HOST_RCB_OVERLAY_ID_16 0x16 |
794 | #define IPR_HOST_RCB_OVERLAY_ID_17 0x17 | 853 | #define IPR_HOST_RCB_OVERLAY_ID_17 0x17 |
854 | #define IPR_HOST_RCB_OVERLAY_ID_20 0x20 | ||
795 | #define IPR_HOST_RCB_OVERLAY_ID_DEFAULT 0xFF | 855 | #define IPR_HOST_RCB_OVERLAY_ID_DEFAULT 0xFF |
796 | 856 | ||
797 | u8 reserved1[3]; | 857 | u8 reserved1[3]; |
@@ -811,6 +871,7 @@ struct ipr_hostrcb { | |||
811 | struct ipr_hcam hcam; | 871 | struct ipr_hcam hcam; |
812 | dma_addr_t hostrcb_dma; | 872 | dma_addr_t hostrcb_dma; |
813 | struct list_head queue; | 873 | struct list_head queue; |
874 | struct ipr_ioa_cfg *ioa_cfg; | ||
814 | }; | 875 | }; |
815 | 876 | ||
816 | /* IPR smart dump table structures */ | 877 | /* IPR smart dump table structures */ |
@@ -1289,6 +1350,17 @@ struct ipr_ucode_image_header { | |||
1289 | } \ | 1350 | } \ |
1290 | } | 1351 | } |
1291 | 1352 | ||
1353 | #define ipr_hcam_err(hostrcb, fmt, ...) \ | ||
1354 | { \ | ||
1355 | if (ipr_is_device(&(hostrcb)->hcam.u.error.failing_dev_res_addr)) { \ | ||
1356 | ipr_ra_err((hostrcb)->ioa_cfg, \ | ||
1357 | (hostrcb)->hcam.u.error.failing_dev_res_addr, \ | ||
1358 | fmt, ##__VA_ARGS__); \ | ||
1359 | } else { \ | ||
1360 | dev_err(&(hostrcb)->ioa_cfg->pdev->dev, fmt, ##__VA_ARGS__); \ | ||
1361 | } \ | ||
1362 | } | ||
1363 | |||
1292 | #define ipr_trace ipr_dbg("%s: %s: Line: %d\n",\ | 1364 | #define ipr_trace ipr_dbg("%s: %s: Line: %d\n",\ |
1293 | __FILE__, __FUNCTION__, __LINE__) | 1365 | __FILE__, __FUNCTION__, __LINE__) |
1294 | 1366 | ||