xref: /onnv-gate/usr/src/lib/scsi/libses/common/ses_snap.c (revision 12126:60364f3f65c7)
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