16316Seschrock /*
26316Seschrock * CDDL HEADER START
36316Seschrock *
46316Seschrock * The contents of this file are subject to the terms of the
56316Seschrock * Common Development and Distribution License (the "License").
66316Seschrock * You may not use this file except in compliance with the License.
76316Seschrock *
86316Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96316Seschrock * or http://www.opensolaris.org/os/licensing.
106316Seschrock * See the License for the specific language governing permissions
116316Seschrock * and limitations under the License.
126316Seschrock *
136316Seschrock * When distributing Covered Code, include this CDDL HEADER in each
146316Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156316Seschrock * If applicable, add the following below this CDDL HEADER, with the
166316Seschrock * fields enclosed by brackets "[]" replaced with your own identifying
176316Seschrock * information: Portions Copyright [yyyy] [name of copyright owner]
186316Seschrock *
196316Seschrock * CDDL HEADER END
206316Seschrock */
216316Seschrock
226316Seschrock /*
23*12126SHyon.Kim@Sun.COM * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
246316Seschrock */
256316Seschrock
266316Seschrock #include <scsi/libses.h>
276316Seschrock #include "ses_impl.h"
286316Seschrock
296316Seschrock ses_snap_page_t *
ses_snap_find_page(ses_snap_t * sp,ses2_diag_page_t page,boolean_t ctl)306316Seschrock ses_snap_find_page(ses_snap_t *sp, ses2_diag_page_t page, boolean_t ctl)
316316Seschrock {
326316Seschrock ses_snap_page_t *pp;
336316Seschrock
346316Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next)
356316Seschrock if (pp->ssp_num == page && pp->ssp_control == ctl &&
366316Seschrock (pp->ssp_len > 0 || pp->ssp_control))
376316Seschrock return (pp);
386316Seschrock
396316Seschrock return (NULL);
406316Seschrock }
416316Seschrock
426316Seschrock static int
grow_snap_page(ses_snap_page_t * pp,size_t min)436316Seschrock grow_snap_page(ses_snap_page_t *pp, size_t min)
446316Seschrock {
456316Seschrock uint8_t *newbuf;
466316Seschrock
476316Seschrock if (min == 0 || min < pp->ssp_alloc)
486316Seschrock min = pp->ssp_alloc * 2;
496316Seschrock
506316Seschrock if ((newbuf = ses_realloc(pp->ssp_page, min)) == NULL)
516316Seschrock return (-1);
526316Seschrock
536316Seschrock pp->ssp_page = newbuf;
546316Seschrock pp->ssp_alloc = min;
556316Seschrock
566316Seschrock bzero(newbuf + pp->ssp_len, pp->ssp_alloc - pp->ssp_len);
576316Seschrock
586316Seschrock return (0);
596316Seschrock }
606316Seschrock
616316Seschrock static ses_snap_page_t *
alloc_snap_page(void)626316Seschrock alloc_snap_page(void)
636316Seschrock {
646316Seschrock ses_snap_page_t *pp;
656316Seschrock
666316Seschrock if ((pp = ses_zalloc(sizeof (ses_snap_page_t))) == NULL)
676316Seschrock return (NULL);
686316Seschrock
696316Seschrock if ((pp->ssp_page = ses_zalloc(SES2_MIN_DIAGPAGE_ALLOC)) == NULL) {
706316Seschrock ses_free(pp);
716316Seschrock return (NULL);
726316Seschrock }
736316Seschrock
746316Seschrock pp->ssp_num = -1;
756316Seschrock pp->ssp_alloc = SES2_MIN_DIAGPAGE_ALLOC;
766316Seschrock
776316Seschrock return (pp);
786316Seschrock }
796316Seschrock
806316Seschrock static void
free_snap_page(ses_snap_page_t * pp)816316Seschrock free_snap_page(ses_snap_page_t *pp)
826316Seschrock {
836316Seschrock if (pp == NULL)
846316Seschrock return;
856316Seschrock
866316Seschrock if (pp->ssp_mmap_base)
876316Seschrock (void) munmap(pp->ssp_mmap_base, pp->ssp_mmap_len);
886316Seschrock else
896316Seschrock ses_free(pp->ssp_page);
906316Seschrock ses_free(pp);
916316Seschrock }
926316Seschrock
936316Seschrock static void
free_all_snap_pages(ses_snap_t * sp)946316Seschrock free_all_snap_pages(ses_snap_t *sp)
956316Seschrock {
966316Seschrock ses_snap_page_t *pp, *np;
976316Seschrock
986316Seschrock for (pp = sp->ss_pages; pp != NULL; pp = np) {
996316Seschrock np = pp->ssp_next;
1006316Seschrock free_snap_page(pp);
1016316Seschrock }
1026316Seschrock
1036316Seschrock sp->ss_pages = NULL;
1046316Seschrock }
1056316Seschrock
1066316Seschrock /*
1076316Seschrock * Grow (if needed) the control page buffer, fill in the page code, page
1086316Seschrock * length, and generation count, and return a pointer to the page. The
1096316Seschrock * caller is responsible for filling in the rest of the page data. If 'unique'
1106316Seschrock * is specified, then a new page instance is created instead of sharing the
1116316Seschrock * current one.
1126316Seschrock */
1136316Seschrock ses_snap_page_t *
ses_snap_ctl_page(ses_snap_t * sp,ses2_diag_page_t page,size_t dlen,boolean_t unique)1146316Seschrock ses_snap_ctl_page(ses_snap_t *sp, ses2_diag_page_t page, size_t dlen,
1156316Seschrock boolean_t unique)
1166316Seschrock {
1176316Seschrock ses_target_t *tp = sp->ss_target;
1186316Seschrock spc3_diag_page_impl_t *pip;
1196316Seschrock ses_snap_page_t *pp, *up, **loc;
1206316Seschrock ses_pagedesc_t *dp;
1216316Seschrock size_t len;
1226316Seschrock
1236316Seschrock pp = ses_snap_find_page(sp, page, B_TRUE);
1246316Seschrock if (pp == NULL) {
1256316Seschrock (void) ses_set_errno(ESES_NOTSUP);
1266316Seschrock return (NULL);
1276316Seschrock }
1286316Seschrock
1296316Seschrock if (pp->ssp_initialized && !unique)
1306316Seschrock return (pp);
1316316Seschrock
1326316Seschrock if (unique) {
1336316Seschrock /*
1346316Seschrock * The user has requested a unique instance of the page. Create
1356316Seschrock * a new ses_snap_page_t instance and chain it off the
1366316Seschrock * 'ssp_instances' list of the master page. These must be
1376316Seschrock * appended to the end of the chain, as the order of operations
1386316Seschrock * may be important (i.e. microcode download).
1396316Seschrock */
1406316Seschrock if ((up = alloc_snap_page()) == NULL)
1416316Seschrock return (NULL);
1426316Seschrock
1436316Seschrock up->ssp_num = pp->ssp_num;
1446316Seschrock up->ssp_control = B_TRUE;
1456316Seschrock
1466316Seschrock for (loc = &pp->ssp_unique; *loc != NULL;
1476316Seschrock loc = &(*loc)->ssp_next)
1486316Seschrock ;
1496316Seschrock
1506316Seschrock *loc = up;
1516316Seschrock pp = up;
1526316Seschrock }
1536316Seschrock
1546316Seschrock dp = ses_get_pagedesc(tp, page, SES_PAGE_CTL);
1556316Seschrock ASSERT(dp != NULL);
1566316Seschrock
1576316Seschrock len = dp->spd_ctl_len(sp->ss_n_elem, page, dlen);
158*12126SHyon.Kim@Sun.COM if (pp->ssp_alloc < len && grow_snap_page(pp, len) != 0)
1596316Seschrock return (NULL);
1606316Seschrock pp->ssp_len = len;
1616316Seschrock bzero(pp->ssp_page, len);
1626316Seschrock pp->ssp_initialized = B_TRUE;
1636316Seschrock
1646316Seschrock pip = (spc3_diag_page_impl_t *)pp->ssp_page;
1656316Seschrock pip->sdpi_page_code = (uint8_t)page;
1666316Seschrock SCSI_WRITE16(&pip->sdpi_page_length,
1676316Seschrock len - offsetof(spc3_diag_page_impl_t, sdpi_data[0]));
1686316Seschrock if (dp->spd_gcoff != -1)
1696316Seschrock SCSI_WRITE32((uint8_t *)pip + dp->spd_gcoff, sp->ss_generation);
1706316Seschrock
1716316Seschrock return (pp);
1726316Seschrock }
1736316Seschrock
1746316Seschrock static int
read_status_page(ses_snap_t * sp,ses2_diag_page_t page)1756316Seschrock read_status_page(ses_snap_t *sp, ses2_diag_page_t page)
1766316Seschrock {
1776316Seschrock libscsi_action_t *ap;
1786316Seschrock ses_snap_page_t *pp;
1796316Seschrock ses_target_t *tp;
1806316Seschrock spc3_diag_page_impl_t *pip;
1816316Seschrock spc3_receive_diagnostic_results_cdb_t *cp;
1826316Seschrock uint_t flags;
1836316Seschrock uint8_t *buf;
1846316Seschrock size_t alloc;
1856316Seschrock uint_t retries = 0;
1866316Seschrock ses2_diag_page_t retpage;
1876316Seschrock
1886316Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next)
1896316Seschrock if (pp->ssp_num == page && !pp->ssp_control)
1906316Seschrock break;
1916316Seschrock
1926316Seschrock /*
1936316Seschrock * No matching page. Since the page number is not under consumer or
1946316Seschrock * device control, this must be a bug.
1956316Seschrock */
1966316Seschrock ASSERT(pp != NULL);
1976316Seschrock
1986316Seschrock tp = sp->ss_target;
1996316Seschrock
2006316Seschrock flags = LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE |
2016316Seschrock LIBSCSI_AF_RQSENSE;
2026316Seschrock
2036316Seschrock again:
2046316Seschrock ap = libscsi_action_alloc(tp->st_scsi_hdl,
2056316Seschrock SPC3_CMD_RECEIVE_DIAGNOSTIC_RESULTS, flags, pp->ssp_page,
2066316Seschrock pp->ssp_alloc);
2076316Seschrock
2086316Seschrock if (ap == NULL)
2096316Seschrock return (ses_libscsi_error(tp->st_scsi_hdl, "failed to "
2106316Seschrock "allocate SCSI action"));
2116316Seschrock
2126316Seschrock cp = (spc3_receive_diagnostic_results_cdb_t *)
2136316Seschrock libscsi_action_get_cdb(ap);
2146316Seschrock
2156316Seschrock cp->rdrc_page_code = pp->ssp_num;
2166316Seschrock cp->rdrc_pcv = 1;
2176316Seschrock SCSI_WRITE16(&cp->rdrc_allocation_length,
2186316Seschrock MIN(pp->ssp_alloc, UINT16_MAX));
2196316Seschrock
2206316Seschrock if (libscsi_exec(ap, tp->st_target) != 0) {
2216316Seschrock libscsi_action_free(ap);
2226316Seschrock return (ses_libscsi_error(tp->st_scsi_hdl,
2236316Seschrock "receive diagnostic results failed"));
2246316Seschrock }
2256316Seschrock
2266316Seschrock if (libscsi_action_get_status(ap) != 0) {
2276316Seschrock (void) ses_scsi_error(ap,
2286316Seschrock "receive diagnostic results failed");
2296316Seschrock libscsi_action_free(ap);
2306316Seschrock return (-1);
2316316Seschrock }
2326316Seschrock
2336316Seschrock (void) libscsi_action_get_buffer(ap, &buf, &alloc, &pp->ssp_len);
2346316Seschrock libscsi_action_free(ap);
2356316Seschrock
2366316Seschrock ASSERT(buf == pp->ssp_page);
2376316Seschrock ASSERT(alloc == pp->ssp_alloc);
2386316Seschrock
239*12126SHyon.Kim@Sun.COM if (pp->ssp_alloc - pp->ssp_len < 0x80 && pp->ssp_alloc < UINT16_MAX) {
2406316Seschrock bzero(pp->ssp_page, pp->ssp_len);
2416316Seschrock pp->ssp_len = 0;
2426316Seschrock if (grow_snap_page(pp, 0) != 0)
2436316Seschrock return (-1);
2446316Seschrock goto again;
2456316Seschrock }
2466316Seschrock
247*12126SHyon.Kim@Sun.COM if (pp->ssp_len < offsetof(spc3_diag_page_impl_t, sdpi_data)) {
248*12126SHyon.Kim@Sun.COM bzero(pp->ssp_page, pp->ssp_len);
249*12126SHyon.Kim@Sun.COM pp->ssp_len = 0;
250*12126SHyon.Kim@Sun.COM return (ses_error(ESES_BAD_RESPONSE, "target returned "
251*12126SHyon.Kim@Sun.COM "truncated page 0x%x (length %d)", page, pp->ssp_len));
252*12126SHyon.Kim@Sun.COM }
253*12126SHyon.Kim@Sun.COM
2546316Seschrock pip = (spc3_diag_page_impl_t *)buf;
2556316Seschrock
2566316Seschrock if (pip->sdpi_page_code == page)
2576316Seschrock return (0);
2586316Seschrock
2596316Seschrock retpage = pip->sdpi_page_code;
2606316Seschrock
2616316Seschrock bzero(pp->ssp_page, pp->ssp_len);
2626316Seschrock pp->ssp_len = 0;
2636316Seschrock
2646316Seschrock if (retpage == SES2_DIAGPAGE_ENCLOSURE_BUSY) {
2656316Seschrock if (++retries > LIBSES_MAX_BUSY_RETRIES)
2666316Seschrock return (ses_error(ESES_BUSY, "too many "
2676316Seschrock "enclosure busy responses for page 0x%x", page));
2686316Seschrock goto again;
2696316Seschrock }
2706316Seschrock
2716316Seschrock return (ses_error(ESES_BAD_RESPONSE, "target returned page 0x%x "
2726316Seschrock "instead of the requested page 0x%x", retpage, page));
2736316Seschrock }
2746316Seschrock
2756316Seschrock static int
send_control_page(ses_snap_t * sp,ses_snap_page_t * pp)2766316Seschrock send_control_page(ses_snap_t *sp, ses_snap_page_t *pp)
2776316Seschrock {
2786316Seschrock ses_target_t *tp;
2796316Seschrock libscsi_action_t *ap;
2806316Seschrock spc3_send_diagnostic_cdb_t *cp;
2816316Seschrock uint_t flags;
2826316Seschrock
2836316Seschrock tp = sp->ss_target;
2846316Seschrock
2856316Seschrock flags = LIBSCSI_AF_WRITE | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE |
2866316Seschrock LIBSCSI_AF_RQSENSE;
2876316Seschrock
2886316Seschrock ap = libscsi_action_alloc(tp->st_scsi_hdl, SPC3_CMD_SEND_DIAGNOSTIC,
2896316Seschrock flags, pp->ssp_page, pp->ssp_len);
2906316Seschrock
2916316Seschrock if (ap == NULL)
2926316Seschrock return (ses_libscsi_error(tp->st_scsi_hdl, "failed to "
2936316Seschrock "allocate SCSI action"));
2946316Seschrock
2956316Seschrock cp = (spc3_send_diagnostic_cdb_t *)libscsi_action_get_cdb(ap);
2966316Seschrock
2976316Seschrock cp->sdc_pf = 1;
2986316Seschrock SCSI_WRITE16(&cp->sdc_parameter_list_length, pp->ssp_len);
2996316Seschrock
3006316Seschrock if (libscsi_exec(ap, tp->st_target) != 0) {
3016316Seschrock libscsi_action_free(ap);
3026316Seschrock return (ses_libscsi_error(tp->st_scsi_hdl,
3036316Seschrock "SEND DIAGNOSTIC command failed for page 0x%x",
3046316Seschrock pp->ssp_num));
3056316Seschrock }
3066316Seschrock
3076316Seschrock if (libscsi_action_get_status(ap) != 0) {
3086316Seschrock (void) ses_scsi_error(ap, "SEND DIAGNOSTIC command "
3096316Seschrock "failed for page 0x%x", pp->ssp_num);
3106316Seschrock libscsi_action_free(ap);
3116316Seschrock return (-1);
3126316Seschrock }
3136316Seschrock
3146316Seschrock libscsi_action_free(ap);
3156316Seschrock
3166316Seschrock return (0);
3176316Seschrock }
3186316Seschrock
3196316Seschrock static int
pages_skel_create(ses_snap_t * sp)3206316Seschrock pages_skel_create(ses_snap_t *sp)
3216316Seschrock {
3226316Seschrock ses_snap_page_t *pp, *np;
3236316Seschrock ses_target_t *tp = sp->ss_target;
3246316Seschrock ses2_supported_ses_diag_page_impl_t *pip;
3256316Seschrock ses2_diag_page_t page;
3266316Seschrock size_t npages;
3276316Seschrock size_t pagelen;
3286316Seschrock off_t i;
3296316Seschrock
3306316Seschrock ASSERT(sp->ss_pages == NULL);
3316316Seschrock
3326316Seschrock if ((pp = alloc_snap_page()) == NULL)
3336316Seschrock return (-1);
3346316Seschrock
3356316Seschrock pp->ssp_num = SES2_DIAGPAGE_SUPPORTED_PAGES;
3366316Seschrock pp->ssp_control = B_FALSE;
3376316Seschrock sp->ss_pages = pp;
3386316Seschrock
3396316Seschrock if (read_status_page(sp, SES2_DIAGPAGE_SUPPORTED_PAGES) != 0) {
3406316Seschrock free_snap_page(pp);
3416316Seschrock sp->ss_pages = NULL;
3426316Seschrock return (-1);
3436316Seschrock }
3446316Seschrock
3456316Seschrock pip = pp->ssp_page;
3466316Seschrock pagelen = pp->ssp_len;
3476316Seschrock
3486316Seschrock npages = SCSI_READ16(&pip->sssdpi_page_length);
3496316Seschrock
3506316Seschrock for (i = 0; i < npages; i++) {
3516316Seschrock if (!SES_WITHIN_PAGE(pip->sssdpi_pages + i, 1, pip,
3526316Seschrock pagelen))
3536316Seschrock break;
3546316Seschrock
3556316Seschrock page = (ses2_diag_page_t)pip->sssdpi_pages[i];
3566316Seschrock /*
3576316Seschrock * Skip the page we already added during the bootstrap.
3586316Seschrock */
3596316Seschrock if (page == SES2_DIAGPAGE_SUPPORTED_PAGES)
3606316Seschrock continue;
3616316Seschrock /*
3626316Seschrock * The end of the page list may be padded with zeros; ignore
3636316Seschrock * them all.
3646316Seschrock */
3656316Seschrock if (page == 0 && i > 0)
3666316Seschrock break;
3676316Seschrock if ((np = alloc_snap_page()) == NULL) {
3686316Seschrock free_all_snap_pages(sp);
3696316Seschrock return (-1);
3706316Seschrock }
3716316Seschrock np->ssp_num = page;
3726316Seschrock pp->ssp_next = np;
3736316Seschrock pp = np;
3746316Seschrock
3756316Seschrock /*
3766316Seschrock * Allocate a control page as well, if we can use it.
3776316Seschrock */
3786316Seschrock if (ses_get_pagedesc(tp, page, SES_PAGE_CTL) != NULL) {
3796316Seschrock if ((np = alloc_snap_page()) == NULL) {
3806316Seschrock free_all_snap_pages(sp);
3816316Seschrock return (-1);
3826316Seschrock }
3836316Seschrock np->ssp_num = page;
3846316Seschrock np->ssp_control = B_TRUE;
3856316Seschrock pp->ssp_next = np;
3866316Seschrock pp = np;
3876316Seschrock }
3886316Seschrock }
3896316Seschrock
3906316Seschrock return (0);
3916316Seschrock }
3926316Seschrock
3936316Seschrock static void
ses_snap_free(ses_snap_t * sp)3946316Seschrock ses_snap_free(ses_snap_t *sp)
3956316Seschrock {
3966316Seschrock free_all_snap_pages(sp);
3976316Seschrock ses_node_teardown(sp->ss_root);
3986316Seschrock ses_free(sp->ss_nodes);
3996316Seschrock ses_free(sp);
4006316Seschrock }
4016316Seschrock
4026316Seschrock static void
ses_snap_rele_unlocked(ses_snap_t * sp)4036316Seschrock ses_snap_rele_unlocked(ses_snap_t *sp)
4046316Seschrock {
4056316Seschrock ses_target_t *tp = sp->ss_target;
4066316Seschrock
4076316Seschrock if (--sp->ss_refcnt != 0)
4086316Seschrock return;
4096316Seschrock
4106316Seschrock if (sp->ss_next != NULL)
4116316Seschrock sp->ss_next->ss_prev = sp->ss_prev;
4126316Seschrock
4136316Seschrock if (sp->ss_prev != NULL)
4146316Seschrock sp->ss_prev->ss_next = sp->ss_next;
4156316Seschrock else
4166316Seschrock tp->st_snapshots = sp->ss_next;
4176316Seschrock
4186316Seschrock ses_snap_free(sp);
4196316Seschrock }
4206316Seschrock
4216316Seschrock ses_snap_t *
ses_snap_hold(ses_target_t * tp)4226316Seschrock ses_snap_hold(ses_target_t *tp)
4236316Seschrock {
4246316Seschrock ses_snap_t *sp;
4256316Seschrock
4266316Seschrock (void) pthread_mutex_lock(&tp->st_lock);
4276316Seschrock sp = tp->st_snapshots;
4286316Seschrock sp->ss_refcnt++;
4296316Seschrock (void) pthread_mutex_unlock(&tp->st_lock);
4306316Seschrock
4316316Seschrock return (sp);
4326316Seschrock }
4336316Seschrock
4346316Seschrock void
ses_snap_rele(ses_snap_t * sp)4356316Seschrock ses_snap_rele(ses_snap_t *sp)
4366316Seschrock {
4376316Seschrock ses_target_t *tp = sp->ss_target;
4386316Seschrock
4396316Seschrock (void) pthread_mutex_lock(&tp->st_lock);
4406316Seschrock ses_snap_rele_unlocked(sp);
4416316Seschrock (void) pthread_mutex_unlock(&tp->st_lock);
4426316Seschrock }
4436316Seschrock
4446316Seschrock ses_snap_t *
ses_snap_new(ses_target_t * tp)4456316Seschrock ses_snap_new(ses_target_t *tp)
4466316Seschrock {
4476316Seschrock ses_snap_t *sp;
4486316Seschrock ses_snap_page_t *pp;
4496316Seschrock uint32_t gc;
4506316Seschrock uint_t retries = 0;
4516316Seschrock ses_pagedesc_t *dp;
4526316Seschrock size_t pages, pagesize, pagelen;
4536316Seschrock char *scratch;
454*12126SHyon.Kim@Sun.COM boolean_t simple;
4556316Seschrock
4566316Seschrock if ((sp = ses_zalloc(sizeof (ses_snap_t))) == NULL)
4576316Seschrock return (NULL);
4586316Seschrock
4596316Seschrock sp->ss_target = tp;
4606316Seschrock
4616316Seschrock again:
4626316Seschrock free_all_snap_pages(sp);
4636316Seschrock
4646316Seschrock if (pages_skel_create(sp) != 0) {
4656316Seschrock free(sp);
4666316Seschrock return (NULL);
4676316Seschrock }
4686316Seschrock
4696316Seschrock sp->ss_generation = (uint32_t)-1;
4706316Seschrock sp->ss_time = gethrtime();
4716316Seschrock
472*12126SHyon.Kim@Sun.COM /*
473*12126SHyon.Kim@Sun.COM * First check for the short enclosure status diagnostic page and
474*12126SHyon.Kim@Sun.COM * determine if this is a simple subenclosure or not.
475*12126SHyon.Kim@Sun.COM */
476*12126SHyon.Kim@Sun.COM simple = B_FALSE;
477*12126SHyon.Kim@Sun.COM for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
478*12126SHyon.Kim@Sun.COM if (pp->ssp_num == SES2_DIAGPAGE_SHORT_STATUS)
479*12126SHyon.Kim@Sun.COM simple = B_TRUE;
480*12126SHyon.Kim@Sun.COM }
481*12126SHyon.Kim@Sun.COM
4826316Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
4836316Seschrock /*
4846316Seschrock * We skip all of:
4856316Seschrock *
4866316Seschrock * - Control pages
4876316Seschrock * - Pages we've already filled in
4886316Seschrock * - Pages we don't understand (those with no descriptor)
4896316Seschrock */
4906316Seschrock if (pp->ssp_len > 0 || pp->ssp_control)
4916316Seschrock continue;
4926316Seschrock if ((dp = ses_get_pagedesc(tp, pp->ssp_num,
4936316Seschrock SES_PAGE_DIAG)) == NULL)
4946316Seschrock continue;
4956316Seschrock
496*12126SHyon.Kim@Sun.COM if (read_status_page(sp, pp->ssp_num) != 0) {
497*12126SHyon.Kim@Sun.COM /*
498*12126SHyon.Kim@Sun.COM * If this page is required, and this is not a simple
499*12126SHyon.Kim@Sun.COM * subenclosure, then fail the entire snapshot.
500*12126SHyon.Kim@Sun.COM */
501*12126SHyon.Kim@Sun.COM if (dp->spd_req == SES_REQ_MANDATORY_ALL ||
502*12126SHyon.Kim@Sun.COM (dp->spd_req == SES_REQ_MANDATORY_STANDARD &&
503*12126SHyon.Kim@Sun.COM !simple)) {
504*12126SHyon.Kim@Sun.COM ses_snap_free(sp);
505*12126SHyon.Kim@Sun.COM return (NULL);
506*12126SHyon.Kim@Sun.COM }
507*12126SHyon.Kim@Sun.COM
5086316Seschrock continue;
509*12126SHyon.Kim@Sun.COM }
5106316Seschrock
5116316Seschrock /*
5126316Seschrock * If the generation code has changed, we don't have a valid
5136316Seschrock * snapshot. Start over.
5146316Seschrock */
5156316Seschrock if (dp->spd_gcoff != -1 &&
5166316Seschrock dp->spd_gcoff + 4 <= pp->ssp_len) {
5176316Seschrock gc = SCSI_READ32((uint8_t *)pp->ssp_page +
5186316Seschrock dp->spd_gcoff);
5196316Seschrock if (sp->ss_generation == (uint32_t)-1) {
5206316Seschrock sp->ss_generation = gc;
5216316Seschrock } else if (sp->ss_generation != gc) {
5226316Seschrock if (++retries > LIBSES_MAX_GC_RETRIES) {
5236316Seschrock (void) ses_error(ESES_TOOMUCHCHANGE,
5246316Seschrock "too many generation count "
5256316Seschrock "mismatches: page 0x%x gc %u "
5266316Seschrock "previous page %u", dp->spd_gcoff,
5276316Seschrock gc, sp->ss_generation);
5286316Seschrock ses_snap_free((ses_snap_t *)sp);
5296316Seschrock return (NULL);
5306316Seschrock }
5316316Seschrock goto again;
5326316Seschrock }
5336316Seschrock }
5346316Seschrock }
5356316Seschrock
5366316Seschrock /*
5376316Seschrock * The LIBSES_TRUNCATE environment variable is a debugging tool which,
5386316Seschrock * if set, randomly truncates all pages (except
5396316Seschrock * SES2_DIAGPAGE_SUPPORTED_PAGES). In order to be truly evil, we
5406316Seschrock * mmap() each page with enough space after it so we can move the data
5416316Seschrock * up to the end of a page and unmap the following page so that any
5426316Seschrock * attempt to read past the end of the page results in a segfault.
5436316Seschrock */
5446316Seschrock if (sp->ss_target->st_truncate) {
5456316Seschrock pagesize = PAGESIZE;
5466316Seschrock
5476316Seschrock /*
5486316Seschrock * Count the maximum number of pages we will need and allocate
5496316Seschrock * the necessary space.
5506316Seschrock */
5516316Seschrock pages = 0;
5526316Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
5536316Seschrock if (pp->ssp_control || pp->ssp_len == 0)
5546316Seschrock continue;
5556316Seschrock
5566316Seschrock pages += (P2ROUNDUP(pp->ssp_len, pagesize) /
5576316Seschrock pagesize) + 1;
5586316Seschrock }
5596316Seschrock
5606316Seschrock if ((scratch = mmap(NULL, pages * pagesize,
5616316Seschrock PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
5626316Seschrock -1, 0)) == MAP_FAILED) {
5636316Seschrock (void) ses_error(ESES_NOMEM,
5646316Seschrock "failed to mmap() pages for truncation");
5656316Seschrock ses_snap_free(sp);
5666316Seschrock return (NULL);
5676316Seschrock }
5686316Seschrock
5696316Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
5706316Seschrock if (pp->ssp_control || pp->ssp_len == 0)
5716316Seschrock continue;
5726316Seschrock
5736316Seschrock pages = P2ROUNDUP(pp->ssp_len, pagesize) / pagesize;
5746316Seschrock pp->ssp_mmap_base = scratch;
5756316Seschrock pp->ssp_mmap_len = pages * pagesize;
5766316Seschrock
5776316Seschrock pagelen = lrand48() % pp->ssp_len;
5786316Seschrock (void) memcpy(pp->ssp_mmap_base + pp->ssp_mmap_len -
5796316Seschrock pagelen, pp->ssp_page, pagelen);
5806316Seschrock ses_free(pp->ssp_page);
5816316Seschrock pp->ssp_page = pp->ssp_mmap_base + pp->ssp_mmap_len -
5826316Seschrock pagelen;
5836316Seschrock pp->ssp_len = pagelen;
5846316Seschrock
5856316Seschrock (void) munmap(pp->ssp_mmap_base + pages * pagesize,
5866316Seschrock pagesize);
5876316Seschrock scratch += (pages + 1) * pagesize;
5886316Seschrock }
5896316Seschrock }
5906316Seschrock
5916316Seschrock
5926316Seschrock if (ses_fill_snap(sp) != 0) {
5936316Seschrock ses_snap_free(sp);
5946316Seschrock return (NULL);
5956316Seschrock }
5966316Seschrock
5976316Seschrock (void) pthread_mutex_lock(&tp->st_lock);
5986316Seschrock if (tp->st_snapshots != NULL)
5996316Seschrock ses_snap_rele_unlocked(tp->st_snapshots);
6006316Seschrock sp->ss_next = tp->st_snapshots;
6016316Seschrock if (tp->st_snapshots != NULL)
6026316Seschrock tp->st_snapshots->ss_prev = sp;
6036316Seschrock tp->st_snapshots = sp;
6046316Seschrock sp->ss_refcnt = 2;
6056316Seschrock (void) pthread_mutex_unlock(&tp->st_lock);
6066316Seschrock
6076316Seschrock return (sp);
6086316Seschrock }
6096316Seschrock
6106316Seschrock int
ses_snap_do_ctl(ses_snap_t * sp)6116316Seschrock ses_snap_do_ctl(ses_snap_t *sp)
6126316Seschrock {
6136316Seschrock ses_snap_page_t *pp, *up;
6146316Seschrock int ret = -1;
6156316Seschrock
6166316Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
6176316Seschrock if (!pp->ssp_control)
6186316Seschrock continue;
6196316Seschrock
6206316Seschrock if (pp->ssp_initialized && send_control_page(sp, pp) != 0)
6216316Seschrock goto error;
6226316Seschrock
6236316Seschrock for (up = pp->ssp_unique; up != NULL; up = up->ssp_next) {
6246316Seschrock if (send_control_page(sp, up) != 0)
6256316Seschrock goto error;
6266316Seschrock }
6276316Seschrock }
6286316Seschrock
6296316Seschrock ret = 0;
6306316Seschrock error:
6316316Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
6326316Seschrock if (!pp->ssp_control)
6336316Seschrock continue;
6346316Seschrock
6356316Seschrock pp->ssp_initialized = B_FALSE;
6366316Seschrock while ((up = pp->ssp_unique) != NULL) {
6376316Seschrock pp->ssp_unique = up->ssp_next;
6386316Seschrock free_snap_page(up);
6396316Seschrock }
6406316Seschrock }
6416316Seschrock
6426316Seschrock
6436316Seschrock return (ret);
6446316Seschrock }
6456316Seschrock
6466316Seschrock uint32_t
ses_snap_generation(ses_snap_t * sp)6476316Seschrock ses_snap_generation(ses_snap_t *sp)
6486316Seschrock {
6496316Seschrock return (sp->ss_generation);
6506316Seschrock }
6516316Seschrock
6526316Seschrock static ses_walk_action_t
ses_walk_node(ses_node_t * np,ses_walk_f func,void * arg)6536316Seschrock ses_walk_node(ses_node_t *np, ses_walk_f func, void *arg)
6546316Seschrock {
6556316Seschrock ses_walk_action_t action;
6566316Seschrock
6576316Seschrock for (; np != NULL; np = ses_node_sibling(np)) {
6586316Seschrock action = func(np, arg);
6596316Seschrock if (action == SES_WALK_ACTION_TERMINATE)
6606316Seschrock return (SES_WALK_ACTION_TERMINATE);
6616316Seschrock if (action == SES_WALK_ACTION_PRUNE ||
6626316Seschrock ses_node_child(np) == NULL)
6636316Seschrock continue;
6646316Seschrock if (ses_walk_node(ses_node_child(np), func, arg) ==
6656316Seschrock SES_WALK_ACTION_TERMINATE)
6666316Seschrock return (SES_WALK_ACTION_TERMINATE);
6676316Seschrock }
6686316Seschrock
6696316Seschrock return (SES_WALK_ACTION_CONTINUE);
6706316Seschrock }
6716316Seschrock
6726316Seschrock int
ses_walk(ses_snap_t * sp,ses_walk_f func,void * arg)6736316Seschrock ses_walk(ses_snap_t *sp, ses_walk_f func, void *arg)
6746316Seschrock {
6756316Seschrock (void) ses_walk_node(ses_root_node(sp), func, arg);
6766316Seschrock
6776316Seschrock return (0);
6786316Seschrock }
6796316Seschrock
6806316Seschrock /*ARGSUSED*/
6816316Seschrock static ses_walk_action_t
ses_fill_nodes(ses_node_t * np,void * unused)6826316Seschrock ses_fill_nodes(ses_node_t *np, void *unused)
6836316Seschrock {
6846316Seschrock np->sn_snapshot->ss_nodes[np->sn_id] = np;
6856316Seschrock
6866316Seschrock return (SES_WALK_ACTION_CONTINUE);
6876316Seschrock }
6886316Seschrock
6896316Seschrock /*
6906316Seschrock * Given an ID returned by ses_node_id(), lookup and return the corresponding
6916316Seschrock * node in the snapshot. If the snapshot generation count has changed, then
6926316Seschrock * return failure.
6936316Seschrock */
6946316Seschrock ses_node_t *
ses_node_lookup(ses_snap_t * sp,uint64_t id)6956316Seschrock ses_node_lookup(ses_snap_t *sp, uint64_t id)
6966316Seschrock {
6976316Seschrock uint32_t gen = (id >> 32);
6986316Seschrock uint32_t idx = (id & 0xFFFFFFFF);
6996316Seschrock
7006316Seschrock if (sp->ss_generation != gen) {
7016316Seschrock (void) ses_set_errno(ESES_CHANGED);
7026316Seschrock return (NULL);
7036316Seschrock }
7046316Seschrock
7056316Seschrock if (idx >= sp->ss_n_nodes) {
7066316Seschrock (void) ses_error(ESES_BAD_NODE,
7076316Seschrock "no such node in snapshot");
7086316Seschrock return (NULL);
7096316Seschrock }
7106316Seschrock
7116316Seschrock /*
7126316Seschrock * If this is our first lookup attempt, construct the array for fast
7136316Seschrock * lookups.
7146316Seschrock */
7156316Seschrock if (sp->ss_nodes == NULL) {
7166316Seschrock if ((sp->ss_nodes = ses_zalloc(
7176316Seschrock sp->ss_n_nodes * sizeof (void *))) == NULL)
7186316Seschrock return (NULL);
7196316Seschrock
7206316Seschrock (void) ses_walk(sp, ses_fill_nodes, NULL);
7216316Seschrock }
7226316Seschrock
7236316Seschrock if (sp->ss_nodes[idx] == NULL)
7246316Seschrock (void) ses_error(ESES_BAD_NODE,
7256316Seschrock "no such node in snapshot");
7266316Seschrock return (sp->ss_nodes[idx]);
7276316Seschrock }
728