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 * Add the tape library call back function (used while scanning the bus) 115 */ 116 static int 117 add_lib(scsi_link_t *slink, struct scsi_inquiry *sd, void *arg) 118 { 119 int l; 120 int *nlp; /* pointer to library counter */ 121 sasd_drive_t *ssd; 122 123 if (!slink || !sd) { 124 NDMP_LOG(LOG_DEBUG, "Invalid argument %x %x %x", 125 slink, sd, arg); 126 return (-TLM_INVALID); 127 } 128 129 if (sd->inq_dtype == DTYPE_CHANGER) { 130 /* This is a robot, which means this is also a library */ 131 nlp = (int *)arg; 132 (*nlp)++; 133 l = tlm_insert_new_library(slink); 134 tlm_enable_barcode(l); 135 136 NDMP_LOG(LOG_DEBUG, "lib %d sid %d lun %d", 137 l, slink->sl_sid, slink->sl_lun); 138 139 if ((ssd = sasd_slink_drive(slink)) != NULL) { 140 (void) strlcpy(ssd->sd_vendor, sd->inq_vid, 141 sizeof (ssd->sd_vendor)); 142 (void) strlcpy(ssd->sd_id, sd->inq_pid, 143 sizeof (ssd->sd_id)); 144 (void) strlcpy(ssd->sd_rev, sd->inq_revision, 145 sizeof (ssd->sd_rev)); 146 } 147 } 148 149 return (TLM_NO_ERRORS); 150 } 151 152 /* 153 * Create some virutal slots 154 */ 155 static int 156 make_virtual_slot(int l, tlm_drive_t *dp) 157 { 158 int s; 159 tlm_slot_t *sp; 160 161 if (l <= 0 || !dp) { 162 NDMP_LOG(LOG_DEBUG, "Invalid argument %d, %x", l, dp); 163 return (-TLM_INVALID); 164 } 165 166 if ((s = tlm_insert_new_slot(l)) <= 0) 167 return (-TLM_NO_MEMORY); 168 169 if (!(sp = tlm_slot(l, s))) { 170 NDMP_LOG(LOG_DEBUG, "Internal error: slot not found %d", s); 171 return (-TLM_ERROR_INTERNAL); 172 } 173 /* 174 * For virtual slots element number is 0 and they are always full. 175 */ 176 sp->ts_element = 0; 177 sp->ts_status_full = TRUE; 178 return (TLM_NO_ERRORS); 179 } 180 181 /* 182 * Make the tape drive not part of a tape library (stand alone) 183 */ 184 static int 185 make_stand_alone_drive(scsi_link_t *slink, int l) 186 { 187 int d; 188 tlm_drive_t *dp; 189 190 if (!slink || l <= 0) { 191 NDMP_LOG(LOG_DEBUG, "Invalid argument %x %d", slink, l); 192 return (-TLM_INVALID); 193 } 194 195 d = tlm_insert_new_drive(l); 196 if (!(dp = tlm_drive(l, d))) { 197 NDMP_LOG(LOG_DEBUG, "Internal error: drive not found %d", d); 198 return (-TLM_ERROR_INTERNAL); 199 } 200 201 /* For stand-alone drives, the element number is the drive number. */ 202 dp->td_element = d; 203 dp->td_slink = slink; 204 dp->td_scsi_id = slink->sl_sid; 205 dp->td_lun = slink->sl_lun; 206 dp->td_exists = TRUE; 207 208 /* 209 * Note: There is no way to remove library elements. We cannot clean 210 * up if make_virtual_slot() fails. 211 */ 212 (void) make_virtual_slot(l, dp); 213 return (d); 214 } 215 216 /* 217 * Find the LIBRARY structure that has control of this DRIVE. 218 */ 219 static int 220 new_drive(scsi_link_t *slink, int *lib) 221 { 222 int d; 223 tlm_drive_t *dp; 224 tlm_library_t *lp; 225 226 /* Walk through all libraries. */ 227 for (*lib = 1; *lib <= tlm_library_count(); (*lib)++) { 228 if (!(lp = tlm_library(*lib))) 229 continue; 230 /* Walk through drives that are already found. */ 231 for (d = 1; d <= lp->tl_drive_count; d++) { 232 if (!(dp = tlm_drive(*lib, d))) 233 continue; 234 if (dp->td_scsi_id == slink->sl_sid && 235 dp->td_lun == slink->sl_lun) 236 return (d); 237 } 238 } 239 240 /* Not part of any library, this is a newly found tape drive. */ 241 return (0); 242 } 243 244 /* 245 * Add the tape library call back function (used while scanning the bus) 246 */ 247 static int 248 add_drv(scsi_link_t *slink, struct scsi_inquiry *sd, void *arg) 249 { 250 int l, d; 251 int *vlp; /* pointer to virtual library number */ 252 sasd_drive_t *ssd; 253 tlm_library_t *library; 254 tlm_drive_t *drive; 255 256 if (!slink || !sd) { 257 NDMP_LOG(LOG_DEBUG, "Invalid argument %x %x %x", 258 slink, sd, arg); 259 return (-TLM_INVALID); 260 } 261 262 if (sd->inq_dtype == DTYPE_SEQUENTIAL) { 263 vlp = (int *)arg; 264 d = new_drive(slink, &l); 265 if (d == 0) { 266 /* This tape drive was not found inside any robot. */ 267 if (*vlp == 0) { 268 /* 269 * First, create a virtual library if it's not 270 * done yet. 271 */ 272 *vlp = tlm_insert_new_library(slink); 273 if ((library = tlm_library(*vlp)) != NULL) 274 library->tl_capability_robot = FALSE; 275 } 276 if ((d = make_stand_alone_drive(slink, *vlp)) < 0) { 277 /* sorry, we can not clean up the vlib now * */ 278 return (-TLM_INVALID); 279 } 280 l = *vlp; 281 NDMP_LOG(LOG_DEBUG, "vlib(%d, %d) sid %d lun %d", 282 l, d, slink->sl_sid, slink->sl_lun); 283 } else 284 NDMP_LOG(LOG_DEBUG, "(%d, %d) sid %d lun %d", 285 l, d, slink->sl_sid, slink->sl_lun); 286 287 if ((drive = tlm_drive(l, d)) != NULL) { 288 drive->td_exists = TRUE; 289 drive->td_slink = slink; 290 } 291 if ((ssd = sasd_slink_drive(slink)) != NULL) { 292 (void) strlcpy(ssd->sd_vendor, 293 sd->inq_vid, sizeof (ssd->sd_vendor)); 294 (void) strlcpy(ssd->sd_id, sd->inq_pid, 295 sizeof (ssd->sd_id)); 296 (void) strlcpy(ssd->sd_rev, sd->inq_revision, 297 sizeof (ssd->sd_rev)); 298 } 299 } 300 301 return (TLM_NO_ERRORS); 302 } 303 304 /* 305 * Scan the specified bus and call the handler function. 306 */ 307 static int 308 scan_bus(scsi_adapter_t *sa, int(*hndlr)(), void *args) 309 { 310 int nerr; 311 scsi_link_t *slink; 312 struct scsi_inquiry scsi_data; 313 314 nerr = 0; 315 slink = sa->sa_link_head.sl_next; 316 for (; slink != &sa->sa_link_head; slink = slink->sl_next) { 317 (void) memset(&scsi_data, 0, sizeof (struct scsi_inquiry)); 318 if (read_inquiry_page(slink, &scsi_data) == -1) 319 nerr++; 320 else 321 if ((*hndlr)(slink, &scsi_data, args) != TLM_NO_ERRORS) 322 nerr++; 323 } 324 325 return (nerr); 326 } 327 328 /* 329 * Marks the library/slots inaccessible if there are not enough drives 330 * available on the library 331 */ 332 static void 333 inaccbl_drv_warn(int start, int max) 334 { 335 char *dname; 336 int l, d; 337 tlm_library_t *lp; 338 339 for (l = start; l < max; l++) { 340 if (!(lp = tlm_library(l))) 341 continue; 342 if (lp->tl_drive_count <= 0) 343 continue; 344 345 NDMP_LOG(LOG_DEBUG, 346 "Warning: The following drives are not accessible:"); 347 for (d = 1; d <= lp->tl_drive_count; d++) 348 if (!(dname = tlm_get_tape_name(l, d))) { 349 NDMP_LOG(LOG_DEBUG, 350 "Error getting drive(%d, %d)", l, d); 351 } else 352 NDMP_LOG(LOG_DEBUG, "%s", dname); 353 354 /* 355 * Note: Make the slots inaccessible to prevent running 356 * discovery on these libraries. The better idea is 357 * removing these libraries, but we don't have that 358 * feature available now. 359 */ 360 lp->tl_slot_count = 0; 361 } 362 } 363 364 /* 365 * Initialize the tape library data structure, asks the libraries what 366 * equipments they have. 367 */ 368 int 369 tlm_init(void) 370 { 371 static int nlibs; /* number of found libraries */ 372 int i, nsa; 373 int l, vlibs, d; 374 int rv; 375 scsi_adapter_t *sa; 376 tlm_library_t *lp; 377 tlm_drive_t *dp; 378 379 /* Search through all SCSI adapters, look for tape robots. */ 380 nlibs = 0; 381 382 /* 383 * We probe both changers and tape drives here 384 * but later on this needs to be removed as the 385 * probe will happen somewhere else. 386 */ 387 (void) probe_scsi(); 388 389 nsa = scsi_get_adapter_count(); 390 for (i = 0; i < nsa; i++) 391 if ((sa = scsi_get_adapter(i))) 392 (void) scan_bus(sa, add_lib, (void *)&nlibs); 393 394 NDMP_LOG(LOG_DEBUG, "nlibs %d", nlibs); 395 396 /* Search through all SCSI adapters, look for tape drives. */ 397 vlibs = 0; 398 for (i = 0; i < nsa; i++) 399 if ((sa = scsi_get_adapter(i))) 400 (void) scan_bus(sa, add_drv, (void *)&vlibs); 401 402 NDMP_LOG(LOG_DEBUG, "vlibs %d", vlibs); 403 404 if (nlibs > 0 && vlibs > 0) 405 inaccbl_drv_warn(nlibs + 1, vlibs + nlibs + 1); 406 407 for (l = 1; l <= tlm_library_count(); l++) { 408 if (!(lp = tlm_library(l))) { 409 NDMP_LOG(LOG_DEBUG, "can't find lib %d", l); 410 continue; 411 } 412 413 /* 414 * Make sure all libraries have tape drives. 415 */ 416 if (lp->tl_drive_count == 0) 417 continue; 418 419 /* 420 * Make sure all tape drives exist. A drive that is not 421 * linked into the SCSI chain will be seen by the library 422 * but we cannot talk to it. 423 */ 424 for (d = 1; d <= lp->tl_drive_count; d++) { 425 dp = tlm_drive(l, d); 426 if (dp && !dp->td_exists) { 427 NDMP_LOG(LOG_DEBUG, "Ghost drive found %d.%d", 428 l, d); 429 lp->tl_ghost_drives = TRUE; 430 continue; 431 } 432 } 433 } 434 435 if (nlibs > 0) 436 rv = (vlibs > 0) ? 0 : nlibs; 437 else 438 rv = vlibs; 439 440 return (rv); 441 } 442