1275c9da8Seschrock /*
2275c9da8Seschrock * CDDL HEADER START
3275c9da8Seschrock *
4275c9da8Seschrock * The contents of this file are subject to the terms of the
5275c9da8Seschrock * Common Development and Distribution License (the "License").
6275c9da8Seschrock * You may not use this file except in compliance with the License.
7275c9da8Seschrock *
8275c9da8Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9275c9da8Seschrock * or http://www.opensolaris.org/os/licensing.
10275c9da8Seschrock * See the License for the specific language governing permissions
11275c9da8Seschrock * and limitations under the License.
12275c9da8Seschrock *
13275c9da8Seschrock * When distributing Covered Code, include this CDDL HEADER in each
14275c9da8Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15275c9da8Seschrock * If applicable, add the following below this CDDL HEADER, with the
16275c9da8Seschrock * fields enclosed by brackets "[]" replaced with your own identifying
17275c9da8Seschrock * information: Portions Copyright [yyyy] [name of copyright owner]
18275c9da8Seschrock *
19275c9da8Seschrock * CDDL HEADER END
20275c9da8Seschrock */
21275c9da8Seschrock
22275c9da8Seschrock /*
23*ac88567aSHyon Kim * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24275c9da8Seschrock */
25275c9da8Seschrock
26275c9da8Seschrock #include <scsi/libses.h>
27275c9da8Seschrock #include "ses_impl.h"
28275c9da8Seschrock
29275c9da8Seschrock ses_snap_page_t *
ses_snap_find_page(ses_snap_t * sp,ses2_diag_page_t page,boolean_t ctl)30275c9da8Seschrock ses_snap_find_page(ses_snap_t *sp, ses2_diag_page_t page, boolean_t ctl)
31275c9da8Seschrock {
32275c9da8Seschrock ses_snap_page_t *pp;
33275c9da8Seschrock
34275c9da8Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next)
35275c9da8Seschrock if (pp->ssp_num == page && pp->ssp_control == ctl &&
36275c9da8Seschrock (pp->ssp_len > 0 || pp->ssp_control))
37275c9da8Seschrock return (pp);
38275c9da8Seschrock
39275c9da8Seschrock return (NULL);
40275c9da8Seschrock }
41275c9da8Seschrock
42275c9da8Seschrock static int
grow_snap_page(ses_snap_page_t * pp,size_t min)43275c9da8Seschrock grow_snap_page(ses_snap_page_t *pp, size_t min)
44275c9da8Seschrock {
45275c9da8Seschrock uint8_t *newbuf;
46275c9da8Seschrock
47275c9da8Seschrock if (min == 0 || min < pp->ssp_alloc)
48275c9da8Seschrock min = pp->ssp_alloc * 2;
49275c9da8Seschrock
50275c9da8Seschrock if ((newbuf = ses_realloc(pp->ssp_page, min)) == NULL)
51275c9da8Seschrock return (-1);
52275c9da8Seschrock
53275c9da8Seschrock pp->ssp_page = newbuf;
54275c9da8Seschrock pp->ssp_alloc = min;
55275c9da8Seschrock
56275c9da8Seschrock bzero(newbuf + pp->ssp_len, pp->ssp_alloc - pp->ssp_len);
57275c9da8Seschrock
58275c9da8Seschrock return (0);
59275c9da8Seschrock }
60275c9da8Seschrock
61275c9da8Seschrock static ses_snap_page_t *
alloc_snap_page(void)62275c9da8Seschrock alloc_snap_page(void)
63275c9da8Seschrock {
64275c9da8Seschrock ses_snap_page_t *pp;
65275c9da8Seschrock
66275c9da8Seschrock if ((pp = ses_zalloc(sizeof (ses_snap_page_t))) == NULL)
67275c9da8Seschrock return (NULL);
68275c9da8Seschrock
69275c9da8Seschrock if ((pp->ssp_page = ses_zalloc(SES2_MIN_DIAGPAGE_ALLOC)) == NULL) {
70275c9da8Seschrock ses_free(pp);
71275c9da8Seschrock return (NULL);
72275c9da8Seschrock }
73275c9da8Seschrock
74275c9da8Seschrock pp->ssp_num = -1;
75275c9da8Seschrock pp->ssp_alloc = SES2_MIN_DIAGPAGE_ALLOC;
76275c9da8Seschrock
77275c9da8Seschrock return (pp);
78275c9da8Seschrock }
79275c9da8Seschrock
80275c9da8Seschrock static void
free_snap_page(ses_snap_page_t * pp)81275c9da8Seschrock free_snap_page(ses_snap_page_t *pp)
82275c9da8Seschrock {
83275c9da8Seschrock if (pp == NULL)
84275c9da8Seschrock return;
85275c9da8Seschrock
86275c9da8Seschrock if (pp->ssp_mmap_base)
87275c9da8Seschrock (void) munmap(pp->ssp_mmap_base, pp->ssp_mmap_len);
88275c9da8Seschrock else
89275c9da8Seschrock ses_free(pp->ssp_page);
90275c9da8Seschrock ses_free(pp);
91275c9da8Seschrock }
92275c9da8Seschrock
93275c9da8Seschrock static void
free_all_snap_pages(ses_snap_t * sp)94275c9da8Seschrock free_all_snap_pages(ses_snap_t *sp)
95275c9da8Seschrock {
96275c9da8Seschrock ses_snap_page_t *pp, *np;
97275c9da8Seschrock
98275c9da8Seschrock for (pp = sp->ss_pages; pp != NULL; pp = np) {
99275c9da8Seschrock np = pp->ssp_next;
100275c9da8Seschrock free_snap_page(pp);
101275c9da8Seschrock }
102275c9da8Seschrock
103275c9da8Seschrock sp->ss_pages = NULL;
104275c9da8Seschrock }
105275c9da8Seschrock
106275c9da8Seschrock /*
107275c9da8Seschrock * Grow (if needed) the control page buffer, fill in the page code, page
108275c9da8Seschrock * length, and generation count, and return a pointer to the page. The
109275c9da8Seschrock * caller is responsible for filling in the rest of the page data. If 'unique'
110275c9da8Seschrock * is specified, then a new page instance is created instead of sharing the
111275c9da8Seschrock * current one.
112275c9da8Seschrock */
113275c9da8Seschrock ses_snap_page_t *
ses_snap_ctl_page(ses_snap_t * sp,ses2_diag_page_t page,size_t dlen,boolean_t unique)114275c9da8Seschrock ses_snap_ctl_page(ses_snap_t *sp, ses2_diag_page_t page, size_t dlen,
115275c9da8Seschrock boolean_t unique)
116275c9da8Seschrock {
117275c9da8Seschrock ses_target_t *tp = sp->ss_target;
118275c9da8Seschrock spc3_diag_page_impl_t *pip;
119275c9da8Seschrock ses_snap_page_t *pp, *up, **loc;
120275c9da8Seschrock ses_pagedesc_t *dp;
121275c9da8Seschrock size_t len;
122275c9da8Seschrock
123275c9da8Seschrock pp = ses_snap_find_page(sp, page, B_TRUE);
124275c9da8Seschrock if (pp == NULL) {
125275c9da8Seschrock (void) ses_set_errno(ESES_NOTSUP);
126275c9da8Seschrock return (NULL);
127275c9da8Seschrock }
128275c9da8Seschrock
129275c9da8Seschrock if (pp->ssp_initialized && !unique)
130275c9da8Seschrock return (pp);
131275c9da8Seschrock
132275c9da8Seschrock if (unique) {
133275c9da8Seschrock /*
134275c9da8Seschrock * The user has requested a unique instance of the page. Create
135275c9da8Seschrock * a new ses_snap_page_t instance and chain it off the
136275c9da8Seschrock * 'ssp_instances' list of the master page. These must be
137275c9da8Seschrock * appended to the end of the chain, as the order of operations
138275c9da8Seschrock * may be important (i.e. microcode download).
139275c9da8Seschrock */
140275c9da8Seschrock if ((up = alloc_snap_page()) == NULL)
141275c9da8Seschrock return (NULL);
142275c9da8Seschrock
143275c9da8Seschrock up->ssp_num = pp->ssp_num;
144275c9da8Seschrock up->ssp_control = B_TRUE;
145275c9da8Seschrock
146275c9da8Seschrock for (loc = &pp->ssp_unique; *loc != NULL;
147275c9da8Seschrock loc = &(*loc)->ssp_next)
148275c9da8Seschrock ;
149275c9da8Seschrock
150275c9da8Seschrock *loc = up;
151275c9da8Seschrock pp = up;
152275c9da8Seschrock }
153275c9da8Seschrock
154275c9da8Seschrock dp = ses_get_pagedesc(tp, page, SES_PAGE_CTL);
155275c9da8Seschrock ASSERT(dp != NULL);
156275c9da8Seschrock
157275c9da8Seschrock len = dp->spd_ctl_len(sp->ss_n_elem, page, dlen);
158*ac88567aSHyon Kim if (pp->ssp_alloc < len && grow_snap_page(pp, len) != 0)
159275c9da8Seschrock return (NULL);
160275c9da8Seschrock pp->ssp_len = len;
161275c9da8Seschrock bzero(pp->ssp_page, len);
162275c9da8Seschrock pp->ssp_initialized = B_TRUE;
163275c9da8Seschrock
164275c9da8Seschrock pip = (spc3_diag_page_impl_t *)pp->ssp_page;
165275c9da8Seschrock pip->sdpi_page_code = (uint8_t)page;
166275c9da8Seschrock SCSI_WRITE16(&pip->sdpi_page_length,
167275c9da8Seschrock len - offsetof(spc3_diag_page_impl_t, sdpi_data[0]));
168275c9da8Seschrock if (dp->spd_gcoff != -1)
169275c9da8Seschrock SCSI_WRITE32((uint8_t *)pip + dp->spd_gcoff, sp->ss_generation);
170275c9da8Seschrock
171275c9da8Seschrock return (pp);
172275c9da8Seschrock }
173275c9da8Seschrock
174275c9da8Seschrock static int
read_status_page(ses_snap_t * sp,ses2_diag_page_t page)175275c9da8Seschrock read_status_page(ses_snap_t *sp, ses2_diag_page_t page)
176275c9da8Seschrock {
177275c9da8Seschrock libscsi_action_t *ap;
178275c9da8Seschrock ses_snap_page_t *pp;
179275c9da8Seschrock ses_target_t *tp;
180275c9da8Seschrock spc3_diag_page_impl_t *pip;
181275c9da8Seschrock spc3_receive_diagnostic_results_cdb_t *cp;
182275c9da8Seschrock uint_t flags;
183275c9da8Seschrock uint8_t *buf;
184275c9da8Seschrock size_t alloc;
185275c9da8Seschrock uint_t retries = 0;
186275c9da8Seschrock ses2_diag_page_t retpage;
187275c9da8Seschrock
188275c9da8Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next)
189275c9da8Seschrock if (pp->ssp_num == page && !pp->ssp_control)
190275c9da8Seschrock break;
191275c9da8Seschrock
192275c9da8Seschrock /*
193275c9da8Seschrock * No matching page. Since the page number is not under consumer or
194275c9da8Seschrock * device control, this must be a bug.
195275c9da8Seschrock */
196275c9da8Seschrock ASSERT(pp != NULL);
197275c9da8Seschrock
198275c9da8Seschrock tp = sp->ss_target;
199275c9da8Seschrock
200275c9da8Seschrock flags = LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE |
201275c9da8Seschrock LIBSCSI_AF_RQSENSE;
202275c9da8Seschrock
203275c9da8Seschrock again:
204275c9da8Seschrock ap = libscsi_action_alloc(tp->st_scsi_hdl,
205275c9da8Seschrock SPC3_CMD_RECEIVE_DIAGNOSTIC_RESULTS, flags, pp->ssp_page,
206275c9da8Seschrock pp->ssp_alloc);
207275c9da8Seschrock
208275c9da8Seschrock if (ap == NULL)
209275c9da8Seschrock return (ses_libscsi_error(tp->st_scsi_hdl, "failed to "
210275c9da8Seschrock "allocate SCSI action"));
211275c9da8Seschrock
212275c9da8Seschrock cp = (spc3_receive_diagnostic_results_cdb_t *)
213275c9da8Seschrock libscsi_action_get_cdb(ap);
214275c9da8Seschrock
215275c9da8Seschrock cp->rdrc_page_code = pp->ssp_num;
216275c9da8Seschrock cp->rdrc_pcv = 1;
217275c9da8Seschrock SCSI_WRITE16(&cp->rdrc_allocation_length,
218275c9da8Seschrock MIN(pp->ssp_alloc, UINT16_MAX));
219275c9da8Seschrock
220275c9da8Seschrock if (libscsi_exec(ap, tp->st_target) != 0) {
221275c9da8Seschrock libscsi_action_free(ap);
222275c9da8Seschrock return (ses_libscsi_error(tp->st_scsi_hdl,
223275c9da8Seschrock "receive diagnostic results failed"));
224275c9da8Seschrock }
225275c9da8Seschrock
226275c9da8Seschrock if (libscsi_action_get_status(ap) != 0) {
227275c9da8Seschrock (void) ses_scsi_error(ap,
228275c9da8Seschrock "receive diagnostic results failed");
229275c9da8Seschrock libscsi_action_free(ap);
230275c9da8Seschrock return (-1);
231275c9da8Seschrock }
232275c9da8Seschrock
233275c9da8Seschrock (void) libscsi_action_get_buffer(ap, &buf, &alloc, &pp->ssp_len);
234275c9da8Seschrock libscsi_action_free(ap);
235275c9da8Seschrock
236275c9da8Seschrock ASSERT(buf == pp->ssp_page);
237275c9da8Seschrock ASSERT(alloc == pp->ssp_alloc);
238275c9da8Seschrock
239*ac88567aSHyon Kim if (pp->ssp_alloc - pp->ssp_len < 0x80 && pp->ssp_alloc < UINT16_MAX) {
240275c9da8Seschrock bzero(pp->ssp_page, pp->ssp_len);
241275c9da8Seschrock pp->ssp_len = 0;
242275c9da8Seschrock if (grow_snap_page(pp, 0) != 0)
243275c9da8Seschrock return (-1);
244275c9da8Seschrock goto again;
245275c9da8Seschrock }
246275c9da8Seschrock
247*ac88567aSHyon Kim if (pp->ssp_len < offsetof(spc3_diag_page_impl_t, sdpi_data)) {
248*ac88567aSHyon Kim bzero(pp->ssp_page, pp->ssp_len);
249*ac88567aSHyon Kim pp->ssp_len = 0;
250*ac88567aSHyon Kim return (ses_error(ESES_BAD_RESPONSE, "target returned "
251*ac88567aSHyon Kim "truncated page 0x%x (length %d)", page, pp->ssp_len));
252*ac88567aSHyon Kim }
253*ac88567aSHyon Kim
254275c9da8Seschrock pip = (spc3_diag_page_impl_t *)buf;
255275c9da8Seschrock
256275c9da8Seschrock if (pip->sdpi_page_code == page)
257275c9da8Seschrock return (0);
258275c9da8Seschrock
259275c9da8Seschrock retpage = pip->sdpi_page_code;
260275c9da8Seschrock
261275c9da8Seschrock bzero(pp->ssp_page, pp->ssp_len);
262275c9da8Seschrock pp->ssp_len = 0;
263275c9da8Seschrock
264275c9da8Seschrock if (retpage == SES2_DIAGPAGE_ENCLOSURE_BUSY) {
265275c9da8Seschrock if (++retries > LIBSES_MAX_BUSY_RETRIES)
266275c9da8Seschrock return (ses_error(ESES_BUSY, "too many "
267275c9da8Seschrock "enclosure busy responses for page 0x%x", page));
268275c9da8Seschrock goto again;
269275c9da8Seschrock }
270275c9da8Seschrock
271275c9da8Seschrock return (ses_error(ESES_BAD_RESPONSE, "target returned page 0x%x "
272275c9da8Seschrock "instead of the requested page 0x%x", retpage, page));
273275c9da8Seschrock }
274275c9da8Seschrock
275275c9da8Seschrock static int
send_control_page(ses_snap_t * sp,ses_snap_page_t * pp)276275c9da8Seschrock send_control_page(ses_snap_t *sp, ses_snap_page_t *pp)
277275c9da8Seschrock {
278275c9da8Seschrock ses_target_t *tp;
279275c9da8Seschrock libscsi_action_t *ap;
280275c9da8Seschrock spc3_send_diagnostic_cdb_t *cp;
281275c9da8Seschrock uint_t flags;
282275c9da8Seschrock
283275c9da8Seschrock tp = sp->ss_target;
284275c9da8Seschrock
285275c9da8Seschrock flags = LIBSCSI_AF_WRITE | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE |
286275c9da8Seschrock LIBSCSI_AF_RQSENSE;
287275c9da8Seschrock
288275c9da8Seschrock ap = libscsi_action_alloc(tp->st_scsi_hdl, SPC3_CMD_SEND_DIAGNOSTIC,
289275c9da8Seschrock flags, pp->ssp_page, pp->ssp_len);
290275c9da8Seschrock
291275c9da8Seschrock if (ap == NULL)
292275c9da8Seschrock return (ses_libscsi_error(tp->st_scsi_hdl, "failed to "
293275c9da8Seschrock "allocate SCSI action"));
294275c9da8Seschrock
295275c9da8Seschrock cp = (spc3_send_diagnostic_cdb_t *)libscsi_action_get_cdb(ap);
296275c9da8Seschrock
297275c9da8Seschrock cp->sdc_pf = 1;
298275c9da8Seschrock SCSI_WRITE16(&cp->sdc_parameter_list_length, pp->ssp_len);
299275c9da8Seschrock
300275c9da8Seschrock if (libscsi_exec(ap, tp->st_target) != 0) {
301275c9da8Seschrock libscsi_action_free(ap);
302275c9da8Seschrock return (ses_libscsi_error(tp->st_scsi_hdl,
303275c9da8Seschrock "SEND DIAGNOSTIC command failed for page 0x%x",
304275c9da8Seschrock pp->ssp_num));
305275c9da8Seschrock }
306275c9da8Seschrock
307275c9da8Seschrock if (libscsi_action_get_status(ap) != 0) {
308275c9da8Seschrock (void) ses_scsi_error(ap, "SEND DIAGNOSTIC command "
309275c9da8Seschrock "failed for page 0x%x", pp->ssp_num);
310275c9da8Seschrock libscsi_action_free(ap);
311275c9da8Seschrock return (-1);
312275c9da8Seschrock }
313275c9da8Seschrock
314275c9da8Seschrock libscsi_action_free(ap);
315275c9da8Seschrock
316275c9da8Seschrock return (0);
317275c9da8Seschrock }
318275c9da8Seschrock
319275c9da8Seschrock static int
pages_skel_create(ses_snap_t * sp)320275c9da8Seschrock pages_skel_create(ses_snap_t *sp)
321275c9da8Seschrock {
322275c9da8Seschrock ses_snap_page_t *pp, *np;
323275c9da8Seschrock ses_target_t *tp = sp->ss_target;
324275c9da8Seschrock ses2_supported_ses_diag_page_impl_t *pip;
325275c9da8Seschrock ses2_diag_page_t page;
326275c9da8Seschrock size_t npages;
327275c9da8Seschrock size_t pagelen;
328275c9da8Seschrock off_t i;
329275c9da8Seschrock
330275c9da8Seschrock ASSERT(sp->ss_pages == NULL);
331275c9da8Seschrock
332275c9da8Seschrock if ((pp = alloc_snap_page()) == NULL)
333275c9da8Seschrock return (-1);
334275c9da8Seschrock
335275c9da8Seschrock pp->ssp_num = SES2_DIAGPAGE_SUPPORTED_PAGES;
336275c9da8Seschrock pp->ssp_control = B_FALSE;
337275c9da8Seschrock sp->ss_pages = pp;
338275c9da8Seschrock
339275c9da8Seschrock if (read_status_page(sp, SES2_DIAGPAGE_SUPPORTED_PAGES) != 0) {
340275c9da8Seschrock free_snap_page(pp);
341275c9da8Seschrock sp->ss_pages = NULL;
342275c9da8Seschrock return (-1);
343275c9da8Seschrock }
344275c9da8Seschrock
345275c9da8Seschrock pip = pp->ssp_page;
346275c9da8Seschrock pagelen = pp->ssp_len;
347275c9da8Seschrock
348275c9da8Seschrock npages = SCSI_READ16(&pip->sssdpi_page_length);
349275c9da8Seschrock
350275c9da8Seschrock for (i = 0; i < npages; i++) {
351275c9da8Seschrock if (!SES_WITHIN_PAGE(pip->sssdpi_pages + i, 1, pip,
352275c9da8Seschrock pagelen))
353275c9da8Seschrock break;
354275c9da8Seschrock
355275c9da8Seschrock page = (ses2_diag_page_t)pip->sssdpi_pages[i];
356275c9da8Seschrock /*
357275c9da8Seschrock * Skip the page we already added during the bootstrap.
358275c9da8Seschrock */
359275c9da8Seschrock if (page == SES2_DIAGPAGE_SUPPORTED_PAGES)
360275c9da8Seschrock continue;
361275c9da8Seschrock /*
362275c9da8Seschrock * The end of the page list may be padded with zeros; ignore
363275c9da8Seschrock * them all.
364275c9da8Seschrock */
365275c9da8Seschrock if (page == 0 && i > 0)
366275c9da8Seschrock break;
367275c9da8Seschrock if ((np = alloc_snap_page()) == NULL) {
368275c9da8Seschrock free_all_snap_pages(sp);
369275c9da8Seschrock return (-1);
370275c9da8Seschrock }
371275c9da8Seschrock np->ssp_num = page;
372275c9da8Seschrock pp->ssp_next = np;
373275c9da8Seschrock pp = np;
374275c9da8Seschrock
375275c9da8Seschrock /*
376275c9da8Seschrock * Allocate a control page as well, if we can use it.
377275c9da8Seschrock */
378275c9da8Seschrock if (ses_get_pagedesc(tp, page, SES_PAGE_CTL) != NULL) {
379275c9da8Seschrock if ((np = alloc_snap_page()) == NULL) {
380275c9da8Seschrock free_all_snap_pages(sp);
381275c9da8Seschrock return (-1);
382275c9da8Seschrock }
383275c9da8Seschrock np->ssp_num = page;
384275c9da8Seschrock np->ssp_control = B_TRUE;
385275c9da8Seschrock pp->ssp_next = np;
386275c9da8Seschrock pp = np;
387275c9da8Seschrock }
388275c9da8Seschrock }
389275c9da8Seschrock
390275c9da8Seschrock return (0);
391275c9da8Seschrock }
392275c9da8Seschrock
393275c9da8Seschrock static void
ses_snap_free(ses_snap_t * sp)394275c9da8Seschrock ses_snap_free(ses_snap_t *sp)
395275c9da8Seschrock {
396275c9da8Seschrock free_all_snap_pages(sp);
397275c9da8Seschrock ses_node_teardown(sp->ss_root);
398275c9da8Seschrock ses_free(sp->ss_nodes);
399275c9da8Seschrock ses_free(sp);
400275c9da8Seschrock }
401275c9da8Seschrock
402275c9da8Seschrock static void
ses_snap_rele_unlocked(ses_snap_t * sp)403275c9da8Seschrock ses_snap_rele_unlocked(ses_snap_t *sp)
404275c9da8Seschrock {
405275c9da8Seschrock ses_target_t *tp = sp->ss_target;
406275c9da8Seschrock
407275c9da8Seschrock if (--sp->ss_refcnt != 0)
408275c9da8Seschrock return;
409275c9da8Seschrock
410275c9da8Seschrock if (sp->ss_next != NULL)
411275c9da8Seschrock sp->ss_next->ss_prev = sp->ss_prev;
412275c9da8Seschrock
413275c9da8Seschrock if (sp->ss_prev != NULL)
414275c9da8Seschrock sp->ss_prev->ss_next = sp->ss_next;
415275c9da8Seschrock else
416275c9da8Seschrock tp->st_snapshots = sp->ss_next;
417275c9da8Seschrock
418275c9da8Seschrock ses_snap_free(sp);
419275c9da8Seschrock }
420275c9da8Seschrock
421275c9da8Seschrock ses_snap_t *
ses_snap_hold(ses_target_t * tp)422275c9da8Seschrock ses_snap_hold(ses_target_t *tp)
423275c9da8Seschrock {
424275c9da8Seschrock ses_snap_t *sp;
425275c9da8Seschrock
426275c9da8Seschrock (void) pthread_mutex_lock(&tp->st_lock);
427275c9da8Seschrock sp = tp->st_snapshots;
428275c9da8Seschrock sp->ss_refcnt++;
429275c9da8Seschrock (void) pthread_mutex_unlock(&tp->st_lock);
430275c9da8Seschrock
431275c9da8Seschrock return (sp);
432275c9da8Seschrock }
433275c9da8Seschrock
434275c9da8Seschrock void
ses_snap_rele(ses_snap_t * sp)435275c9da8Seschrock ses_snap_rele(ses_snap_t *sp)
436275c9da8Seschrock {
437275c9da8Seschrock ses_target_t *tp = sp->ss_target;
438275c9da8Seschrock
439275c9da8Seschrock (void) pthread_mutex_lock(&tp->st_lock);
440275c9da8Seschrock ses_snap_rele_unlocked(sp);
441275c9da8Seschrock (void) pthread_mutex_unlock(&tp->st_lock);
442275c9da8Seschrock }
443275c9da8Seschrock
444275c9da8Seschrock ses_snap_t *
ses_snap_new(ses_target_t * tp)445275c9da8Seschrock ses_snap_new(ses_target_t *tp)
446275c9da8Seschrock {
447275c9da8Seschrock ses_snap_t *sp;
448275c9da8Seschrock ses_snap_page_t *pp;
449275c9da8Seschrock uint32_t gc;
450275c9da8Seschrock uint_t retries = 0;
451275c9da8Seschrock ses_pagedesc_t *dp;
452275c9da8Seschrock size_t pages, pagesize, pagelen;
453275c9da8Seschrock char *scratch;
454*ac88567aSHyon Kim boolean_t simple;
455275c9da8Seschrock
456275c9da8Seschrock if ((sp = ses_zalloc(sizeof (ses_snap_t))) == NULL)
457275c9da8Seschrock return (NULL);
458275c9da8Seschrock
459275c9da8Seschrock sp->ss_target = tp;
460275c9da8Seschrock
461275c9da8Seschrock again:
462275c9da8Seschrock free_all_snap_pages(sp);
463275c9da8Seschrock
464275c9da8Seschrock if (pages_skel_create(sp) != 0) {
465275c9da8Seschrock free(sp);
466275c9da8Seschrock return (NULL);
467275c9da8Seschrock }
468275c9da8Seschrock
469275c9da8Seschrock sp->ss_generation = (uint32_t)-1;
470275c9da8Seschrock sp->ss_time = gethrtime();
471275c9da8Seschrock
472*ac88567aSHyon Kim /*
473*ac88567aSHyon Kim * First check for the short enclosure status diagnostic page and
474*ac88567aSHyon Kim * determine if this is a simple subenclosure or not.
475*ac88567aSHyon Kim */
476*ac88567aSHyon Kim simple = B_FALSE;
477*ac88567aSHyon Kim for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
478*ac88567aSHyon Kim if (pp->ssp_num == SES2_DIAGPAGE_SHORT_STATUS)
479*ac88567aSHyon Kim simple = B_TRUE;
480*ac88567aSHyon Kim }
481*ac88567aSHyon Kim
482275c9da8Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
483275c9da8Seschrock /*
484275c9da8Seschrock * We skip all of:
485275c9da8Seschrock *
486275c9da8Seschrock * - Control pages
487275c9da8Seschrock * - Pages we've already filled in
488275c9da8Seschrock * - Pages we don't understand (those with no descriptor)
489275c9da8Seschrock */
490275c9da8Seschrock if (pp->ssp_len > 0 || pp->ssp_control)
491275c9da8Seschrock continue;
492275c9da8Seschrock if ((dp = ses_get_pagedesc(tp, pp->ssp_num,
493275c9da8Seschrock SES_PAGE_DIAG)) == NULL)
494275c9da8Seschrock continue;
495275c9da8Seschrock
496*ac88567aSHyon Kim if (read_status_page(sp, pp->ssp_num) != 0) {
497*ac88567aSHyon Kim /*
498*ac88567aSHyon Kim * If this page is required, and this is not a simple
499*ac88567aSHyon Kim * subenclosure, then fail the entire snapshot.
500*ac88567aSHyon Kim */
501*ac88567aSHyon Kim if (dp->spd_req == SES_REQ_MANDATORY_ALL ||
502*ac88567aSHyon Kim (dp->spd_req == SES_REQ_MANDATORY_STANDARD &&
503*ac88567aSHyon Kim !simple)) {
504*ac88567aSHyon Kim ses_snap_free(sp);
505*ac88567aSHyon Kim return (NULL);
506*ac88567aSHyon Kim }
507*ac88567aSHyon Kim
508275c9da8Seschrock continue;
509*ac88567aSHyon Kim }
510275c9da8Seschrock
511275c9da8Seschrock /*
512275c9da8Seschrock * If the generation code has changed, we don't have a valid
513275c9da8Seschrock * snapshot. Start over.
514275c9da8Seschrock */
515275c9da8Seschrock if (dp->spd_gcoff != -1 &&
516275c9da8Seschrock dp->spd_gcoff + 4 <= pp->ssp_len) {
517275c9da8Seschrock gc = SCSI_READ32((uint8_t *)pp->ssp_page +
518275c9da8Seschrock dp->spd_gcoff);
519275c9da8Seschrock if (sp->ss_generation == (uint32_t)-1) {
520275c9da8Seschrock sp->ss_generation = gc;
521275c9da8Seschrock } else if (sp->ss_generation != gc) {
522275c9da8Seschrock if (++retries > LIBSES_MAX_GC_RETRIES) {
523275c9da8Seschrock (void) ses_error(ESES_TOOMUCHCHANGE,
524275c9da8Seschrock "too many generation count "
525275c9da8Seschrock "mismatches: page 0x%x gc %u "
526275c9da8Seschrock "previous page %u", dp->spd_gcoff,
527275c9da8Seschrock gc, sp->ss_generation);
528275c9da8Seschrock ses_snap_free((ses_snap_t *)sp);
529275c9da8Seschrock return (NULL);
530275c9da8Seschrock }
531275c9da8Seschrock goto again;
532275c9da8Seschrock }
533275c9da8Seschrock }
534275c9da8Seschrock }
535275c9da8Seschrock
536275c9da8Seschrock /*
537275c9da8Seschrock * The LIBSES_TRUNCATE environment variable is a debugging tool which,
538275c9da8Seschrock * if set, randomly truncates all pages (except
539275c9da8Seschrock * SES2_DIAGPAGE_SUPPORTED_PAGES). In order to be truly evil, we
540275c9da8Seschrock * mmap() each page with enough space after it so we can move the data
541275c9da8Seschrock * up to the end of a page and unmap the following page so that any
542275c9da8Seschrock * attempt to read past the end of the page results in a segfault.
543275c9da8Seschrock */
544275c9da8Seschrock if (sp->ss_target->st_truncate) {
545275c9da8Seschrock pagesize = PAGESIZE;
546275c9da8Seschrock
547275c9da8Seschrock /*
548275c9da8Seschrock * Count the maximum number of pages we will need and allocate
549275c9da8Seschrock * the necessary space.
550275c9da8Seschrock */
551275c9da8Seschrock pages = 0;
552275c9da8Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
553275c9da8Seschrock if (pp->ssp_control || pp->ssp_len == 0)
554275c9da8Seschrock continue;
555275c9da8Seschrock
556275c9da8Seschrock pages += (P2ROUNDUP(pp->ssp_len, pagesize) /
557275c9da8Seschrock pagesize) + 1;
558275c9da8Seschrock }
559275c9da8Seschrock
560275c9da8Seschrock if ((scratch = mmap(NULL, pages * pagesize,
561275c9da8Seschrock PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
562275c9da8Seschrock -1, 0)) == MAP_FAILED) {
563275c9da8Seschrock (void) ses_error(ESES_NOMEM,
564275c9da8Seschrock "failed to mmap() pages for truncation");
565275c9da8Seschrock ses_snap_free(sp);
566275c9da8Seschrock return (NULL);
567275c9da8Seschrock }
568275c9da8Seschrock
569275c9da8Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
570275c9da8Seschrock if (pp->ssp_control || pp->ssp_len == 0)
571275c9da8Seschrock continue;
572275c9da8Seschrock
573275c9da8Seschrock pages = P2ROUNDUP(pp->ssp_len, pagesize) / pagesize;
574275c9da8Seschrock pp->ssp_mmap_base = scratch;
575275c9da8Seschrock pp->ssp_mmap_len = pages * pagesize;
576275c9da8Seschrock
577275c9da8Seschrock pagelen = lrand48() % pp->ssp_len;
578275c9da8Seschrock (void) memcpy(pp->ssp_mmap_base + pp->ssp_mmap_len -
579275c9da8Seschrock pagelen, pp->ssp_page, pagelen);
580275c9da8Seschrock ses_free(pp->ssp_page);
581275c9da8Seschrock pp->ssp_page = pp->ssp_mmap_base + pp->ssp_mmap_len -
582275c9da8Seschrock pagelen;
583275c9da8Seschrock pp->ssp_len = pagelen;
584275c9da8Seschrock
585275c9da8Seschrock (void) munmap(pp->ssp_mmap_base + pages * pagesize,
586275c9da8Seschrock pagesize);
587275c9da8Seschrock scratch += (pages + 1) * pagesize;
588275c9da8Seschrock }
589275c9da8Seschrock }
590275c9da8Seschrock
591275c9da8Seschrock
592275c9da8Seschrock if (ses_fill_snap(sp) != 0) {
593275c9da8Seschrock ses_snap_free(sp);
594275c9da8Seschrock return (NULL);
595275c9da8Seschrock }
596275c9da8Seschrock
597275c9da8Seschrock (void) pthread_mutex_lock(&tp->st_lock);
598275c9da8Seschrock if (tp->st_snapshots != NULL)
599275c9da8Seschrock ses_snap_rele_unlocked(tp->st_snapshots);
600275c9da8Seschrock sp->ss_next = tp->st_snapshots;
601275c9da8Seschrock if (tp->st_snapshots != NULL)
602275c9da8Seschrock tp->st_snapshots->ss_prev = sp;
603275c9da8Seschrock tp->st_snapshots = sp;
604275c9da8Seschrock sp->ss_refcnt = 2;
605275c9da8Seschrock (void) pthread_mutex_unlock(&tp->st_lock);
606275c9da8Seschrock
607275c9da8Seschrock return (sp);
608275c9da8Seschrock }
609275c9da8Seschrock
610275c9da8Seschrock int
ses_snap_do_ctl(ses_snap_t * sp)611275c9da8Seschrock ses_snap_do_ctl(ses_snap_t *sp)
612275c9da8Seschrock {
613275c9da8Seschrock ses_snap_page_t *pp, *up;
614275c9da8Seschrock int ret = -1;
615275c9da8Seschrock
616275c9da8Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
617275c9da8Seschrock if (!pp->ssp_control)
618275c9da8Seschrock continue;
619275c9da8Seschrock
620275c9da8Seschrock if (pp->ssp_initialized && send_control_page(sp, pp) != 0)
621275c9da8Seschrock goto error;
622275c9da8Seschrock
623275c9da8Seschrock for (up = pp->ssp_unique; up != NULL; up = up->ssp_next) {
624275c9da8Seschrock if (send_control_page(sp, up) != 0)
625275c9da8Seschrock goto error;
626275c9da8Seschrock }
627275c9da8Seschrock }
628275c9da8Seschrock
629275c9da8Seschrock ret = 0;
630275c9da8Seschrock error:
631275c9da8Seschrock for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
632275c9da8Seschrock if (!pp->ssp_control)
633275c9da8Seschrock continue;
634275c9da8Seschrock
635275c9da8Seschrock pp->ssp_initialized = B_FALSE;
636275c9da8Seschrock while ((up = pp->ssp_unique) != NULL) {
637275c9da8Seschrock pp->ssp_unique = up->ssp_next;
638275c9da8Seschrock free_snap_page(up);
639275c9da8Seschrock }
640275c9da8Seschrock }
641275c9da8Seschrock
642275c9da8Seschrock
643275c9da8Seschrock return (ret);
644275c9da8Seschrock }
645275c9da8Seschrock
646275c9da8Seschrock uint32_t
ses_snap_generation(ses_snap_t * sp)647275c9da8Seschrock ses_snap_generation(ses_snap_t *sp)
648275c9da8Seschrock {
649275c9da8Seschrock return (sp->ss_generation);
650275c9da8Seschrock }
651275c9da8Seschrock
652275c9da8Seschrock static ses_walk_action_t
ses_walk_node(ses_node_t * np,ses_walk_f func,void * arg)653275c9da8Seschrock ses_walk_node(ses_node_t *np, ses_walk_f func, void *arg)
654275c9da8Seschrock {
655275c9da8Seschrock ses_walk_action_t action;
656275c9da8Seschrock
657275c9da8Seschrock for (; np != NULL; np = ses_node_sibling(np)) {
658275c9da8Seschrock action = func(np, arg);
659275c9da8Seschrock if (action == SES_WALK_ACTION_TERMINATE)
660275c9da8Seschrock return (SES_WALK_ACTION_TERMINATE);
661275c9da8Seschrock if (action == SES_WALK_ACTION_PRUNE ||
662275c9da8Seschrock ses_node_child(np) == NULL)
663275c9da8Seschrock continue;
664275c9da8Seschrock if (ses_walk_node(ses_node_child(np), func, arg) ==
665275c9da8Seschrock SES_WALK_ACTION_TERMINATE)
666275c9da8Seschrock return (SES_WALK_ACTION_TERMINATE);
667275c9da8Seschrock }
668275c9da8Seschrock
669275c9da8Seschrock return (SES_WALK_ACTION_CONTINUE);
670275c9da8Seschrock }
671275c9da8Seschrock
672275c9da8Seschrock int
ses_walk(ses_snap_t * sp,ses_walk_f func,void * arg)673275c9da8Seschrock ses_walk(ses_snap_t *sp, ses_walk_f func, void *arg)
674275c9da8Seschrock {
675275c9da8Seschrock (void) ses_walk_node(ses_root_node(sp), func, arg);
676275c9da8Seschrock
677275c9da8Seschrock return (0);
678275c9da8Seschrock }
679275c9da8Seschrock
680275c9da8Seschrock /*ARGSUSED*/
681275c9da8Seschrock static ses_walk_action_t
ses_fill_nodes(ses_node_t * np,void * unused)682275c9da8Seschrock ses_fill_nodes(ses_node_t *np, void *unused)
683275c9da8Seschrock {
684275c9da8Seschrock np->sn_snapshot->ss_nodes[np->sn_id] = np;
685275c9da8Seschrock
686275c9da8Seschrock return (SES_WALK_ACTION_CONTINUE);
687275c9da8Seschrock }
688275c9da8Seschrock
689275c9da8Seschrock /*
690275c9da8Seschrock * Given an ID returned by ses_node_id(), lookup and return the corresponding
691275c9da8Seschrock * node in the snapshot. If the snapshot generation count has changed, then
692275c9da8Seschrock * return failure.
693275c9da8Seschrock */
694275c9da8Seschrock ses_node_t *
ses_node_lookup(ses_snap_t * sp,uint64_t id)695275c9da8Seschrock ses_node_lookup(ses_snap_t *sp, uint64_t id)
696275c9da8Seschrock {
697275c9da8Seschrock uint32_t gen = (id >> 32);
698275c9da8Seschrock uint32_t idx = (id & 0xFFFFFFFF);
699275c9da8Seschrock
700275c9da8Seschrock if (sp->ss_generation != gen) {
701275c9da8Seschrock (void) ses_set_errno(ESES_CHANGED);
702275c9da8Seschrock return (NULL);
703275c9da8Seschrock }
704275c9da8Seschrock
705275c9da8Seschrock if (idx >= sp->ss_n_nodes) {
706275c9da8Seschrock (void) ses_error(ESES_BAD_NODE,
707275c9da8Seschrock "no such node in snapshot");
708275c9da8Seschrock return (NULL);
709275c9da8Seschrock }
710275c9da8Seschrock
711275c9da8Seschrock /*
712275c9da8Seschrock * If this is our first lookup attempt, construct the array for fast
713275c9da8Seschrock * lookups.
714275c9da8Seschrock */
715275c9da8Seschrock if (sp->ss_nodes == NULL) {
716275c9da8Seschrock if ((sp->ss_nodes = ses_zalloc(
717275c9da8Seschrock sp->ss_n_nodes * sizeof (void *))) == NULL)
718275c9da8Seschrock return (NULL);
719275c9da8Seschrock
720275c9da8Seschrock (void) ses_walk(sp, ses_fill_nodes, NULL);
721275c9da8Seschrock }
722275c9da8Seschrock
723275c9da8Seschrock if (sp->ss_nodes[idx] == NULL)
724275c9da8Seschrock (void) ses_error(ESES_BAD_NODE,
725275c9da8Seschrock "no such node in snapshot");
726275c9da8Seschrock return (sp->ss_nodes[idx]);
727275c9da8Seschrock }
728