diff options
Diffstat (limited to 'drivers/message/fusion/mptsas.c')
| -rw-r--r-- | drivers/message/fusion/mptsas.c | 241 |
1 files changed, 213 insertions, 28 deletions
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 5a06d8d8694e..2512d0e6155e 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c | |||
| @@ -89,6 +89,8 @@ static int mptsasMgmtCtx = -1; | |||
| 89 | enum mptsas_hotplug_action { | 89 | enum mptsas_hotplug_action { |
| 90 | MPTSAS_ADD_DEVICE, | 90 | MPTSAS_ADD_DEVICE, |
| 91 | MPTSAS_DEL_DEVICE, | 91 | MPTSAS_DEL_DEVICE, |
| 92 | MPTSAS_ADD_RAID, | ||
| 93 | MPTSAS_DEL_RAID, | ||
| 92 | }; | 94 | }; |
| 93 | 95 | ||
| 94 | struct mptsas_hotplug_event { | 96 | struct mptsas_hotplug_event { |
| @@ -114,6 +116,7 @@ struct mptsas_hotplug_event { | |||
| 114 | 116 | ||
| 115 | struct mptsas_devinfo { | 117 | struct mptsas_devinfo { |
| 116 | u16 handle; /* unique id to address this device */ | 118 | u16 handle; /* unique id to address this device */ |
| 119 | u16 handle_parent; /* unique id to address parent device */ | ||
| 117 | u8 phy_id; /* phy number of parent device */ | 120 | u8 phy_id; /* phy number of parent device */ |
| 118 | u8 port_id; /* sas physical port this device | 121 | u8 port_id; /* sas physical port this device |
| 119 | is assoc'd with */ | 122 | is assoc'd with */ |
| @@ -301,9 +304,8 @@ mptsas_slave_alloc(struct scsi_device *sdev) | |||
| 301 | } | 304 | } |
| 302 | mutex_unlock(&hd->ioc->sas_topology_mutex); | 305 | mutex_unlock(&hd->ioc->sas_topology_mutex); |
| 303 | 306 | ||
| 304 | printk("No matching SAS device found!!\n"); | ||
| 305 | kfree(vdev); | 307 | kfree(vdev); |
| 306 | return -ENODEV; | 308 | return -ENXIO; |
| 307 | 309 | ||
| 308 | out: | 310 | out: |
| 309 | vtarget->ioc_id = vdev->ioc_id; | 311 | vtarget->ioc_id = vdev->ioc_id; |
| @@ -321,6 +323,7 @@ mptsas_slave_destroy(struct scsi_device *sdev) | |||
| 321 | struct sas_rphy *rphy; | 323 | struct sas_rphy *rphy; |
| 322 | struct mptsas_portinfo *p; | 324 | struct mptsas_portinfo *p; |
| 323 | int i; | 325 | int i; |
| 326 | VirtDevice *vdev; | ||
| 324 | 327 | ||
| 325 | /* | 328 | /* |
| 326 | * Handle hotplug removal case. | 329 | * Handle hotplug removal case. |
| @@ -344,8 +347,29 @@ mptsas_slave_destroy(struct scsi_device *sdev) | |||
| 344 | out: | 347 | out: |
| 345 | mutex_unlock(&hd->ioc->sas_topology_mutex); | 348 | mutex_unlock(&hd->ioc->sas_topology_mutex); |
| 346 | /* | 349 | /* |
| 347 | * TODO: Issue target reset to flush firmware outstanding commands. | 350 | * Issue target reset to flush firmware outstanding commands. |
| 348 | */ | 351 | */ |
| 352 | vdev = sdev->hostdata; | ||
| 353 | if (vdev->configured_lun){ | ||
| 354 | if (mptscsih_TMHandler(hd, | ||
| 355 | MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, | ||
| 356 | vdev->bus_id, | ||
| 357 | vdev->target_id, | ||
| 358 | 0, 0, 5 /* 5 second timeout */) | ||
| 359 | < 0){ | ||
| 360 | |||
| 361 | /* The TM request failed! | ||
| 362 | * Fatal error case. | ||
| 363 | */ | ||
| 364 | printk(MYIOC_s_WARN_FMT | ||
| 365 | "Error processing TaskMgmt id=%d TARGET_RESET\n", | ||
| 366 | hd->ioc->name, | ||
| 367 | vdev->target_id); | ||
| 368 | |||
| 369 | hd->tmPending = 0; | ||
| 370 | hd->tmState = TM_STATE_NONE; | ||
| 371 | } | ||
| 372 | } | ||
| 349 | mptscsih_slave_destroy(sdev); | 373 | mptscsih_slave_destroy(sdev); |
| 350 | } | 374 | } |
| 351 | 375 | ||
| @@ -714,6 +738,7 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, | |||
| 714 | mptsas_print_device_pg0(buffer); | 738 | mptsas_print_device_pg0(buffer); |
| 715 | 739 | ||
| 716 | device_info->handle = le16_to_cpu(buffer->DevHandle); | 740 | device_info->handle = le16_to_cpu(buffer->DevHandle); |
| 741 | device_info->handle_parent = le16_to_cpu(buffer->ParentDevHandle); | ||
| 717 | device_info->phy_id = buffer->PhyNum; | 742 | device_info->phy_id = buffer->PhyNum; |
| 718 | device_info->port_id = buffer->PhysicalPort; | 743 | device_info->port_id = buffer->PhysicalPort; |
| 719 | device_info->id = buffer->TargetID; | 744 | device_info->id = buffer->TargetID; |
| @@ -863,6 +888,26 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, | |||
| 863 | return error; | 888 | return error; |
| 864 | } | 889 | } |
| 865 | 890 | ||
| 891 | /* | ||
| 892 | * Returns true if there is a scsi end device | ||
| 893 | */ | ||
| 894 | static inline int | ||
| 895 | mptsas_is_end_device(struct mptsas_devinfo * attached) | ||
| 896 | { | ||
| 897 | if ((attached->handle) && | ||
| 898 | (attached->device_info & | ||
| 899 | MPI_SAS_DEVICE_INFO_END_DEVICE) && | ||
| 900 | ((attached->device_info & | ||
| 901 | MPI_SAS_DEVICE_INFO_SSP_TARGET) | | ||
| 902 | (attached->device_info & | ||
| 903 | MPI_SAS_DEVICE_INFO_STP_TARGET) | | ||
| 904 | (attached->device_info & | ||
| 905 | MPI_SAS_DEVICE_INFO_SATA_DEVICE))) | ||
| 906 | return 1; | ||
| 907 | else | ||
| 908 | return 0; | ||
| 909 | } | ||
| 910 | |||
| 866 | static void | 911 | static void |
| 867 | mptsas_parse_device_info(struct sas_identify *identify, | 912 | mptsas_parse_device_info(struct sas_identify *identify, |
| 868 | struct mptsas_devinfo *device_info) | 913 | struct mptsas_devinfo *device_info) |
| @@ -1227,7 +1272,7 @@ mptsas_find_phyinfo_by_parent(MPT_ADAPTER *ioc, u16 parent_handle, u8 phy_id) | |||
| 1227 | } | 1272 | } |
| 1228 | 1273 | ||
| 1229 | static struct mptsas_phyinfo * | 1274 | static struct mptsas_phyinfo * |
| 1230 | mptsas_find_phyinfo_by_handle(MPT_ADAPTER *ioc, u16 handle) | 1275 | mptsas_find_phyinfo_by_target(MPT_ADAPTER *ioc, u32 id) |
| 1231 | { | 1276 | { |
| 1232 | struct mptsas_portinfo *port_info; | 1277 | struct mptsas_portinfo *port_info; |
| 1233 | struct mptsas_phyinfo *phy_info = NULL; | 1278 | struct mptsas_phyinfo *phy_info = NULL; |
| @@ -1239,12 +1284,12 @@ mptsas_find_phyinfo_by_handle(MPT_ADAPTER *ioc, u16 handle) | |||
| 1239 | */ | 1284 | */ |
| 1240 | mutex_lock(&ioc->sas_topology_mutex); | 1285 | mutex_lock(&ioc->sas_topology_mutex); |
| 1241 | list_for_each_entry(port_info, &ioc->sas_topology, list) { | 1286 | list_for_each_entry(port_info, &ioc->sas_topology, list) { |
| 1242 | for (i = 0; i < port_info->num_phys; i++) { | 1287 | for (i = 0; i < port_info->num_phys; i++) |
| 1243 | if (port_info->phy_info[i].attached.handle == handle) { | 1288 | if (mptsas_is_end_device(&port_info->phy_info[i].attached)) |
| 1244 | phy_info = &port_info->phy_info[i]; | 1289 | if (port_info->phy_info[i].attached.id == id) { |
| 1245 | break; | 1290 | phy_info = &port_info->phy_info[i]; |
| 1246 | } | 1291 | break; |
| 1247 | } | 1292 | } |
| 1248 | } | 1293 | } |
| 1249 | mutex_unlock(&ioc->sas_topology_mutex); | 1294 | mutex_unlock(&ioc->sas_topology_mutex); |
| 1250 | 1295 | ||
| @@ -1258,36 +1303,58 @@ mptsas_hotplug_work(void *arg) | |||
| 1258 | MPT_ADAPTER *ioc = ev->ioc; | 1303 | MPT_ADAPTER *ioc = ev->ioc; |
| 1259 | struct mptsas_phyinfo *phy_info; | 1304 | struct mptsas_phyinfo *phy_info; |
| 1260 | struct sas_rphy *rphy; | 1305 | struct sas_rphy *rphy; |
| 1306 | struct scsi_device *sdev; | ||
| 1261 | char *ds = NULL; | 1307 | char *ds = NULL; |
| 1262 | 1308 | struct mptsas_devinfo sas_device; | |
| 1263 | if (ev->device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET) | ||
| 1264 | ds = "ssp"; | ||
| 1265 | if (ev->device_info & MPI_SAS_DEVICE_INFO_STP_TARGET) | ||
| 1266 | ds = "stp"; | ||
| 1267 | if (ev->device_info & MPI_SAS_DEVICE_INFO_SATA_DEVICE) | ||
| 1268 | ds = "sata"; | ||
| 1269 | 1309 | ||
| 1270 | switch (ev->event_type) { | 1310 | switch (ev->event_type) { |
| 1271 | case MPTSAS_DEL_DEVICE: | 1311 | case MPTSAS_DEL_DEVICE: |
| 1272 | printk(MYIOC_s_INFO_FMT | ||
| 1273 | "removing %s device, channel %d, id %d, phy %d\n", | ||
| 1274 | ioc->name, ds, ev->channel, ev->id, ev->phy_id); | ||
| 1275 | 1312 | ||
| 1276 | phy_info = mptsas_find_phyinfo_by_handle(ioc, ev->handle); | 1313 | phy_info = mptsas_find_phyinfo_by_target(ioc, ev->id); |
| 1277 | if (!phy_info) { | 1314 | if (!phy_info) { |
| 1278 | printk("mptsas: remove event for non-existant PHY.\n"); | 1315 | printk("mptsas: remove event for non-existant PHY.\n"); |
| 1279 | break; | 1316 | break; |
| 1280 | } | 1317 | } |
| 1281 | 1318 | ||
| 1319 | if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET) | ||
| 1320 | ds = "ssp"; | ||
| 1321 | if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_STP_TARGET) | ||
| 1322 | ds = "stp"; | ||
| 1323 | if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SATA_DEVICE) | ||
| 1324 | ds = "sata"; | ||
| 1325 | |||
| 1326 | printk(MYIOC_s_INFO_FMT | ||
| 1327 | "removing %s device, channel %d, id %d, phy %d\n", | ||
| 1328 | ioc->name, ds, ev->channel, ev->id, phy_info->phy_id); | ||
| 1329 | |||
| 1282 | if (phy_info->rphy) { | 1330 | if (phy_info->rphy) { |
| 1283 | sas_rphy_delete(phy_info->rphy); | 1331 | sas_rphy_delete(phy_info->rphy); |
| 1284 | phy_info->rphy = NULL; | 1332 | phy_info->rphy = NULL; |
| 1285 | } | 1333 | } |
| 1286 | break; | 1334 | break; |
| 1287 | case MPTSAS_ADD_DEVICE: | 1335 | case MPTSAS_ADD_DEVICE: |
| 1288 | printk(MYIOC_s_INFO_FMT | 1336 | |
| 1289 | "attaching %s device, channel %d, id %d, phy %d\n", | 1337 | /* |
| 1290 | ioc->name, ds, ev->channel, ev->id, ev->phy_id); | 1338 | * When there is no sas address, |
| 1339 | * RAID volumes are being deleted, | ||
| 1340 | * and hidden phy disk are being added. | ||
| 1341 | * We don't know the SAS data yet, | ||
| 1342 | * so lookup sas device page to get | ||
| 1343 | * pertaining info | ||
| 1344 | */ | ||
| 1345 | if (!ev->sas_address) { | ||
| 1346 | if (mptsas_sas_device_pg0(ioc, | ||
| 1347 | &sas_device, ev->id, | ||
| 1348 | (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID << | ||
| 1349 | MPI_SAS_DEVICE_PGAD_FORM_SHIFT))) | ||
| 1350 | break; | ||
| 1351 | ev->handle = sas_device.handle; | ||
| 1352 | ev->parent_handle = sas_device.handle_parent; | ||
| 1353 | ev->channel = sas_device.channel; | ||
| 1354 | ev->phy_id = sas_device.phy_id; | ||
| 1355 | ev->sas_address = sas_device.sas_address; | ||
| 1356 | ev->device_info = sas_device.device_info; | ||
| 1357 | } | ||
| 1291 | 1358 | ||
| 1292 | phy_info = mptsas_find_phyinfo_by_parent(ioc, | 1359 | phy_info = mptsas_find_phyinfo_by_parent(ioc, |
| 1293 | ev->parent_handle, ev->phy_id); | 1360 | ev->parent_handle, ev->phy_id); |
| @@ -1310,10 +1377,23 @@ mptsas_hotplug_work(void *arg) | |||
| 1310 | phy_info->attached.sas_address = ev->sas_address; | 1377 | phy_info->attached.sas_address = ev->sas_address; |
| 1311 | phy_info->attached.device_info = ev->device_info; | 1378 | phy_info->attached.device_info = ev->device_info; |
| 1312 | 1379 | ||
| 1380 | if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET) | ||
| 1381 | ds = "ssp"; | ||
| 1382 | if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_STP_TARGET) | ||
| 1383 | ds = "stp"; | ||
| 1384 | if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SATA_DEVICE) | ||
| 1385 | ds = "sata"; | ||
| 1386 | |||
| 1387 | printk(MYIOC_s_INFO_FMT | ||
| 1388 | "attaching %s device, channel %d, id %d, phy %d\n", | ||
| 1389 | ioc->name, ds, ev->channel, ev->id, ev->phy_id); | ||
| 1390 | |||
| 1391 | |||
| 1313 | rphy = sas_rphy_alloc(phy_info->phy); | 1392 | rphy = sas_rphy_alloc(phy_info->phy); |
| 1314 | if (!rphy) | 1393 | if (!rphy) |
| 1315 | break; /* non-fatal: an rphy can be added later */ | 1394 | break; /* non-fatal: an rphy can be added later */ |
| 1316 | 1395 | ||
| 1396 | rphy->scsi_target_id = phy_info->attached.id; | ||
| 1317 | mptsas_parse_device_info(&rphy->identify, &phy_info->attached); | 1397 | mptsas_parse_device_info(&rphy->identify, &phy_info->attached); |
| 1318 | if (sas_rphy_add(rphy)) { | 1398 | if (sas_rphy_add(rphy)) { |
| 1319 | sas_rphy_free(rphy); | 1399 | sas_rphy_free(rphy); |
| @@ -1322,6 +1402,40 @@ mptsas_hotplug_work(void *arg) | |||
| 1322 | 1402 | ||
| 1323 | phy_info->rphy = rphy; | 1403 | phy_info->rphy = rphy; |
| 1324 | break; | 1404 | break; |
| 1405 | case MPTSAS_ADD_RAID: | ||
| 1406 | sdev = scsi_device_lookup( | ||
| 1407 | ioc->sh, | ||
| 1408 | ioc->num_ports, | ||
| 1409 | ev->id, | ||
| 1410 | 0); | ||
| 1411 | if (sdev) { | ||
| 1412 | scsi_device_put(sdev); | ||
| 1413 | break; | ||
| 1414 | } | ||
| 1415 | printk(MYIOC_s_INFO_FMT | ||
| 1416 | "attaching device, channel %d, id %d\n", | ||
| 1417 | ioc->name, ioc->num_ports, ev->id); | ||
| 1418 | scsi_add_device(ioc->sh, | ||
| 1419 | ioc->num_ports, | ||
| 1420 | ev->id, | ||
| 1421 | 0); | ||
| 1422 | mpt_findImVolumes(ioc); | ||
| 1423 | break; | ||
| 1424 | case MPTSAS_DEL_RAID: | ||
| 1425 | sdev = scsi_device_lookup( | ||
| 1426 | ioc->sh, | ||
| 1427 | ioc->num_ports, | ||
| 1428 | ev->id, | ||
| 1429 | 0); | ||
| 1430 | if (!sdev) | ||
| 1431 | break; | ||
| 1432 | printk(MYIOC_s_INFO_FMT | ||
| 1433 | "removing device, channel %d, id %d\n", | ||
| 1434 | ioc->name, ioc->num_ports, ev->id); | ||
| 1435 | scsi_remove_device(sdev); | ||
| 1436 | scsi_device_put(sdev); | ||
| 1437 | mpt_findImVolumes(ioc); | ||
| 1438 | break; | ||
| 1325 | } | 1439 | } |
| 1326 | 1440 | ||
| 1327 | kfree(ev); | 1441 | kfree(ev); |
| @@ -1372,23 +1486,94 @@ mptscsih_send_sas_event(MPT_ADAPTER *ioc, | |||
| 1372 | schedule_work(&ev->work); | 1486 | schedule_work(&ev->work); |
| 1373 | } | 1487 | } |
| 1374 | 1488 | ||
| 1489 | static void | ||
| 1490 | mptscsih_send_raid_event(MPT_ADAPTER *ioc, | ||
| 1491 | EVENT_DATA_RAID *raid_event_data) | ||
| 1492 | { | ||
| 1493 | struct mptsas_hotplug_event *ev; | ||
| 1494 | RAID_VOL0_STATUS * volumeStatus; | ||
| 1495 | |||
| 1496 | if (ioc->bus_type != SAS) | ||
| 1497 | return; | ||
| 1498 | |||
| 1499 | ev = kmalloc(sizeof(*ev), GFP_ATOMIC); | ||
| 1500 | if (!ev) { | ||
| 1501 | printk(KERN_WARNING "mptsas: lost hotplug event\n"); | ||
| 1502 | return; | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | memset(ev,0,sizeof(struct mptsas_hotplug_event)); | ||
| 1506 | INIT_WORK(&ev->work, mptsas_hotplug_work, ev); | ||
| 1507 | ev->ioc = ioc; | ||
| 1508 | ev->id = raid_event_data->VolumeID; | ||
| 1509 | |||
| 1510 | switch (raid_event_data->ReasonCode) { | ||
| 1511 | case MPI_EVENT_RAID_RC_PHYSDISK_DELETED: | ||
| 1512 | ev->event_type = MPTSAS_ADD_DEVICE; | ||
| 1513 | break; | ||
| 1514 | case MPI_EVENT_RAID_RC_PHYSDISK_CREATED: | ||
| 1515 | ev->event_type = MPTSAS_DEL_DEVICE; | ||
| 1516 | break; | ||
| 1517 | case MPI_EVENT_RAID_RC_VOLUME_DELETED: | ||
| 1518 | ev->event_type = MPTSAS_DEL_RAID; | ||
| 1519 | break; | ||
| 1520 | case MPI_EVENT_RAID_RC_VOLUME_CREATED: | ||
| 1521 | ev->event_type = MPTSAS_ADD_RAID; | ||
| 1522 | break; | ||
| 1523 | case MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED: | ||
| 1524 | volumeStatus = (RAID_VOL0_STATUS *) & | ||
| 1525 | raid_event_data->SettingsStatus; | ||
| 1526 | ev->event_type = (volumeStatus->State == | ||
| 1527 | MPI_RAIDVOL0_STATUS_STATE_FAILED) ? | ||
| 1528 | MPTSAS_DEL_RAID : MPTSAS_ADD_RAID; | ||
| 1529 | break; | ||
| 1530 | default: | ||
| 1531 | break; | ||
| 1532 | } | ||
| 1533 | schedule_work(&ev->work); | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | ||
| 1537 | /* work queue thread to clear the persitency table */ | ||
| 1538 | static void | ||
| 1539 | mptscsih_sas_persist_clear_table(void * arg) | ||
| 1540 | { | ||
| 1541 | MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg; | ||
| 1542 | |||
| 1543 | mptbase_sas_persist_operation(ioc, MPI_SAS_OP_CLEAR_NOT_PRESENT); | ||
| 1544 | } | ||
| 1545 | |||
| 1375 | static int | 1546 | static int |
| 1376 | mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) | 1547 | mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) |
| 1377 | { | 1548 | { |
| 1549 | int rc=1; | ||
| 1378 | u8 event = le32_to_cpu(reply->Event) & 0xFF; | 1550 | u8 event = le32_to_cpu(reply->Event) & 0xFF; |
| 1379 | 1551 | ||
| 1380 | if (!ioc->sh) | 1552 | if (!ioc->sh) |
| 1381 | return 1; | 1553 | goto out; |
| 1382 | 1554 | ||
| 1383 | switch (event) { | 1555 | switch (event) { |
| 1384 | case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE: | 1556 | case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE: |
| 1385 | mptscsih_send_sas_event(ioc, | 1557 | mptscsih_send_sas_event(ioc, |
| 1386 | (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data); | 1558 | (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data); |
| 1387 | return 1; /* currently means nothing really */ | 1559 | break; |
| 1388 | 1560 | case MPI_EVENT_INTEGRATED_RAID: | |
| 1561 | mptscsih_send_raid_event(ioc, | ||
| 1562 | (EVENT_DATA_RAID *)reply->Data); | ||
| 1563 | break; | ||
| 1564 | case MPI_EVENT_PERSISTENT_TABLE_FULL: | ||
| 1565 | INIT_WORK(&ioc->mptscsih_persistTask, | ||
| 1566 | mptscsih_sas_persist_clear_table, | ||
| 1567 | (void *)ioc); | ||
| 1568 | schedule_work(&ioc->mptscsih_persistTask); | ||
| 1569 | break; | ||
| 1389 | default: | 1570 | default: |
| 1390 | return mptscsih_event_process(ioc, reply); | 1571 | rc = mptscsih_event_process(ioc, reply); |
| 1572 | break; | ||
| 1391 | } | 1573 | } |
| 1574 | out: | ||
| 1575 | |||
| 1576 | return rc; | ||
| 1392 | } | 1577 | } |
| 1393 | 1578 | ||
| 1394 | static int | 1579 | static int |
