| 1379 | /// 3SNIC RAID support |
| 1380 | |
| 1381 | class linux_sssraid_device |
| 1382 | : public /* implements */ scsi_device, |
| 1383 | public /* extends */ linux_smart_device |
| 1384 | { |
| 1385 | public: |
| 1386 | linux_sssraid_device(smart_interface *intf, const char *name, |
| 1387 | unsigned int eid, unsigned int sid); |
| 1388 | |
| 1389 | virtual bool scsi_pass_through(scsi_cmnd_io *iop) override; |
| 1390 | |
| 1391 | private: |
| 1392 | unsigned int m_eid; |
| 1393 | unsigned int m_sid; |
| 1394 | |
| 1395 | bool scsi_cmd(int cdbLen, void *cdb, int dataLen, void *data, int direction); |
| 1396 | }; |
| 1397 | |
| 1398 | linux_sssraid_device::linux_sssraid_device(smart_interface *intf, |
| 1399 | const char *dev_name, unsigned int eid, unsigned int sid) |
| 1400 | : smart_device(intf, dev_name, "sssraid", "sssraid"), |
| 1401 | linux_smart_device(O_RDWR | O_NONBLOCK), |
| 1402 | m_eid(eid), m_sid(sid) |
| 1403 | { |
| 1404 | set_info().info_name = strprintf("%s [sssraid_disk_%02d_%02d]", dev_name, eid, sid); |
| 1405 | set_info().dev_type = strprintf("sssraid,%d,%d", eid, sid); |
| 1406 | } |
| 1407 | |
| 1408 | bool linux_sssraid_device::scsi_pass_through(scsi_cmnd_io *iop) |
| 1409 | { |
| 1410 | int report = scsi_debugmode; |
| 1411 | if (report > 0) { |
| 1412 | int k, j; |
| 1413 | const unsigned char * ucp = iop->cmnd; |
| 1414 | const char * np; |
| 1415 | char buff[256]; |
| 1416 | const int sz = (int)sizeof(buff); |
| 1417 | |
| 1418 | np = scsi_get_opcode_name(ucp[0]); |
| 1419 | j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); |
| 1420 | for (k = 0; k < (int)iop->cmnd_len; ++k) |
| 1421 | j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); |
| 1422 | if ((report > 1) && (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { |
| 1423 | int trunc = (iop->dxfer_len > 256) ? 1 : 0; |
| 1424 | |
| 1425 | snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " |
| 1426 | "data, len=%d%s:\n", (int)iop->dxfer_len, |
| 1427 | (trunc ? " [only first 256 bytes shown]" : "")); |
| 1428 | dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); |
| 1429 | } |
| 1430 | else |
| 1431 | snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); |
| 1432 | pout("%s", buff); |
| 1433 | } |
| 1434 | |
| 1435 | bool r = scsi_cmd(iop->cmnd_len, iop->cmnd, |
| 1436 | iop->dxfer_len, iop->dxferp, iop->dxfer_dir); |
| 1437 | return r; |
| 1438 | } |
| 1439 | |
| 1440 | /* Issue passthrough scsi commands to sssraid controllers */ |
| 1441 | bool linux_sssraid_device::scsi_cmd(int cdbLen, void *cdb, |
| 1442 | int dataLen, void *data, int dxfer_dir) |
| 1443 | { |
| 1444 | struct sg_io_v4 io_hdr_v4; |
| 1445 | struct cmd_scsi_passthrough scsi_param; |
| 1446 | unsigned char sense_buff[96] = { 0 }; |
| 1447 | struct bsg_ioctl_cmd bsg_param; |
| 1448 | memset(&io_hdr_v4, 0, sizeof(io_hdr_v4)); |
| 1449 | memset(&scsi_param, 0, sizeof(scsi_param)); |
| 1450 | memset(&bsg_param, 0, sizeof(bsg_param)); |
| 1451 | scsi_param.sense_buffer = sense_buff; |
| 1452 | scsi_param.sense_buffer_len = 96; |
| 1453 | scsi_param.cdb_len = cdbLen; |
| 1454 | memcpy(scsi_param.cdb, cdb, cdbLen); |
| 1455 | scsi_param.loc.enc_id = m_eid; |
| 1456 | scsi_param.loc.slot_id = m_sid; |
| 1457 | |
| 1458 | io_hdr_v4.guard = 'Q'; |
| 1459 | io_hdr_v4.protocol = BSG_PROTOCOL_SCSI; |
| 1460 | io_hdr_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT; |
| 1461 | io_hdr_v4.response = (uintptr_t)sense_buff; |
| 1462 | io_hdr_v4.max_response_len = ADM_SCSI_CDB_SENSE_MAX_LEN; |
| 1463 | io_hdr_v4.request_len = sizeof(struct bsg_ioctl_cmd); |
| 1464 | io_hdr_v4.request = (uintptr_t)(&bsg_param); |
| 1465 | io_hdr_v4.timeout = BSG_APPEND_TIMEOUT_MS + DEFAULT_CONMMAND_TIMEOUT_MS; |
| 1466 | |
| 1467 | switch (dxfer_dir) { |
| 1468 | case DXFER_NONE: |
| 1469 | case DXFER_FROM_DEVICE: |
| 1470 | io_hdr_v4.din_xferp = (uintptr_t)data; |
| 1471 | io_hdr_v4.din_xfer_len = dataLen; |
| 1472 | bsg_param.ioctl_pthru.opcode = ADM_RAID_READ; |
| 1473 | break; |
| 1474 | case DXFER_TO_DEVICE: |
| 1475 | io_hdr_v4.dout_xferp = (uintptr_t)data; |
| 1476 | io_hdr_v4.dout_xfer_len = dataLen; |
| 1477 | bsg_param.ioctl_pthru.opcode = ADM_RAID_WRITE; |
| 1478 | break; |
| 1479 | default: |
| 1480 | pout("scsi_cmd: bad dxfer_dir\n"); |
| 1481 | return set_err(EINVAL, "scsi_cmd: bad dxfer_dir\n"); |
| 1482 | } |
| 1483 | |
| 1484 | bsg_param.msgcode = ADM_BSG_MSGCODE_SCSI_PTHRU; |
| 1485 | bsg_param.ioctl_pthru.timeout_ms = DEFAULT_CONMMAND_TIMEOUT_MS; |
| 1486 | bsg_param.ioctl_pthru.info_1.subopcode = ADM_CMD_SCSI_PASSTHROUGH; |
| 1487 | bsg_param.ioctl_pthru.addr = (uintptr_t)data; |
| 1488 | bsg_param.ioctl_pthru.data_len = dataLen; |
| 1489 | |
| 1490 | bsg_param.ioctl_pthru.info_0.cdb_len = scsi_param.cdb_len; |
| 1491 | bsg_param.ioctl_pthru.sense_addr = (uintptr_t)scsi_param.sense_buffer; |
| 1492 | bsg_param.ioctl_pthru.info_0.res_sense_len = scsi_param.sense_buffer_len; |
| 1493 | io_hdr_v4.response = (uintptr_t)scsi_param.sense_buffer; |
| 1494 | io_hdr_v4.response_len = scsi_param.sense_buffer_len; |
| 1495 | bsg_param.ioctl_pthru.info_3.eid = scsi_param.loc.enc_id; |
| 1496 | bsg_param.ioctl_pthru.info_3.sid = scsi_param.loc.slot_id; |
| 1497 | bsg_param.ioctl_pthru.info_4.did = scsi_param.loc.did; |
| 1498 | bsg_param.ioctl_pthru.info_4.did_flag = scsi_param.loc.flag; |
| 1499 | |
| 1500 | memcpy(&bsg_param.ioctl_pthru.cdw16, scsi_param.cdb, scsi_param.cdb_len); |
| 1501 | |
| 1502 | int r = ioctl(get_fd(), SG_IO, &io_hdr_v4); |
| 1503 | if (r < 0) { |
| 1504 | return (r); |
| 1505 | } |
| 1506 | |
| 1507 | return true; |
| 1508 | } |
| 1509 | |
| 1510 | ///////////////////////////////////////////////////////////////////////////// |
| 3031 | // getting devices from 3SNIC Raid, if available |
| 3032 | bool linux_smart_interface::get_dev_sssraid(smart_device_list & devlist) |
| 3033 | { |
| 3034 | /* Scanning of disks on sssraid device */ |
| 3035 | char line[128]; |
| 3036 | FILE * fp = NULL; |
| 3037 | |
| 3038 | // getting bus numbers with 3snic sas devices |
| 3039 | // we are using sysfs to get list of all scsi hosts |
| 3040 | DIR * dp = opendir ("/sys/class/scsi_host/"); |
| 3041 | if (dp != NULL) |
| 3042 | { |
| 3043 | struct dirent *ep; |
| 3044 | while ((ep = readdir (dp)) != NULL) { |
| 3045 | unsigned int host_no = 0; |
| 3046 | if (!sscanf(ep->d_name, "host%u", &host_no)) |
| 3047 | continue; |
| 3048 | /* proc_name should be sssraid */ |
| 3049 | char sysfsdir[256]; |
| 3050 | snprintf(sysfsdir, sizeof(sysfsdir) - 1, |
| 3051 | "/sys/class/scsi_host/host%u/proc_name", host_no); |
| 3052 | if((fp = fopen(sysfsdir, "r")) == NULL) |
| 3053 | continue; |
| 3054 | if(fgets(line, sizeof(line), fp) != NULL && !strncmp(line,"sssraid",7)) { |
| 3055 | sssraid_pd_add_list(host_no, devlist); |
| 3056 | } |
| 3057 | fclose(fp); |
| 3058 | } |
| 3059 | (void) closedir (dp); |
| 3060 | } else { /* sysfs not mounted ? */ |
| 3061 | for(unsigned i = 0; i <=16; i++) // trying to add devices on first 16 buses |
| 3062 | sssraid_pd_add_list(i, devlist); |
| 3063 | } |
| 3064 | return true; |
| 3065 | } |
| 3066 | |
| 3251 | int |
| 3252 | linux_smart_interface::sssraid_pdlist_cmd(int bus_no, uint16_t start_idx_param, void *buf, size_t bufsize, uint8_t *statusp) |
| 3253 | { |
| 3254 | struct sg_io_v4 io_hdr_v4; |
| 3255 | unsigned char sense_buff[ADM_SCSI_CDB_SENSE_MAX_LEN] = { 0 }; |
| 3256 | struct bsg_ioctl_cmd bsg_param; |
| 3257 | u8 cmd_param[24] = { 0 }; |
| 3258 | |
| 3259 | memset(&io_hdr_v4, 0, sizeof(io_hdr_v4)); |
| 3260 | memset(&bsg_param, 0, sizeof(bsg_param)); |
| 3261 | io_hdr_v4.guard = 'Q'; |
| 3262 | io_hdr_v4.protocol = BSG_PROTOCOL_SCSI; |
| 3263 | io_hdr_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT; |
| 3264 | io_hdr_v4.response = (uintptr_t)sense_buff; |
| 3265 | io_hdr_v4.max_response_len = ADM_SCSI_CDB_SENSE_MAX_LEN; |
| 3266 | io_hdr_v4.request_len = sizeof(struct bsg_ioctl_cmd); |
| 3267 | io_hdr_v4.request = (uintptr_t)(&bsg_param); |
| 3268 | io_hdr_v4.timeout = BSG_APPEND_TIMEOUT_MS + DEFAULT_CONMMAND_TIMEOUT_MS; |
| 3269 | |
| 3270 | if (bufsize >0) { |
| 3271 | io_hdr_v4.din_xferp = (uintptr_t)buf; |
| 3272 | io_hdr_v4.din_xfer_len = bufsize; |
| 3273 | } |
| 3274 | |
| 3275 | bsg_param.msgcode = 0; |
| 3276 | bsg_param.ioctl_r64.opcode = ADM_RAID_READ; |
| 3277 | bsg_param.ioctl_r64.timeout_ms = DEFAULT_CONMMAND_TIMEOUT_MS; |
| 3278 | bsg_param.ioctl_r64.info_0.subopcode = ADM_CMD_SHOW_PDLIST; |
| 3279 | bsg_param.ioctl_r64.addr = (uintptr_t)buf; |
| 3280 | bsg_param.ioctl_r64.info_1.data_len = bufsize; |
| 3281 | bsg_param.ioctl_r64.data_len = bufsize; |
| 3282 | bsg_param.ioctl_r64.info_1.param_len = sizeof(struct cmd_pdlist_idx); |
| 3283 | memset(&cmd_param, 0, 24); |
| 3284 | struct cmd_pdlist_idx *p_cmd_param = (struct cmd_pdlist_idx *)(&cmd_param); |
| 3285 | p_cmd_param->start_idx = start_idx_param; |
| 3286 | p_cmd_param->count = CMD_PDLIST_ONCE_NUM; |
| 3287 | memcpy((u32*)&bsg_param.ioctl_r64.cdw10, cmd_param, sizeof(struct cmd_pdlist_idx)); |
| 3288 | |
| 3289 | int fd; |
| 3290 | char line[128]; |
| 3291 | snprintf(line, sizeof(line) - 1, "/dev/bsg/sssraid%d", bus_no); |
| 3292 | if ((fd = ::open(line, O_RDONLY)) < 0) { |
| 3293 | pout("open %s error %d\n", line, fd); |
| 3294 | return (errno); |
| 3295 | } |
| 3296 | |
| 3297 | int r = ioctl(fd, SG_IO, &io_hdr_v4); |
| 3298 | ::close(fd); |
| 3299 | if (r < 0) { |
| 3300 | return (r); |
| 3301 | } |
| 3302 | |
| 3303 | if (statusp != NULL) { |
| 3304 | *statusp = (io_hdr_v4.transport_status << 0x8) | io_hdr_v4.device_status; |
| 3305 | pout("statusp = 0x%x\n", *statusp); |
| 3306 | if (*statusp) { |
| 3307 | pout("controller returns an error - 0x%x", *statusp); |
| 3308 | return (-1); |
| 3309 | } |
| 3310 | } |
| 3311 | return (0); |
| 3312 | } |
| 3313 | |
| 3314 | int |
| 3315 | linux_smart_interface::sssraid_pd_add_list(int bus_no, smart_device_list & devlist) |
| 3316 | { |
| 3317 | unsigned disk_num = 0; |
| 3318 | struct cmd_pdlist_entry pdlist[CMD_PDS_MAX_NUM]; |
| 3319 | while (disk_num < CMD_PDS_MAX_NUM) { |
| 3320 | struct cmd_show_pdlist list; |
| 3321 | memset(&list, 0, sizeof(list)); |
| 3322 | if (sssraid_pdlist_cmd(bus_no, disk_num, &list, sizeof(struct cmd_show_pdlist), NULL) < 0) |
| 3323 | { |
| 3324 | return (-1); |
| 3325 | } |
| 3326 | if (list.num == 0) |
| 3327 | break; |
| 3328 | memcpy(&pdlist[disk_num], list.disks, list.num * sizeof(struct cmd_pdlist_entry)); |
| 3329 | disk_num += list.num; |
| 3330 | if (list.num < CMD_PDLIST_ONCE_NUM) |
| 3331 | break; |
| 3332 | } |
| 3333 | |
| 3334 | // adding all SCSI devices |
| 3335 | for (unsigned i = 0; i < disk_num; i++) { |
| 3336 | if(!(pdlist[i].interface == ADM_DEVICE_TYPE_SATA || pdlist[i].interface == ADM_DEVICE_TYPE_SAS |
| 3337 | || pdlist[i].interface == ADM_DEVICE_TYPE_NVME)) |
| 3338 | continue; /* non disk device found */ |
| 3339 | char line[128]; |
| 3340 | snprintf(line, sizeof(line) - 1, "/dev/bsg/sssraid%d", bus_no); |
| 3341 | smart_device * dev = new linux_sssraid_device(this, line, (unsigned int)pdlist[i].enc_id, (unsigned int)pdlist[i].slot_id); |
| 3342 | devlist.push_back(dev); |
| 3343 | } |
| 3344 | return (0); |
| 3345 | } |
| 3346 | |