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