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