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.
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.
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]
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <scsi/libses.h>
30 ses_snap_find_page(ses_snap_t
*sp
, ses2_diag_page_t page
, boolean_t ctl
)
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
))
43 grow_snap_page(ses_snap_page_t
*pp
, size_t min
)
47 if (min
== 0 || min
< pp
->ssp_alloc
)
48 min
= pp
->ssp_alloc
* 2;
50 if ((newbuf
= ses_realloc(pp
->ssp_page
, min
)) == NULL
)
53 pp
->ssp_page
= newbuf
;
56 bzero(newbuf
+ pp
->ssp_len
, pp
->ssp_alloc
- pp
->ssp_len
);
61 static ses_snap_page_t
*
66 if ((pp
= ses_zalloc(sizeof (ses_snap_page_t
))) == NULL
)
69 if ((pp
->ssp_page
= ses_zalloc(SES2_MIN_DIAGPAGE_ALLOC
)) == NULL
) {
75 pp
->ssp_alloc
= SES2_MIN_DIAGPAGE_ALLOC
;
81 free_snap_page(ses_snap_page_t
*pp
)
86 if (pp
->ssp_mmap_base
)
87 (void) munmap(pp
->ssp_mmap_base
, pp
->ssp_mmap_len
);
89 ses_free(pp
->ssp_page
);
94 free_all_snap_pages(ses_snap_t
*sp
)
96 ses_snap_page_t
*pp
, *np
;
98 for (pp
= sp
->ss_pages
; pp
!= NULL
; pp
= np
) {
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
114 ses_snap_ctl_page(ses_snap_t
*sp
, ses2_diag_page_t page
, size_t dlen
,
117 ses_target_t
*tp
= sp
->ss_target
;
118 spc3_diag_page_impl_t
*pip
;
119 ses_snap_page_t
*pp
, *up
, **loc
;
123 pp
= ses_snap_find_page(sp
, page
, B_TRUE
);
125 (void) ses_set_errno(ESES_NOTSUP
);
129 if (pp
->ssp_initialized
&& !unique
)
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).
140 if ((up
= alloc_snap_page()) == NULL
)
143 up
->ssp_num
= pp
->ssp_num
;
144 up
->ssp_control
= B_TRUE
;
146 for (loc
= &pp
->ssp_unique
; *loc
!= NULL
;
147 loc
= &(*loc
)->ssp_next
)
154 dp
= ses_get_pagedesc(tp
, page
, SES_PAGE_CTL
);
157 len
= dp
->spd_ctl_len(sp
->ss_n_elem
, page
, dlen
);
158 if (pp
->ssp_alloc
< len
&& grow_snap_page(pp
, len
) != 0)
161 bzero(pp
->ssp_page
, len
);
162 pp
->ssp_initialized
= B_TRUE
;
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
);
175 read_status_page(ses_snap_t
*sp
, ses2_diag_page_t page
)
177 libscsi_action_t
*ap
;
180 spc3_diag_page_impl_t
*pip
;
181 spc3_receive_diagnostic_results_cdb_t
*cp
;
186 ses2_diag_page_t retpage
;
188 for (pp
= sp
->ss_pages
; pp
!= NULL
; pp
= pp
->ssp_next
)
189 if (pp
->ssp_num
== page
&& !pp
->ssp_control
)
193 * No matching page. Since the page number is not under consumer or
194 * device control, this must be a bug.
200 flags
= LIBSCSI_AF_READ
| LIBSCSI_AF_SILENT
| LIBSCSI_AF_DIAGNOSE
|
204 ap
= libscsi_action_alloc(tp
->st_scsi_hdl
,
205 SPC3_CMD_RECEIVE_DIAGNOSTIC_RESULTS
, flags
, pp
->ssp_page
,
209 return (ses_libscsi_error(tp
->st_scsi_hdl
, "failed to "
210 "allocate SCSI action"));
212 cp
= (spc3_receive_diagnostic_results_cdb_t
*)
213 libscsi_action_get_cdb(ap
);
215 cp
->rdrc_page_code
= pp
->ssp_num
;
217 SCSI_WRITE16(&cp
->rdrc_allocation_length
,
218 MIN(pp
->ssp_alloc
, UINT16_MAX
));
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"));
226 if (libscsi_action_get_status(ap
) != 0) {
227 (void) ses_scsi_error(ap
,
228 "receive diagnostic results failed");
229 libscsi_action_free(ap
);
233 (void) libscsi_action_get_buffer(ap
, &buf
, &alloc
, &pp
->ssp_len
);
234 libscsi_action_free(ap
);
236 ASSERT(buf
== pp
->ssp_page
);
237 ASSERT(alloc
== pp
->ssp_alloc
);
239 if (pp
->ssp_alloc
- pp
->ssp_len
< 0x80 && pp
->ssp_alloc
< UINT16_MAX
) {
240 bzero(pp
->ssp_page
, pp
->ssp_len
);
242 if (grow_snap_page(pp
, 0) != 0)
247 if (pp
->ssp_len
< offsetof(spc3_diag_page_impl_t
, sdpi_data
)) {
248 bzero(pp
->ssp_page
, pp
->ssp_len
);
250 return (ses_error(ESES_BAD_RESPONSE
, "target returned "
251 "truncated page 0x%x (length %d)", page
, pp
->ssp_len
));
254 pip
= (spc3_diag_page_impl_t
*)buf
;
256 if (pip
->sdpi_page_code
== page
)
259 retpage
= pip
->sdpi_page_code
;
261 bzero(pp
->ssp_page
, pp
->ssp_len
);
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
));
271 return (ses_error(ESES_BAD_RESPONSE
, "target returned page 0x%x "
272 "instead of the requested page 0x%x", retpage
, page
));
276 send_control_page(ses_snap_t
*sp
, ses_snap_page_t
*pp
)
279 libscsi_action_t
*ap
;
280 spc3_send_diagnostic_cdb_t
*cp
;
285 flags
= LIBSCSI_AF_WRITE
| LIBSCSI_AF_SILENT
| LIBSCSI_AF_DIAGNOSE
|
288 ap
= libscsi_action_alloc(tp
->st_scsi_hdl
, SPC3_CMD_SEND_DIAGNOSTIC
,
289 flags
, pp
->ssp_page
, pp
->ssp_len
);
292 return (ses_libscsi_error(tp
->st_scsi_hdl
, "failed to "
293 "allocate SCSI action"));
295 cp
= (spc3_send_diagnostic_cdb_t
*)libscsi_action_get_cdb(ap
);
298 SCSI_WRITE16(&cp
->sdc_parameter_list_length
, pp
->ssp_len
);
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",
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
);
314 libscsi_action_free(ap
);
320 pages_skel_create(ses_snap_t
*sp
)
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
;
330 ASSERT(sp
->ss_pages
== NULL
);
332 if ((pp
= alloc_snap_page()) == NULL
)
335 pp
->ssp_num
= SES2_DIAGPAGE_SUPPORTED_PAGES
;
336 pp
->ssp_control
= B_FALSE
;
339 if (read_status_page(sp
, SES2_DIAGPAGE_SUPPORTED_PAGES
) != 0) {
346 pagelen
= pp
->ssp_len
;
348 npages
= SCSI_READ16(&pip
->sssdpi_page_length
);
350 for (i
= 0; i
< npages
; i
++) {
351 if (!SES_WITHIN_PAGE(pip
->sssdpi_pages
+ i
, 1, pip
,
355 page
= (ses2_diag_page_t
)pip
->sssdpi_pages
[i
];
357 * Skip the page we already added during the bootstrap.
359 if (page
== SES2_DIAGPAGE_SUPPORTED_PAGES
)
362 * The end of the page list may be padded with zeros; ignore
365 if (page
== 0 && i
> 0)
367 if ((np
= alloc_snap_page()) == NULL
) {
368 free_all_snap_pages(sp
);
376 * Allocate a control page as well, if we can use it.
378 if (ses_get_pagedesc(tp
, page
, SES_PAGE_CTL
) != NULL
) {
379 if ((np
= alloc_snap_page()) == NULL
) {
380 free_all_snap_pages(sp
);
384 np
->ssp_control
= B_TRUE
;
394 ses_snap_free(ses_snap_t
*sp
)
396 free_all_snap_pages(sp
);
397 ses_node_teardown(sp
->ss_root
);
398 ses_free(sp
->ss_nodes
);
403 ses_snap_rele_unlocked(ses_snap_t
*sp
)
405 ses_target_t
*tp
= sp
->ss_target
;
407 if (--sp
->ss_refcnt
!= 0)
410 if (sp
->ss_next
!= NULL
)
411 sp
->ss_next
->ss_prev
= sp
->ss_prev
;
413 if (sp
->ss_prev
!= NULL
)
414 sp
->ss_prev
->ss_next
= sp
->ss_next
;
416 tp
->st_snapshots
= sp
->ss_next
;
422 ses_snap_hold(ses_target_t
*tp
)
426 (void) pthread_mutex_lock(&tp
->st_lock
);
427 sp
= tp
->st_snapshots
;
429 (void) pthread_mutex_unlock(&tp
->st_lock
);
435 ses_snap_rele(ses_snap_t
*sp
)
437 ses_target_t
*tp
= sp
->ss_target
;
439 (void) pthread_mutex_lock(&tp
->st_lock
);
440 ses_snap_rele_unlocked(sp
);
441 (void) pthread_mutex_unlock(&tp
->st_lock
);
445 ses_snap_new(ses_target_t
*tp
)
452 size_t pages
, pagesize
, pagelen
;
456 if ((sp
= ses_zalloc(sizeof (ses_snap_t
))) == NULL
)
462 free_all_snap_pages(sp
);
464 if (pages_skel_create(sp
) != 0) {
469 sp
->ss_generation
= (uint32_t)-1;
470 sp
->ss_time
= gethrtime();
473 * First check for the short enclosure status diagnostic page and
474 * determine if this is a simple subenclosure or not.
477 for (pp
= sp
->ss_pages
; pp
!= NULL
; pp
= pp
->ssp_next
) {
478 if (pp
->ssp_num
== SES2_DIAGPAGE_SHORT_STATUS
)
482 for (pp
= sp
->ss_pages
; pp
!= NULL
; pp
= pp
->ssp_next
) {
487 * - Pages we've already filled in
488 * - Pages we don't understand (those with no descriptor)
490 if (pp
->ssp_len
> 0 || pp
->ssp_control
)
492 if ((dp
= ses_get_pagedesc(tp
, pp
->ssp_num
,
493 SES_PAGE_DIAG
)) == NULL
)
496 if (read_status_page(sp
, pp
->ssp_num
) != 0) {
498 * If this page is required, and this is not a simple
499 * subenclosure, then fail the entire snapshot.
501 if (dp
->spd_req
== SES_REQ_MANDATORY_ALL
||
502 (dp
->spd_req
== SES_REQ_MANDATORY_STANDARD
&&
512 * If the generation code has changed, we don't have a valid
513 * snapshot. Start over.
515 if (dp
->spd_gcoff
!= -1 &&
516 dp
->spd_gcoff
+ 4 <= pp
->ssp_len
) {
517 gc
= SCSI_READ32((uint8_t *)pp
->ssp_page
+
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
);
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.
544 if (sp
->ss_target
->st_truncate
) {
548 * Count the maximum number of pages we will need and allocate
549 * the necessary space.
552 for (pp
= sp
->ss_pages
; pp
!= NULL
; pp
= pp
->ssp_next
) {
553 if (pp
->ssp_control
|| pp
->ssp_len
== 0)
556 pages
+= (P2ROUNDUP(pp
->ssp_len
, pagesize
) /
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");
569 for (pp
= sp
->ss_pages
; pp
!= NULL
; pp
= pp
->ssp_next
) {
570 if (pp
->ssp_control
|| pp
->ssp_len
== 0)
573 pages
= P2ROUNDUP(pp
->ssp_len
, pagesize
) / pagesize
;
574 pp
->ssp_mmap_base
= scratch
;
575 pp
->ssp_mmap_len
= pages
* pagesize
;
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
-
583 pp
->ssp_len
= pagelen
;
585 (void) munmap(pp
->ssp_mmap_base
+ pages
* pagesize
,
587 scratch
+= (pages
+ 1) * pagesize
;
592 if (ses_fill_snap(sp
) != 0) {
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
;
605 (void) pthread_mutex_unlock(&tp
->st_lock
);
611 ses_snap_do_ctl(ses_snap_t
*sp
)
613 ses_snap_page_t
*pp
, *up
;
616 for (pp
= sp
->ss_pages
; pp
!= NULL
; pp
= pp
->ssp_next
) {
617 if (!pp
->ssp_control
)
620 if (pp
->ssp_initialized
&& send_control_page(sp
, pp
) != 0)
623 for (up
= pp
->ssp_unique
; up
!= NULL
; up
= up
->ssp_next
) {
624 if (send_control_page(sp
, up
) != 0)
631 for (pp
= sp
->ss_pages
; pp
!= NULL
; pp
= pp
->ssp_next
) {
632 if (!pp
->ssp_control
)
635 pp
->ssp_initialized
= B_FALSE
;
636 while ((up
= pp
->ssp_unique
) != NULL
) {
637 pp
->ssp_unique
= up
->ssp_next
;
647 ses_snap_generation(ses_snap_t
*sp
)
649 return (sp
->ss_generation
);
652 static ses_walk_action_t
653 ses_walk_node(ses_node_t
*np
, ses_walk_f func
, void *arg
)
655 ses_walk_action_t action
;
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
)
664 if (ses_walk_node(ses_node_child(np
), func
, arg
) ==
665 SES_WALK_ACTION_TERMINATE
)
666 return (SES_WALK_ACTION_TERMINATE
);
669 return (SES_WALK_ACTION_CONTINUE
);
673 ses_walk(ses_snap_t
*sp
, ses_walk_f func
, void *arg
)
675 (void) ses_walk_node(ses_root_node(sp
), func
, arg
);
681 static ses_walk_action_t
682 ses_fill_nodes(ses_node_t
*np
, void *unused
)
684 np
->sn_snapshot
->ss_nodes
[np
->sn_id
] = np
;
686 return (SES_WALK_ACTION_CONTINUE
);
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
695 ses_node_lookup(ses_snap_t
*sp
, uint64_t id
)
697 uint32_t gen
= (id
>> 32);
698 uint32_t idx
= (id
& 0xFFFFFFFF);
700 if (sp
->ss_generation
!= gen
) {
701 (void) ses_set_errno(ESES_CHANGED
);
705 if (idx
>= sp
->ss_n_nodes
) {
706 (void) ses_error(ESES_BAD_NODE
,
707 "no such node in snapshot");
712 * If this is our first lookup attempt, construct the array for fast
715 if (sp
->ss_nodes
== NULL
) {
716 if ((sp
->ss_nodes
= ses_zalloc(
717 sp
->ss_n_nodes
* sizeof (void *))) == NULL
)
720 (void) ses_walk(sp
, ses_fill_nodes
, NULL
);
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
]);