xref: /illumos-gate/usr/src/cmd/ndmpd/tlm/tlm_init.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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