1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * BSD 3 Clause License 8 * 9 * Copyright (c) 2007, The Storage Networking Industry Association. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * - Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * - Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in 19 * the documentation and/or other materials provided with the 20 * distribution. 21 * 22 * - Neither the name of The Storage Networking Industry Association (SNIA) 23 * nor the names of its contributors may be used to endorse or promote 24 * products derived from this software without specific prior written 25 * permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 #include <sys/errno.h> 40 #include <sys/types.h> 41 #include <stdlib.h> 42 #include <unistd.h> 43 #include <sys/scsi/impl/uscsi.h> 44 #include <sys/scsi/scsi.h> 45 #include <tlm.h> 46 #include <pthread.h> 47 #include "tlm_proto.h" 48 49 /* 50 * generic routine to read a SCSI page 51 */ 52 int 53 read_scsi_page(scsi_link_t *slink, union scsi_cdb *cdb, 54 int command_size, caddr_t data, int size) 55 { 56 struct uscsi_cmd uscsi_cmd; 57 char *dname; 58 int dev; 59 60 if (slink == 0 || slink->sl_sa == 0) 61 return (EINVAL); 62 63 (void) memset(&uscsi_cmd, 0, sizeof (uscsi_cmd)); 64 65 /* Lun is in the 5th bit */ 66 cdb->scc_lun = slink->sl_lun; 67 uscsi_cmd.uscsi_flags |= USCSI_READ | USCSI_ISOLATE; 68 uscsi_cmd.uscsi_bufaddr = data; 69 uscsi_cmd.uscsi_buflen = size; 70 uscsi_cmd.uscsi_timeout = 1000; 71 uscsi_cmd.uscsi_cdb = (char *)cdb; 72 73 if (cdb->scc_cmd == SCMD_READ_ELEMENT_STATUS) { 74 uscsi_cmd.uscsi_flags |= USCSI_RQENABLE; 75 uscsi_cmd.uscsi_rqbuf = data; 76 uscsi_cmd.uscsi_rqlen = size; 77 } 78 uscsi_cmd.uscsi_cdblen = command_size; 79 80 dname = sasd_slink_name(slink); 81 dev = open(dname, O_RDWR | O_NDELAY); 82 if (dev == -1) { 83 NDMP_LOG(LOG_DEBUG, "Open failed for %s err=%d", 84 dname, errno); 85 return (errno); 86 } 87 if (tlm_ioctl(dev, USCSICMD, &uscsi_cmd) < 0) { 88 NDMP_LOG(LOG_DEBUG, "SCSI cmd %d failed for %s err=%d", 89 cdb->scc_cmd, dname, errno); 90 (void) close(dev); 91 return (errno); 92 } 93 (void) close(dev); 94 return (uscsi_cmd.uscsi_status); 95 } 96 97 /* 98 * Read the Inquiry Page. 99 */ 100 static int 101 read_inquiry_page(scsi_link_t *slink, struct scsi_inquiry *inq) 102 { 103 union scsi_cdb cdb; 104 105 (void) memset(&cdb, 0, sizeof (union scsi_cdb)); 106 cdb.scc_cmd = SCMD_INQUIRY; 107 cdb.g0_count0 = sizeof (struct scsi_inquiry); 108 109 return (read_scsi_page(slink, &cdb, CDB_GROUP0, 110 (caddr_t)inq, sizeof (*inq)) ? -1 : 0); 111 } 112 113 /* 114 * Read the Product Data Page. 115 */ 116 static int 117 read_data_page(scsi_link_t *slink, int pcode, char *snum, int size) 118 { 119 char cmd[CDB_GROUP0]; 120 121 (void) memset(cmd, 0, sizeof (cmd)); 122 123 cmd[0] = SCMD_INQUIRY; 124 cmd[1] = pcode ? 0x01 : 0x00; 125 cmd[2] = pcode; 126 cmd[4] = size; 127 128 /* LINTED improper alignment */ 129 return (read_scsi_page(slink, (union scsi_cdb *)&cmd, CDB_GROUP0, 130 (caddr_t)snum, size) == -1 ? -1 : 0); 131 } 132 133 134 /* 135 * Read the Serial Number Page. 136 */ 137 static int 138 read_serial_num_page(scsi_link_t *slink, char *snum, int size) 139 { 140 scsi_serial_t serial; 141 int rv; 142 143 (void) memset(&serial, 0, sizeof (scsi_serial_t)); 144 rv = read_data_page(slink, SCSI_SERIAL_PAGE, (caddr_t)&serial, 145 sizeof (scsi_serial_t)); 146 (void) strlcpy(snum, serial.sr_num, size); 147 148 return (rv == -1 ? -1 : 0); 149 } 150 151 152 /* 153 * Read the Device Name Page. 154 */ 155 static int 156 read_dev_name_page(scsi_link_t *slink, device_name_page_t *devp) 157 { 158 (void) memset(devp, 0, sizeof (device_name_page_t)); 159 160 if (read_data_page(slink, SCSI_DEVICE_IDENT_PAGE, (caddr_t)devp, 161 sizeof (device_name_page_t)) == -1) 162 return (-1); 163 164 if (devp->np_header.di_page_code == SCSI_DEVICE_IDENT_PAGE && 165 devp->np_node.ni_code_set == 1 && 166 devp->np_node.ni_ident_type == 3 && 167 devp->np_node.ni_ident_length == 8) 168 return (0); 169 170 if (devp->np_header.di_page_code == SCSI_DEVICE_IDENT_PAGE) 171 return (0); 172 173 return (-1); 174 } 175 176 /* 177 * Formatted print of WWN 178 */ 179 char * 180 snprintf_wwn(char *buf, int size, uint8_t *wwn) 181 { 182 if (wwn == NULL || buf == NULL) 183 return (0); 184 185 (void) snprintf(buf, size, "0x%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", 186 wwn[0], wwn[1], wwn[2], wwn[3], wwn[4], wwn[5], wwn[6], wwn[7]); 187 return (buf); 188 } 189 190 191 /* 192 * Extract and print the world wide name (WWN) 193 */ 194 int 195 read_device_wwn(scsi_link_t *slink, char *wwnp, int wsize) 196 { 197 device_name_page_t dinfo; 198 199 (void) memset(wwnp, 0, wsize); 200 if (read_dev_name_page(slink, &dinfo) == -1) 201 return (-1); 202 203 if (dinfo.np_port.ni_code_set == 1 && 204 dinfo.np_port.ni_ident_type == 3) { 205 (void) snprintf_wwn(wwnp, wsize, dinfo.np_port_info.d_name); 206 return (0); 207 } 208 if (dinfo.np_node.ni_code_set == 1 && 209 dinfo.np_node.ni_ident_type == 3) { 210 (void) snprintf_wwn(wwnp, wsize, dinfo.np_node_info.d_name); 211 return (0); 212 } 213 if (dinfo.np_port.ni_code_set == 2 && 214 dinfo.np_port.ni_ident_type == 1) { 215 (void) snprintf(wwnp, wsize, "%.*s", 216 dinfo.np_port.ni_ident_length, dinfo.np_port_info.d_name); 217 return (0); 218 } 219 if (dinfo.np_node.ni_code_set == 2 && 220 dinfo.np_node.ni_ident_type == 1) { 221 (void) snprintf(wwnp, wsize, "%.*s", 222 dinfo.np_node.ni_ident_length, dinfo.np_node_info.d_name); 223 return (0); 224 } 225 return (-1); 226 } 227 228 /* 229 * Add the tape library call back function (used while scanning the bus) 230 */ 231 static int 232 add_lib(scsi_link_t *slink, struct scsi_inquiry *sd, void *arg) 233 { 234 int l; 235 int *nlp; /* pointer to library counter */ 236 sasd_drive_t *ssd; 237 238 if (!slink || !sd) { 239 NDMP_LOG(LOG_DEBUG, "Invalid argument %x %x %x", 240 slink, sd, arg); 241 return (-TLM_INVALID); 242 } 243 244 if (sd->inq_dtype == DTYPE_CHANGER) { 245 /* This is a robot, which means this is also a library */ 246 nlp = (int *)arg; 247 (*nlp)++; 248 l = tlm_insert_new_library(slink); 249 tlm_enable_barcode(l); 250 251 NDMP_LOG(LOG_DEBUG, "lib %d sid %d lun %d", 252 l, slink->sl_sid, slink->sl_lun); 253 254 if ((ssd = sasd_slink_drive(slink)) != NULL) { 255 (void) strlcpy(ssd->sd_vendor, sd->inq_vid, 256 sizeof (ssd->sd_vendor)); 257 (void) strlcpy(ssd->sd_id, sd->inq_pid, 258 sizeof (ssd->sd_id)); 259 (void) strlcpy(ssd->sd_rev, sd->inq_revision, 260 sizeof (ssd->sd_rev)); 261 (void) read_serial_num_page(slink, ssd->sd_serial, 262 sizeof (ssd->sd_serial)); 263 (void) read_device_wwn(slink, ssd->sd_wwn, 264 sizeof (ssd->sd_wwn)); 265 } 266 } 267 268 return (TLM_NO_ERRORS); 269 } 270 271 /* 272 * Create some virutal slots 273 */ 274 static int 275 make_virtual_slot(int l, tlm_drive_t *dp) 276 { 277 int s; 278 tlm_slot_t *sp; 279 280 if (l <= 0 || !dp) { 281 NDMP_LOG(LOG_DEBUG, "Invalid argument %d, %x", l, dp); 282 return (-TLM_INVALID); 283 } 284 285 if ((s = tlm_insert_new_slot(l)) <= 0) 286 return (-TLM_NO_MEMORY); 287 288 if (!(sp = tlm_slot(l, s))) { 289 NDMP_LOG(LOG_DEBUG, "Internal error: slot not found %d", s); 290 return (-TLM_ERROR_INTERNAL); 291 } 292 /* 293 * For virtual slots element number is 0 and they are always full. 294 */ 295 sp->ts_element = 0; 296 sp->ts_status_full = TRUE; 297 return (TLM_NO_ERRORS); 298 } 299 300 /* 301 * Make the tape drive not part of a tape library (stand alone) 302 */ 303 static int 304 make_stand_alone_drive(scsi_link_t *slink, int l) 305 { 306 int d; 307 tlm_drive_t *dp; 308 309 if (!slink || l <= 0) { 310 NDMP_LOG(LOG_DEBUG, "Invalid argument %x %d", slink, l); 311 return (-TLM_INVALID); 312 } 313 314 d = tlm_insert_new_drive(l); 315 if (!(dp = tlm_drive(l, d))) { 316 NDMP_LOG(LOG_DEBUG, "Internal error: drive not found %d", d); 317 return (-TLM_ERROR_INTERNAL); 318 } 319 320 /* For stand-alone drives, the element number is the drive number. */ 321 dp->td_element = d; 322 dp->td_slink = slink; 323 dp->td_scsi_id = slink->sl_sid; 324 dp->td_lun = slink->sl_lun; 325 dp->td_exists = TRUE; 326 327 /* 328 * Note: There is no way to remove library elements. We cannot clean 329 * up if make_virtual_slot() fails. 330 */ 331 (void) make_virtual_slot(l, dp); 332 return (d); 333 } 334 335 /* 336 * Find the LIBRARY structure that has control of this DRIVE. 337 */ 338 static int 339 new_drive(scsi_link_t *slink, int *lib) 340 { 341 int d; 342 tlm_drive_t *dp; 343 tlm_library_t *lp; 344 345 /* Walk through all libraries. */ 346 for (*lib = 1; *lib <= tlm_library_count(); (*lib)++) { 347 if (!(lp = tlm_library(*lib))) 348 continue; 349 /* Walk through drives that are already found. */ 350 for (d = 1; d <= lp->tl_drive_count; d++) { 351 if (!(dp = tlm_drive(*lib, d))) 352 continue; 353 if (dp->td_scsi_id == slink->sl_sid && 354 dp->td_lun == slink->sl_lun) 355 return (d); 356 } 357 } 358 359 /* Not part of any library, this is a newly found tape drive. */ 360 return (0); 361 } 362 363 364 /* 365 * Add the tape library call back function (used while scanning the bus) 366 */ 367 static int 368 add_drv(scsi_link_t *slink, struct scsi_inquiry *sd, void *arg) 369 { 370 int l, d; 371 int *vlp; /* pointer to virtual library number */ 372 sasd_drive_t *ssd; 373 tlm_library_t *library; 374 tlm_drive_t *drive; 375 376 if (!slink || !sd) { 377 NDMP_LOG(LOG_DEBUG, "Invalid argument %x %x %x", 378 slink, sd, arg); 379 return (-TLM_INVALID); 380 } 381 382 if (sd->inq_dtype == DTYPE_SEQUENTIAL) { 383 vlp = (int *)arg; 384 d = new_drive(slink, &l); 385 if (d == 0) { 386 /* This tape drive was not found inside any robot. */ 387 if (*vlp == 0) { 388 /* 389 * First, create a virtual library if it's not 390 * done yet. 391 */ 392 *vlp = tlm_insert_new_library(slink); 393 if ((library = tlm_library(*vlp)) != NULL) 394 library->tl_capability_robot = FALSE; 395 } 396 if ((d = make_stand_alone_drive(slink, *vlp)) < 0) { 397 /* sorry, we can not clean up the vlib now * */ 398 return (-TLM_INVALID); 399 } 400 l = *vlp; 401 NDMP_LOG(LOG_DEBUG, "vlib(%d, %d) sid %d lun %d", 402 l, d, slink->sl_sid, slink->sl_lun); 403 } else 404 NDMP_LOG(LOG_DEBUG, "(%d, %d) sid %d lun %d", 405 l, d, slink->sl_sid, slink->sl_lun); 406 407 if ((drive = tlm_drive(l, d)) != NULL) { 408 drive->td_exists = TRUE; 409 drive->td_slink = slink; 410 } 411 if ((ssd = sasd_slink_drive(slink)) != NULL) { 412 (void) strlcpy(ssd->sd_vendor, 413 sd->inq_vid, sizeof (ssd->sd_vendor)); 414 (void) strlcpy(ssd->sd_id, sd->inq_pid, 415 sizeof (ssd->sd_id)); 416 (void) strlcpy(ssd->sd_rev, sd->inq_revision, 417 sizeof (ssd->sd_rev)); 418 (void) read_serial_num_page(slink, ssd->sd_serial, 419 sizeof (ssd->sd_serial)); 420 (void) read_device_wwn(slink, ssd->sd_wwn, 421 sizeof (ssd->sd_wwn)); 422 } 423 } 424 425 return (TLM_NO_ERRORS); 426 } 427 428 /* 429 * Scan the specified bus and call the handler function. 430 */ 431 static int 432 scan_bus(scsi_adapter_t *sa, int(*hndlr)(), void *args) 433 { 434 int nerr; 435 scsi_link_t *slink; 436 struct scsi_inquiry scsi_data; 437 438 nerr = 0; 439 slink = sa->sa_link_head.sl_next; 440 for (; slink != &sa->sa_link_head; slink = slink->sl_next) { 441 (void) memset(&scsi_data, 0, sizeof (struct scsi_inquiry)); 442 if (read_inquiry_page(slink, &scsi_data) == -1) 443 nerr++; 444 else 445 if ((*hndlr)(slink, &scsi_data, args) != TLM_NO_ERRORS) 446 nerr++; 447 } 448 449 return (nerr); 450 } 451 452 /* 453 * Marks the library/slots inaccessible if there are not enough drives 454 * available on the library 455 */ 456 static void 457 inaccbl_drv_warn(int start, int max) 458 { 459 char *dname; 460 int l, d; 461 tlm_library_t *lp; 462 463 for (l = start; l < max; l++) { 464 if (!(lp = tlm_library(l))) 465 continue; 466 if (lp->tl_drive_count <= 0) 467 continue; 468 469 NDMP_LOG(LOG_DEBUG, 470 "Warning: The following drives are not accessible:"); 471 for (d = 1; d <= lp->tl_drive_count; d++) 472 if (!(dname = tlm_get_tape_name(l, d))) { 473 NDMP_LOG(LOG_DEBUG, 474 "Error getting drive(%d, %d)", l, d); 475 } else 476 NDMP_LOG(LOG_DEBUG, "%s", dname); 477 478 /* 479 * Note: Make the slots inaccessible to prevent running 480 * discovery on these libraries. The better idea is 481 * removing these libraries, but we don't have that 482 * feature available now. 483 */ 484 lp->tl_slot_count = 0; 485 } 486 } 487 488 /* 489 * Initialize the tape library data structure, asks the libraries what 490 * equipments they have. 491 */ 492 int 493 tlm_init(void) 494 { 495 static int nlibs; /* number of found libraries */ 496 int i, nsa; 497 int l, vlibs, d; 498 int rv; 499 scsi_adapter_t *sa; 500 tlm_library_t *lp; 501 tlm_drive_t *dp; 502 503 /* Search through all SCSI adapters, look for tape robots. */ 504 nlibs = 0; 505 506 /* 507 * We probe both changers and tape drives here 508 * but later on this needs to be removed as the 509 * probe will happen somewhere else. 510 */ 511 (void) probe_scsi(); 512 513 nsa = scsi_get_adapter_count(); 514 for (i = 0; i < nsa; i++) 515 if ((sa = scsi_get_adapter(i))) 516 (void) scan_bus(sa, add_lib, (void *)&nlibs); 517 518 NDMP_LOG(LOG_DEBUG, "nlibs %d", nlibs); 519 520 /* Search through all SCSI adapters, look for tape drives. */ 521 vlibs = 0; 522 for (i = 0; i < nsa; i++) 523 if ((sa = scsi_get_adapter(i))) 524 (void) scan_bus(sa, add_drv, (void *)&vlibs); 525 526 NDMP_LOG(LOG_DEBUG, "vlibs %d", vlibs); 527 528 if (nlibs > 0 && vlibs > 0) 529 inaccbl_drv_warn(nlibs + 1, vlibs + nlibs + 1); 530 531 for (l = 1; l <= tlm_library_count(); l++) { 532 if (!(lp = tlm_library(l))) { 533 NDMP_LOG(LOG_DEBUG, "can't find lib %d", l); 534 continue; 535 } 536 537 /* 538 * Make sure all libraries have tape drives. 539 */ 540 if (lp->tl_drive_count == 0) 541 continue; 542 543 /* 544 * Make sure all tape drives exist. A drive that is not 545 * linked into the SCSI chain will be seen by the library 546 * but we cannot talk to it. 547 */ 548 for (d = 1; d <= lp->tl_drive_count; d++) { 549 dp = tlm_drive(l, d); 550 if (dp && !dp->td_exists) { 551 NDMP_LOG(LOG_DEBUG, "Ghost drive found %d.%d", 552 l, d); 553 lp->tl_ghost_drives = TRUE; 554 continue; 555 } 556 } 557 } 558 559 if (nlibs > 0) 560 rv = (vlibs > 0) ? 0 : nlibs; 561 else 562 rv = vlibs; 563 564 return (rv); 565 } 566