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