xref: /illumos-gate/usr/src/lib/scsi/libses/common/ses_snap.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <scsi/libses.h>
30 #include "ses_impl.h"
31 
32 ses_snap_page_t *
33 ses_snap_find_page(ses_snap_t *sp, ses2_diag_page_t page, boolean_t ctl)
34 {
35 	ses_snap_page_t *pp;
36 
37 	for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next)
38 		if (pp->ssp_num == page && pp->ssp_control == ctl &&
39 		    (pp->ssp_len > 0 || pp->ssp_control))
40 			return (pp);
41 
42 	return (NULL);
43 }
44 
45 static int
46 grow_snap_page(ses_snap_page_t *pp, size_t min)
47 {
48 	uint8_t *newbuf;
49 
50 	if (min == 0 || min < pp->ssp_alloc)
51 		min = pp->ssp_alloc * 2;
52 
53 	if ((newbuf = ses_realloc(pp->ssp_page, min)) == NULL)
54 		return (-1);
55 
56 	pp->ssp_page = newbuf;
57 	pp->ssp_alloc = min;
58 
59 	bzero(newbuf + pp->ssp_len, pp->ssp_alloc - pp->ssp_len);
60 
61 	return (0);
62 }
63 
64 static ses_snap_page_t *
65 alloc_snap_page(void)
66 {
67 	ses_snap_page_t *pp;
68 
69 	if ((pp = ses_zalloc(sizeof (ses_snap_page_t))) == NULL)
70 		return (NULL);
71 
72 	if ((pp->ssp_page = ses_zalloc(SES2_MIN_DIAGPAGE_ALLOC)) == NULL) {
73 		ses_free(pp);
74 		return (NULL);
75 	}
76 
77 	pp->ssp_num = -1;
78 	pp->ssp_alloc = SES2_MIN_DIAGPAGE_ALLOC;
79 
80 	return (pp);
81 }
82 
83 static void
84 free_snap_page(ses_snap_page_t *pp)
85 {
86 	if (pp == NULL)
87 		return;
88 
89 	if (pp->ssp_mmap_base)
90 		(void) munmap(pp->ssp_mmap_base, pp->ssp_mmap_len);
91 	else
92 		ses_free(pp->ssp_page);
93 	ses_free(pp);
94 }
95 
96 static void
97 free_all_snap_pages(ses_snap_t *sp)
98 {
99 	ses_snap_page_t *pp, *np;
100 
101 	for (pp = sp->ss_pages; pp != NULL; pp = np) {
102 		np = pp->ssp_next;
103 		free_snap_page(pp);
104 	}
105 
106 	sp->ss_pages = NULL;
107 }
108 
109 /*
110  * Grow (if needed) the control page buffer, fill in the page code, page
111  * length, and generation count, and return a pointer to the page.  The
112  * caller is responsible for filling in the rest of the page data.  If 'unique'
113  * is specified, then a new page instance is created instead of sharing the
114  * current one.
115  */
116 ses_snap_page_t *
117 ses_snap_ctl_page(ses_snap_t *sp, ses2_diag_page_t page, size_t dlen,
118     boolean_t unique)
119 {
120 	ses_target_t *tp = sp->ss_target;
121 	spc3_diag_page_impl_t *pip;
122 	ses_snap_page_t *pp, *up, **loc;
123 	ses_pagedesc_t *dp;
124 	size_t len;
125 
126 	pp = ses_snap_find_page(sp, page, B_TRUE);
127 	if (pp == NULL) {
128 		(void) ses_set_errno(ESES_NOTSUP);
129 		return (NULL);
130 	}
131 
132 	if (pp->ssp_initialized && !unique)
133 		return (pp);
134 
135 	if (unique) {
136 		/*
137 		 * The user has requested a unique instance of the page.  Create
138 		 * a new ses_snap_page_t instance and chain it off the
139 		 * 'ssp_instances' list of the master page.  These must be
140 		 * appended to the end of the chain, as the order of operations
141 		 * may be important (i.e. microcode download).
142 		 */
143 		if ((up = alloc_snap_page()) == NULL)
144 			return (NULL);
145 
146 		up->ssp_num = pp->ssp_num;
147 		up->ssp_control = B_TRUE;
148 
149 		for (loc = &pp->ssp_unique; *loc != NULL;
150 		    loc = &(*loc)->ssp_next)
151 			;
152 
153 		*loc = up;
154 		pp = up;
155 	}
156 
157 	dp = ses_get_pagedesc(tp, page, SES_PAGE_CTL);
158 	ASSERT(dp != NULL);
159 
160 	len = dp->spd_ctl_len(sp->ss_n_elem, page, dlen);
161 	if (pp->ssp_alloc < dlen && grow_snap_page(pp, len) != 0)
162 		return (NULL);
163 	pp->ssp_len = len;
164 	bzero(pp->ssp_page, len);
165 	pp->ssp_initialized = B_TRUE;
166 
167 	pip = (spc3_diag_page_impl_t *)pp->ssp_page;
168 	pip->sdpi_page_code = (uint8_t)page;
169 	SCSI_WRITE16(&pip->sdpi_page_length,
170 	    len - offsetof(spc3_diag_page_impl_t, sdpi_data[0]));
171 	if (dp->spd_gcoff != -1)
172 		SCSI_WRITE32((uint8_t *)pip + dp->spd_gcoff, sp->ss_generation);
173 
174 	return (pp);
175 }
176 
177 static int
178 read_status_page(ses_snap_t *sp, ses2_diag_page_t page)
179 {
180 	libscsi_action_t *ap;
181 	ses_snap_page_t *pp;
182 	ses_target_t *tp;
183 	spc3_diag_page_impl_t *pip;
184 	spc3_receive_diagnostic_results_cdb_t *cp;
185 	uint_t flags;
186 	uint8_t *buf;
187 	size_t alloc;
188 	uint_t retries = 0;
189 	ses2_diag_page_t retpage;
190 
191 	for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next)
192 		if (pp->ssp_num == page && !pp->ssp_control)
193 			break;
194 
195 	/*
196 	 * No matching page.  Since the page number is not under consumer or
197 	 * device control, this must be a bug.
198 	 */
199 	ASSERT(pp != NULL);
200 
201 	tp = sp->ss_target;
202 
203 	flags = LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE |
204 	    LIBSCSI_AF_RQSENSE;
205 
206 again:
207 	ap = libscsi_action_alloc(tp->st_scsi_hdl,
208 	    SPC3_CMD_RECEIVE_DIAGNOSTIC_RESULTS, flags, pp->ssp_page,
209 	    pp->ssp_alloc);
210 
211 	if (ap == NULL)
212 		return (ses_libscsi_error(tp->st_scsi_hdl, "failed to "
213 		    "allocate SCSI action"));
214 
215 	cp = (spc3_receive_diagnostic_results_cdb_t *)
216 	    libscsi_action_get_cdb(ap);
217 
218 	cp->rdrc_page_code = pp->ssp_num;
219 	cp->rdrc_pcv = 1;
220 	SCSI_WRITE16(&cp->rdrc_allocation_length,
221 	    MIN(pp->ssp_alloc, UINT16_MAX));
222 
223 	if (libscsi_exec(ap, tp->st_target) != 0) {
224 		libscsi_action_free(ap);
225 		return (ses_libscsi_error(tp->st_scsi_hdl,
226 		    "receive diagnostic results failed"));
227 	}
228 
229 	if (libscsi_action_get_status(ap) != 0) {
230 		(void) ses_scsi_error(ap,
231 		    "receive diagnostic results failed");
232 		libscsi_action_free(ap);
233 		return (-1);
234 	}
235 
236 	(void) libscsi_action_get_buffer(ap, &buf, &alloc, &pp->ssp_len);
237 	libscsi_action_free(ap);
238 
239 	ASSERT(buf == pp->ssp_page);
240 	ASSERT(alloc == pp->ssp_alloc);
241 
242 	if (pp->ssp_len == pp->ssp_alloc && pp->ssp_alloc < UINT16_MAX) {
243 		bzero(pp->ssp_page, pp->ssp_len);
244 		pp->ssp_len = 0;
245 		if (grow_snap_page(pp, 0) != 0)
246 			return (-1);
247 		goto again;
248 	}
249 
250 	pip = (spc3_diag_page_impl_t *)buf;
251 
252 	if (pip->sdpi_page_code == page)
253 		return (0);
254 
255 	retpage = pip->sdpi_page_code;
256 
257 	bzero(pp->ssp_page, pp->ssp_len);
258 	pp->ssp_len = 0;
259 
260 	if (retpage == SES2_DIAGPAGE_ENCLOSURE_BUSY) {
261 		if (++retries > LIBSES_MAX_BUSY_RETRIES)
262 			return (ses_error(ESES_BUSY, "too many "
263 			    "enclosure busy responses for page 0x%x", page));
264 		goto again;
265 	}
266 
267 	return (ses_error(ESES_BAD_RESPONSE, "target returned page 0x%x "
268 	    "instead of the requested page 0x%x", retpage, page));
269 }
270 
271 static int
272 send_control_page(ses_snap_t *sp, ses_snap_page_t *pp)
273 {
274 	ses_target_t *tp;
275 	libscsi_action_t *ap;
276 	spc3_send_diagnostic_cdb_t *cp;
277 	uint_t flags;
278 
279 	tp = sp->ss_target;
280 
281 	flags = LIBSCSI_AF_WRITE | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE |
282 	    LIBSCSI_AF_RQSENSE;
283 
284 	ap = libscsi_action_alloc(tp->st_scsi_hdl, SPC3_CMD_SEND_DIAGNOSTIC,
285 	    flags, pp->ssp_page, pp->ssp_len);
286 
287 	if (ap == NULL)
288 		return (ses_libscsi_error(tp->st_scsi_hdl, "failed to "
289 		    "allocate SCSI action"));
290 
291 	cp = (spc3_send_diagnostic_cdb_t *)libscsi_action_get_cdb(ap);
292 
293 	cp->sdc_pf = 1;
294 	SCSI_WRITE16(&cp->sdc_parameter_list_length, pp->ssp_len);
295 
296 	if (libscsi_exec(ap, tp->st_target) != 0) {
297 		libscsi_action_free(ap);
298 		return (ses_libscsi_error(tp->st_scsi_hdl,
299 		    "SEND DIAGNOSTIC command failed for page 0x%x",
300 		    pp->ssp_num));
301 	}
302 
303 	if (libscsi_action_get_status(ap) != 0) {
304 		(void) ses_scsi_error(ap, "SEND DIAGNOSTIC command "
305 		    "failed for page 0x%x", pp->ssp_num);
306 		libscsi_action_free(ap);
307 		return (-1);
308 	}
309 
310 	libscsi_action_free(ap);
311 
312 	return (0);
313 }
314 
315 static int
316 pages_skel_create(ses_snap_t *sp)
317 {
318 	ses_snap_page_t *pp, *np;
319 	ses_target_t *tp = sp->ss_target;
320 	ses2_supported_ses_diag_page_impl_t *pip;
321 	ses2_diag_page_t page;
322 	size_t npages;
323 	size_t pagelen;
324 	off_t i;
325 
326 	ASSERT(sp->ss_pages == NULL);
327 
328 	if ((pp = alloc_snap_page()) == NULL)
329 		return (-1);
330 
331 	pp->ssp_num = SES2_DIAGPAGE_SUPPORTED_PAGES;
332 	pp->ssp_control = B_FALSE;
333 	sp->ss_pages = pp;
334 
335 	if (read_status_page(sp, SES2_DIAGPAGE_SUPPORTED_PAGES) != 0) {
336 		free_snap_page(pp);
337 		sp->ss_pages = NULL;
338 		return (-1);
339 	}
340 
341 	pip = pp->ssp_page;
342 	pagelen = pp->ssp_len;
343 
344 	npages = SCSI_READ16(&pip->sssdpi_page_length);
345 
346 	for (i = 0; i < npages; i++) {
347 		if (!SES_WITHIN_PAGE(pip->sssdpi_pages + i, 1, pip,
348 		    pagelen))
349 			break;
350 
351 		page = (ses2_diag_page_t)pip->sssdpi_pages[i];
352 		/*
353 		 * Skip the page we already added during the bootstrap.
354 		 */
355 		if (page == SES2_DIAGPAGE_SUPPORTED_PAGES)
356 			continue;
357 		/*
358 		 * The end of the page list may be padded with zeros; ignore
359 		 * them all.
360 		 */
361 		if (page == 0 && i > 0)
362 			break;
363 		if ((np = alloc_snap_page()) == NULL) {
364 			free_all_snap_pages(sp);
365 			return (-1);
366 		}
367 		np->ssp_num = page;
368 		pp->ssp_next = np;
369 		pp = np;
370 
371 		/*
372 		 * Allocate a control page as well, if we can use it.
373 		 */
374 		if (ses_get_pagedesc(tp, page, SES_PAGE_CTL) != NULL) {
375 			if ((np = alloc_snap_page()) == NULL) {
376 				free_all_snap_pages(sp);
377 				return (-1);
378 			}
379 			np->ssp_num = page;
380 			np->ssp_control = B_TRUE;
381 			pp->ssp_next = np;
382 			pp = np;
383 		}
384 	}
385 
386 	return (0);
387 }
388 
389 static void
390 ses_snap_free(ses_snap_t *sp)
391 {
392 	free_all_snap_pages(sp);
393 	ses_node_teardown(sp->ss_root);
394 	ses_free(sp->ss_nodes);
395 	ses_free(sp);
396 }
397 
398 static void
399 ses_snap_rele_unlocked(ses_snap_t *sp)
400 {
401 	ses_target_t *tp = sp->ss_target;
402 
403 	if (--sp->ss_refcnt != 0)
404 		return;
405 
406 	if (sp->ss_next != NULL)
407 		sp->ss_next->ss_prev = sp->ss_prev;
408 
409 	if (sp->ss_prev != NULL)
410 		sp->ss_prev->ss_next = sp->ss_next;
411 	else
412 		tp->st_snapshots = sp->ss_next;
413 
414 	ses_snap_free(sp);
415 }
416 
417 ses_snap_t *
418 ses_snap_hold(ses_target_t *tp)
419 {
420 	ses_snap_t *sp;
421 
422 	(void) pthread_mutex_lock(&tp->st_lock);
423 	sp = tp->st_snapshots;
424 	sp->ss_refcnt++;
425 	(void) pthread_mutex_unlock(&tp->st_lock);
426 
427 	return (sp);
428 }
429 
430 void
431 ses_snap_rele(ses_snap_t *sp)
432 {
433 	ses_target_t *tp = sp->ss_target;
434 
435 	(void) pthread_mutex_lock(&tp->st_lock);
436 	ses_snap_rele_unlocked(sp);
437 	(void) pthread_mutex_unlock(&tp->st_lock);
438 }
439 
440 ses_snap_t *
441 ses_snap_new(ses_target_t *tp)
442 {
443 	ses_snap_t *sp;
444 	ses_snap_page_t *pp;
445 	uint32_t gc;
446 	uint_t retries = 0;
447 	ses_pagedesc_t *dp;
448 	size_t pages, pagesize, pagelen;
449 	char *scratch;
450 
451 	if ((sp = ses_zalloc(sizeof (ses_snap_t))) == NULL)
452 		return (NULL);
453 
454 	sp->ss_target = tp;
455 
456 again:
457 	free_all_snap_pages(sp);
458 
459 	if (pages_skel_create(sp) != 0) {
460 		free(sp);
461 		return (NULL);
462 	}
463 
464 	sp->ss_generation = (uint32_t)-1;
465 	sp->ss_time = gethrtime();
466 
467 	for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
468 		/*
469 		 * We skip all of:
470 		 *
471 		 * - Control pages
472 		 * - Pages we've already filled in
473 		 * - Pages we don't understand (those with no descriptor)
474 		 */
475 		if (pp->ssp_len > 0 || pp->ssp_control)
476 			continue;
477 		if ((dp = ses_get_pagedesc(tp, pp->ssp_num,
478 		    SES_PAGE_DIAG)) == NULL)
479 			continue;
480 
481 		if (read_status_page(sp, pp->ssp_num) != 0)
482 			continue;
483 
484 		/*
485 		 * If the generation code has changed, we don't have a valid
486 		 * snapshot.  Start over.
487 		 */
488 		if (dp->spd_gcoff != -1 &&
489 		    dp->spd_gcoff + 4 <= pp->ssp_len) {
490 			gc = SCSI_READ32((uint8_t *)pp->ssp_page +
491 			    dp->spd_gcoff);
492 			if (sp->ss_generation == (uint32_t)-1) {
493 				sp->ss_generation = gc;
494 			} else if (sp->ss_generation != gc) {
495 				if (++retries > LIBSES_MAX_GC_RETRIES) {
496 					(void) ses_error(ESES_TOOMUCHCHANGE,
497 					    "too many generation count "
498 					    "mismatches: page 0x%x gc %u "
499 					    "previous page %u", dp->spd_gcoff,
500 					    gc, sp->ss_generation);
501 					ses_snap_free((ses_snap_t *)sp);
502 					return (NULL);
503 				}
504 				goto again;
505 			}
506 		}
507 	}
508 
509 	/*
510 	 * The LIBSES_TRUNCATE environment variable is a debugging tool which,
511 	 * if set, randomly truncates all pages (except
512 	 * SES2_DIAGPAGE_SUPPORTED_PAGES).  In order to be truly evil, we
513 	 * mmap() each page with enough space after it so we can move the data
514 	 * up to the end of a page and unmap the following page so that any
515 	 * attempt to read past the end of the page results in a segfault.
516 	 */
517 	if (sp->ss_target->st_truncate) {
518 		pagesize = PAGESIZE;
519 
520 		/*
521 		 * Count the maximum number of pages we will need and allocate
522 		 * the necessary space.
523 		 */
524 		pages = 0;
525 		for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
526 			if (pp->ssp_control || pp->ssp_len == 0)
527 				continue;
528 
529 			pages += (P2ROUNDUP(pp->ssp_len, pagesize) /
530 			    pagesize) + 1;
531 		}
532 
533 		if ((scratch = mmap(NULL, pages * pagesize,
534 		    PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
535 		    -1, 0)) == MAP_FAILED) {
536 			(void) ses_error(ESES_NOMEM,
537 			    "failed to mmap() pages for truncation");
538 			ses_snap_free(sp);
539 			return (NULL);
540 		}
541 
542 		for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
543 			if (pp->ssp_control || pp->ssp_len == 0)
544 				continue;
545 
546 			pages = P2ROUNDUP(pp->ssp_len, pagesize) / pagesize;
547 			pp->ssp_mmap_base = scratch;
548 			pp->ssp_mmap_len = pages * pagesize;
549 
550 			pagelen = lrand48() % pp->ssp_len;
551 			(void) memcpy(pp->ssp_mmap_base + pp->ssp_mmap_len -
552 			    pagelen, pp->ssp_page, pagelen);
553 			ses_free(pp->ssp_page);
554 			pp->ssp_page = pp->ssp_mmap_base + pp->ssp_mmap_len -
555 			    pagelen;
556 			pp->ssp_len = pagelen;
557 
558 			(void) munmap(pp->ssp_mmap_base + pages * pagesize,
559 			    pagesize);
560 			scratch += (pages + 1) * pagesize;
561 		}
562 	}
563 
564 
565 	if (ses_fill_snap(sp) != 0) {
566 		ses_snap_free(sp);
567 		return (NULL);
568 	}
569 
570 	(void) pthread_mutex_lock(&tp->st_lock);
571 	if (tp->st_snapshots != NULL)
572 		ses_snap_rele_unlocked(tp->st_snapshots);
573 	sp->ss_next = tp->st_snapshots;
574 	if (tp->st_snapshots != NULL)
575 		tp->st_snapshots->ss_prev = sp;
576 	tp->st_snapshots = sp;
577 	sp->ss_refcnt = 2;
578 	(void) pthread_mutex_unlock(&tp->st_lock);
579 
580 	return (sp);
581 }
582 
583 int
584 ses_snap_do_ctl(ses_snap_t *sp)
585 {
586 	ses_snap_page_t *pp, *up;
587 	int ret = -1;
588 
589 	for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
590 		if (!pp->ssp_control)
591 			continue;
592 
593 		if (pp->ssp_initialized && send_control_page(sp, pp) != 0)
594 			goto error;
595 
596 		for (up = pp->ssp_unique; up != NULL; up = up->ssp_next) {
597 			if (send_control_page(sp, up) != 0)
598 				goto error;
599 		}
600 	}
601 
602 	ret = 0;
603 error:
604 	for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
605 		if (!pp->ssp_control)
606 			continue;
607 
608 		pp->ssp_initialized = B_FALSE;
609 		while ((up = pp->ssp_unique) != NULL) {
610 			pp->ssp_unique = up->ssp_next;
611 			free_snap_page(up);
612 		}
613 	}
614 
615 
616 	return (ret);
617 }
618 
619 uint32_t
620 ses_snap_generation(ses_snap_t *sp)
621 {
622 	return (sp->ss_generation);
623 }
624 
625 static ses_walk_action_t
626 ses_walk_node(ses_node_t *np, ses_walk_f func, void *arg)
627 {
628 	ses_walk_action_t action;
629 
630 	for (; np != NULL; np = ses_node_sibling(np)) {
631 		action = func(np, arg);
632 		if (action == SES_WALK_ACTION_TERMINATE)
633 			return (SES_WALK_ACTION_TERMINATE);
634 		if (action == SES_WALK_ACTION_PRUNE ||
635 		    ses_node_child(np) == NULL)
636 			continue;
637 		if (ses_walk_node(ses_node_child(np), func, arg) ==
638 		    SES_WALK_ACTION_TERMINATE)
639 			return (SES_WALK_ACTION_TERMINATE);
640 	}
641 
642 	return (SES_WALK_ACTION_CONTINUE);
643 }
644 
645 int
646 ses_walk(ses_snap_t *sp, ses_walk_f func, void *arg)
647 {
648 	(void) ses_walk_node(ses_root_node(sp), func, arg);
649 
650 	return (0);
651 }
652 
653 /*ARGSUSED*/
654 static ses_walk_action_t
655 ses_fill_nodes(ses_node_t *np, void *unused)
656 {
657 	np->sn_snapshot->ss_nodes[np->sn_id] = np;
658 
659 	return (SES_WALK_ACTION_CONTINUE);
660 }
661 
662 /*
663  * Given an ID returned by ses_node_id(), lookup and return the corresponding
664  * node in the snapshot.  If the snapshot generation count has changed, then
665  * return failure.
666  */
667 ses_node_t *
668 ses_node_lookup(ses_snap_t *sp, uint64_t id)
669 {
670 	uint32_t gen = (id >> 32);
671 	uint32_t idx = (id & 0xFFFFFFFF);
672 
673 	if (sp->ss_generation != gen) {
674 		(void) ses_set_errno(ESES_CHANGED);
675 		return (NULL);
676 	}
677 
678 	if (idx >= sp->ss_n_nodes) {
679 		(void) ses_error(ESES_BAD_NODE,
680 		    "no such node in snapshot");
681 		return (NULL);
682 	}
683 
684 	/*
685 	 * If this is our first lookup attempt, construct the array for fast
686 	 * lookups.
687 	 */
688 	if (sp->ss_nodes == NULL) {
689 		if ((sp->ss_nodes = ses_zalloc(
690 		    sp->ss_n_nodes * sizeof (void *))) == NULL)
691 			return (NULL);
692 
693 		(void) ses_walk(sp, ses_fill_nodes, NULL);
694 	}
695 
696 	if (sp->ss_nodes[idx] == NULL)
697 		(void) ses_error(ESES_BAD_NODE,
698 		    "no such node in snapshot");
699 	return (sp->ss_nodes[idx]);
700 }
701