diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/message/fusion/mptsas.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 29add83da588..b9c69bff218c 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c | |||
@@ -1312,11 +1312,137 @@ mptsas_get_bay_identifier(struct sas_rphy *rphy) | |||
1312 | return rc; | 1312 | return rc; |
1313 | } | 1313 | } |
1314 | 1314 | ||
1315 | static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, | ||
1316 | struct request *req) | ||
1317 | { | ||
1318 | MPT_ADAPTER *ioc = ((MPT_SCSI_HOST *) shost->hostdata)->ioc; | ||
1319 | MPT_FRAME_HDR *mf; | ||
1320 | SmpPassthroughRequest_t *smpreq; | ||
1321 | struct request *rsp = req->next_rq; | ||
1322 | int ret; | ||
1323 | int flagsLength; | ||
1324 | unsigned long timeleft; | ||
1325 | char *psge; | ||
1326 | dma_addr_t dma_addr_in = 0; | ||
1327 | dma_addr_t dma_addr_out = 0; | ||
1328 | u64 sas_address = 0; | ||
1329 | |||
1330 | if (!rsp) { | ||
1331 | printk(KERN_ERR "%s: the smp response space is missing\n", | ||
1332 | __FUNCTION__); | ||
1333 | return -EINVAL; | ||
1334 | } | ||
1335 | |||
1336 | /* do we need to support multiple segments? */ | ||
1337 | if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { | ||
1338 | printk(KERN_ERR "%s: multiple segments req %u %u, rsp %u %u\n", | ||
1339 | __FUNCTION__, req->bio->bi_vcnt, req->data_len, | ||
1340 | rsp->bio->bi_vcnt, rsp->data_len); | ||
1341 | return -EINVAL; | ||
1342 | } | ||
1343 | |||
1344 | ret = mutex_lock_interruptible(&ioc->sas_mgmt.mutex); | ||
1345 | if (ret) | ||
1346 | goto out; | ||
1347 | |||
1348 | mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc); | ||
1349 | if (!mf) { | ||
1350 | ret = -ENOMEM; | ||
1351 | goto out_unlock; | ||
1352 | } | ||
1353 | |||
1354 | smpreq = (SmpPassthroughRequest_t *)mf; | ||
1355 | memset(smpreq, 0, sizeof(*smpreq)); | ||
1356 | |||
1357 | smpreq->RequestDataLength = cpu_to_le16(req->data_len - 4); | ||
1358 | smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH; | ||
1359 | |||
1360 | if (rphy) | ||
1361 | sas_address = rphy->identify.sas_address; | ||
1362 | else { | ||
1363 | struct mptsas_portinfo *port_info; | ||
1364 | |||
1365 | mutex_lock(&ioc->sas_topology_mutex); | ||
1366 | port_info = mptsas_find_portinfo_by_handle(ioc, ioc->handle); | ||
1367 | if (port_info && port_info->phy_info) | ||
1368 | sas_address = | ||
1369 | port_info->phy_info[0].phy->identify.sas_address; | ||
1370 | mutex_unlock(&ioc->sas_topology_mutex); | ||
1371 | } | ||
1372 | |||
1373 | *((u64 *)&smpreq->SASAddress) = cpu_to_le64(sas_address); | ||
1374 | |||
1375 | psge = (char *) | ||
1376 | (((int *) mf) + (offsetof(SmpPassthroughRequest_t, SGL) / 4)); | ||
1377 | |||
1378 | /* request */ | ||
1379 | flagsLength = (MPI_SGE_FLAGS_SIMPLE_ELEMENT | | ||
1380 | MPI_SGE_FLAGS_END_OF_BUFFER | | ||
1381 | MPI_SGE_FLAGS_DIRECTION | | ||
1382 | mpt_addr_size()) << MPI_SGE_FLAGS_SHIFT; | ||
1383 | flagsLength |= (req->data_len - 4); | ||
1384 | |||
1385 | dma_addr_out = pci_map_single(ioc->pcidev, bio_data(req->bio), | ||
1386 | req->data_len, PCI_DMA_BIDIRECTIONAL); | ||
1387 | if (!dma_addr_out) | ||
1388 | goto put_mf; | ||
1389 | mpt_add_sge(psge, flagsLength, dma_addr_out); | ||
1390 | psge += (sizeof(u32) + sizeof(dma_addr_t)); | ||
1391 | |||
1392 | /* response */ | ||
1393 | flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ; | ||
1394 | flagsLength |= rsp->data_len + 4; | ||
1395 | dma_addr_in = pci_map_single(ioc->pcidev, bio_data(rsp->bio), | ||
1396 | rsp->data_len, PCI_DMA_BIDIRECTIONAL); | ||
1397 | if (!dma_addr_in) | ||
1398 | goto unmap; | ||
1399 | mpt_add_sge(psge, flagsLength, dma_addr_in); | ||
1400 | |||
1401 | mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf); | ||
1402 | |||
1403 | timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ); | ||
1404 | if (!timeleft) { | ||
1405 | printk(KERN_ERR "%s: smp timeout!\n", __FUNCTION__); | ||
1406 | /* On timeout reset the board */ | ||
1407 | mpt_HardResetHandler(ioc, CAN_SLEEP); | ||
1408 | ret = -ETIMEDOUT; | ||
1409 | goto unmap; | ||
1410 | } | ||
1411 | mf = NULL; | ||
1412 | |||
1413 | if (ioc->sas_mgmt.status & MPT_IOCTL_STATUS_RF_VALID) { | ||
1414 | SmpPassthroughReply_t *smprep; | ||
1415 | |||
1416 | smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply; | ||
1417 | memcpy(req->sense, smprep, sizeof(*smprep)); | ||
1418 | req->sense_len = sizeof(*smprep); | ||
1419 | } else { | ||
1420 | printk(KERN_ERR "%s: smp passthru reply failed to be returned\n", | ||
1421 | __FUNCTION__); | ||
1422 | ret = -ENXIO; | ||
1423 | } | ||
1424 | unmap: | ||
1425 | if (dma_addr_out) | ||
1426 | pci_unmap_single(ioc->pcidev, dma_addr_out, req->data_len, | ||
1427 | PCI_DMA_BIDIRECTIONAL); | ||
1428 | if (dma_addr_in) | ||
1429 | pci_unmap_single(ioc->pcidev, dma_addr_in, rsp->data_len, | ||
1430 | PCI_DMA_BIDIRECTIONAL); | ||
1431 | put_mf: | ||
1432 | if (mf) | ||
1433 | mpt_free_msg_frame(ioc, mf); | ||
1434 | out_unlock: | ||
1435 | mutex_unlock(&ioc->sas_mgmt.mutex); | ||
1436 | out: | ||
1437 | return ret; | ||
1438 | } | ||
1439 | |||
1315 | static struct sas_function_template mptsas_transport_functions = { | 1440 | static struct sas_function_template mptsas_transport_functions = { |
1316 | .get_linkerrors = mptsas_get_linkerrors, | 1441 | .get_linkerrors = mptsas_get_linkerrors, |
1317 | .get_enclosure_identifier = mptsas_get_enclosure_identifier, | 1442 | .get_enclosure_identifier = mptsas_get_enclosure_identifier, |
1318 | .get_bay_identifier = mptsas_get_bay_identifier, | 1443 | .get_bay_identifier = mptsas_get_bay_identifier, |
1319 | .phy_reset = mptsas_phy_reset, | 1444 | .phy_reset = mptsas_phy_reset, |
1445 | .smp_handler = mptsas_smp_handler, | ||
1320 | }; | 1446 | }; |
1321 | 1447 | ||
1322 | static struct scsi_transport_template *mptsas_transport_template; | 1448 | static struct scsi_transport_template *mptsas_transport_template; |