/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #define __NSC_GEN__ #include "nsc_dev.h" #include "../nsctl.h" /* * (Un)Freeze Module * * This module provides a means to 'freeze' a device and ensure * that no SP software has an open reference to that device. Later * the device can be 'unfrozen' and the SP software can resume * normal operations. * * This module is required because it is possible to place a virtual * volume driver (RAID-0, 1 or 5) into a state whereby it needs to be * disabled for corrective action. The (un)freeze facility provides a * method of doing this without downtime. * * A device that is frozen should be frozen on all nodes. It is the * responsibility of the management software or the user to perform * the freeze and unfreeze on the required nodes. */ extern nsc_mem_t *_nsc_local_mem; typedef struct _nsc_frz_s { struct _nsc_frz_s *next; nsc_path_t *token; char path[NSC_MAXPATH]; } _nsc_frz_t; extern int _nsc_frz_stop(char *, int *); /* forward decl */ static _nsc_frz_t *_nsc_frz_top; static nsc_def_t _nsc_frz_def[]; static kmutex_t _nsc_frz_sleep; static nsc_io_t *_nsc_frz_io; void _nsc_init_frz(void) { mutex_init(&_nsc_frz_sleep, NULL, MUTEX_DRIVER, NULL); _nsc_frz_io = nsc_register_io("frz", NSC_FREEZE_ID | NSC_FILTER, _nsc_frz_def); if (!_nsc_frz_io) cmn_err(CE_WARN, "nsctl: _nsc_init_frz: register failed"); } void _nsc_deinit_frz(void) { if (_nsc_frz_io) (void) nsc_unregister_io(_nsc_frz_io, 0); _nsc_frz_io = NULL; mutex_destroy(&_nsc_frz_sleep); } /* * int _nsc_frz_start(char *path, int *rvp) * Freeze a device * * Calling/Exit State: * Must be called from a context that can block. * Returns 0 for success, or one of the following error codes: * EINVAL - invalid 'path' argument * ENOMEM - failed to allocate memory * EALREADY - 'path' is already frozen * * Description: * Registers 'path' to be accessed through the NSC_FREEZE_ID * io module, and forces any open file descriptors for 'path' * to be re-opened as appropriate. */ int _nsc_frz_start(path, rvp) char *path; int *rvp; { _nsc_frz_t *frz, *xfrz; int rc; *rvp = 0; if (strlen(path) >= NSC_MAXPATH) return (EINVAL); frz = nsc_kmem_zalloc(sizeof (*frz), KM_SLEEP, _nsc_local_mem); if (!frz) return (ENOMEM); (void) strcpy(frz->path, path); mutex_enter(&_nsc_frz_sleep); for (xfrz = _nsc_frz_top; xfrz; xfrz = xfrz->next) if (strcmp(frz->path, xfrz->path) == 0) break; if (!xfrz) { frz->next = _nsc_frz_top; _nsc_frz_top = frz; } mutex_exit(&_nsc_frz_sleep); if (xfrz) { nsc_kmem_free(frz, sizeof (*frz)); return (EALREADY); } frz->token = nsc_register_path(path, NSC_DEVICE, _nsc_frz_io); if (!frz->token) { (void) _nsc_frz_stop(path, &rc); return (EINVAL); } return (0); } /* * int _nsc_frz_stop(char *path, int *rvp) * Unfreeze a device * * Calling/Exit State: * Must be called from a context that can block. * Returns 0 or an error code. * * Description: * Removes the path registration for the NSC_FREEZE_ID io module * and forces any re-opens as appropriate. */ int _nsc_frz_stop(path, rvp) char *path; int *rvp; { _nsc_frz_t **xfrz, *frz = NULL; int rc = 0; *rvp = 0; mutex_enter(&_nsc_frz_sleep); for (xfrz = &_nsc_frz_top; *xfrz; xfrz = &(*xfrz)->next) if (strcmp(path, (*xfrz)->path) == 0) { frz = *xfrz; break; } if (!frz) { mutex_exit(&_nsc_frz_sleep); return (EINVAL); } if (frz->token) rc = nsc_unregister_path(frz->token, NSC_PCATCH); if (rc) { mutex_exit(&_nsc_frz_sleep); return (rc); } (*xfrz) = frz->next; mutex_exit(&_nsc_frz_sleep); nsc_kmem_free(frz, sizeof (*frz)); return (0); } /* * int _nsc_frz_isfrozen(char *path, int *rvp) * Tests whether a device is frozen. * * Calling/Exit State: * Returns 0 or EINVAL. * Sets *rvp to 1 if the device was not frozen, and 0 otherwise. * This function returns historical information. */ int _nsc_frz_isfrozen(path, rvp) char *path; int *rvp; { _nsc_frz_t *frz; *rvp = 1; if (! _nsc_frz_io) return (EINVAL); mutex_enter(&_nsc_frz_sleep); for (frz = _nsc_frz_top; frz; frz = frz->next) if (strcmp(frz->path, path) == 0) { *rvp = 0; break; } mutex_exit(&_nsc_frz_sleep); return (0); } /* * static int * _nsc_frz_open(char *path, int flag, blind_t *cdp) * Dummy open function. * * Description: * This is the "Open" function for the I/O module. * It is just a dummy. */ /* ARGSUSED */ static int _nsc_frz_open(path, flag, cdp) char *path; int flag; blind_t *cdp; { *cdp = 0; return (0); } /* * static int * _nsc_frz_close() * Dummy close function. * * Description: * This is the "Close" function for the I/O module. * It is just a dummy. */ static int _nsc_frz_close() { return (0); } /* * static int * _nsc_frz_attach() * Attach a device to this i/o module. * * Calling/Exit State: * Returns EACCES in all cricumstances. * * Description: * This function is called by the nsctl module when it wishes * to attach the device to this I/O module (ie. as part of * nsc_reserve() processing). This function unconditionally * returns an error which forces the nsc_reserve() to fail, and * so no access to possible to the underlying device. */ static int _nsc_frz_attach() { return (EACCES); } static nsc_def_t _nsc_frz_def[] = { "Open", (uintptr_t)_nsc_frz_open, 0, "Close", (uintptr_t)_nsc_frz_close, 0, "Attach", (uintptr_t)_nsc_frz_attach, 0, "Provide", 0, 0, 0, 0, 0 };