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 * 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 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 * 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 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 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 * 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 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 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 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 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 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 * 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 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 * 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 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 647275c9da8Seschrock ses_snap_generation(ses_snap_t *sp) 648275c9da8Seschrock { 649275c9da8Seschrock return (sp->ss_generation); 650275c9da8Seschrock } 651275c9da8Seschrock 652275c9da8Seschrock static ses_walk_action_t 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 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 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 * 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