1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/errno.h>
  28 #include <sys/cmn_err.h>
  29 #include <sys/ksynch.h>
  30 #include <sys/kmem.h>
  31 #include <sys/ddi.h>
  32 
  33 #define __NSC_GEN__
  34 #include "nsc_dev.h"
  35 #include "../nsctl.h"
  36 
  37 /*
  38  * (Un)Freeze Module
  39  *
  40  * This module provides a means to 'freeze' a device and ensure
  41  * that no SP software has an open reference to that device.  Later
  42  * the device can be 'unfrozen' and the SP software can resume
  43  * normal operations.
  44  *
  45  * This module is required because it is possible to place a virtual
  46  * volume driver (RAID-0, 1 or 5) into a state whereby it needs to be
  47  * disabled for corrective action.  The (un)freeze facility provides a
  48  * method of doing this without downtime.
  49  *
  50  * A device that is frozen should be frozen on all nodes.  It is the
  51  * responsibility of the management software or the user to perform
  52  * the freeze and unfreeze on the required nodes.
  53  */
  54 
  55 extern nsc_mem_t *_nsc_local_mem;
  56 
  57 typedef struct _nsc_frz_s {
  58         struct _nsc_frz_s       *next;
  59         nsc_path_t              *token;
  60         char                    path[NSC_MAXPATH];
  61 } _nsc_frz_t;
  62 
  63 
  64 extern int _nsc_frz_stop(char *, int *);                /* forward decl */
  65 
  66 static _nsc_frz_t *_nsc_frz_top;
  67 static nsc_def_t _nsc_frz_def[];
  68 static kmutex_t _nsc_frz_sleep;
  69 static nsc_io_t *_nsc_frz_io;
  70 
  71 
  72 void
  73 _nsc_init_frz(void)
  74 {
  75         mutex_init(&_nsc_frz_sleep, NULL, MUTEX_DRIVER, NULL);
  76 
  77         _nsc_frz_io = nsc_register_io("frz",
  78                         NSC_FREEZE_ID | NSC_FILTER, _nsc_frz_def);
  79 
  80         if (!_nsc_frz_io)
  81                 cmn_err(CE_WARN, "nsctl: _nsc_init_frz: register failed");
  82 }
  83 
  84 
  85 void
  86 _nsc_deinit_frz(void)
  87 {
  88         if (_nsc_frz_io)
  89                 (void) nsc_unregister_io(_nsc_frz_io, 0);
  90 
  91         _nsc_frz_io = NULL;
  92 
  93         mutex_destroy(&_nsc_frz_sleep);
  94 }
  95 
  96 
  97 /*
  98  * int _nsc_frz_start(char *path, int *rvp)
  99  *      Freeze a device
 100  *
 101  * Calling/Exit State:
 102  *      Must be called from a context that can block.
 103  *      Returns 0 for success, or one of the following error codes:
 104  *              EINVAL   - invalid 'path' argument
 105  *              ENOMEM   - failed to allocate memory
 106  *              EALREADY - 'path' is already frozen
 107  *
 108  * Description:
 109  *      Registers 'path' to be accessed through the NSC_FREEZE_ID
 110  *      io module, and forces any open file descriptors for 'path'
 111  *      to be re-opened as appropriate.
 112  */
 113 int
 114 _nsc_frz_start(path, rvp)
 115 char *path;
 116 int *rvp;
 117 {
 118         _nsc_frz_t *frz, *xfrz;
 119         int rc;
 120 
 121         *rvp = 0;
 122 
 123         if (strlen(path) >= NSC_MAXPATH)
 124                 return (EINVAL);
 125 
 126         frz = nsc_kmem_zalloc(sizeof (*frz), KM_SLEEP, _nsc_local_mem);
 127         if (!frz)
 128                 return (ENOMEM);
 129 
 130         (void) strcpy(frz->path, path);
 131 
 132         mutex_enter(&_nsc_frz_sleep);
 133 
 134         for (xfrz = _nsc_frz_top; xfrz; xfrz = xfrz->next)
 135                 if (strcmp(frz->path, xfrz->path) == 0)
 136                         break;
 137 
 138         if (!xfrz) {
 139                 frz->next = _nsc_frz_top;
 140                 _nsc_frz_top = frz;
 141         }
 142 
 143         mutex_exit(&_nsc_frz_sleep);
 144 
 145         if (xfrz) {
 146                 nsc_kmem_free(frz, sizeof (*frz));
 147                 return (EALREADY);
 148         }
 149 
 150         frz->token = nsc_register_path(path, NSC_DEVICE, _nsc_frz_io);
 151 
 152         if (!frz->token) {
 153                 (void) _nsc_frz_stop(path, &rc);
 154                 return (EINVAL);
 155         }
 156 
 157         return (0);
 158 }
 159 
 160 
 161 /*
 162  * int _nsc_frz_stop(char *path, int *rvp)
 163  *      Unfreeze a device
 164  *
 165  * Calling/Exit State:
 166  *      Must be called from a context that can block.
 167  *      Returns 0 or an error code.
 168  *
 169  * Description:
 170  *      Removes the path registration for the NSC_FREEZE_ID io module
 171  *      and forces any re-opens as appropriate.
 172  */
 173 int
 174 _nsc_frz_stop(path, rvp)
 175 char *path;
 176 int *rvp;
 177 {
 178         _nsc_frz_t **xfrz, *frz = NULL;
 179         int rc = 0;
 180 
 181         *rvp = 0;
 182 
 183         mutex_enter(&_nsc_frz_sleep);
 184 
 185         for (xfrz = &_nsc_frz_top; *xfrz; xfrz = &(*xfrz)->next)
 186                 if (strcmp(path, (*xfrz)->path) == 0) {
 187                         frz = *xfrz;
 188                         break;
 189                 }
 190 
 191         if (!frz) {
 192                 mutex_exit(&_nsc_frz_sleep);
 193                 return (EINVAL);
 194         }
 195 
 196         if (frz->token)
 197                 rc = nsc_unregister_path(frz->token, NSC_PCATCH);
 198 
 199         if (rc) {
 200                 mutex_exit(&_nsc_frz_sleep);
 201                 return (rc);
 202         }
 203 
 204         (*xfrz) = frz->next;
 205 
 206         mutex_exit(&_nsc_frz_sleep);
 207 
 208         nsc_kmem_free(frz, sizeof (*frz));
 209 
 210         return (0);
 211 }
 212 
 213 
 214 /*
 215  * int _nsc_frz_isfrozen(char *path, int *rvp)
 216  *      Tests whether a device is frozen.
 217  *
 218  * Calling/Exit State:
 219  *      Returns 0 or EINVAL.
 220  *      Sets *rvp to 1 if the device was not frozen, and 0 otherwise.
 221  *      This function returns historical information.
 222  */
 223 int
 224 _nsc_frz_isfrozen(path, rvp)
 225 char *path;
 226 int *rvp;
 227 {
 228         _nsc_frz_t *frz;
 229 
 230         *rvp = 1;
 231 
 232         if (! _nsc_frz_io)
 233                 return (EINVAL);
 234 
 235         mutex_enter(&_nsc_frz_sleep);
 236 
 237         for (frz = _nsc_frz_top; frz; frz = frz->next)
 238                 if (strcmp(frz->path, path) == 0) {
 239                         *rvp = 0;
 240                         break;
 241                 }
 242 
 243         mutex_exit(&_nsc_frz_sleep);
 244 
 245         return (0);
 246 }
 247 
 248 
 249 /*
 250  * static int
 251  * _nsc_frz_open(char *path, int flag, blind_t *cdp)
 252  *      Dummy open function.
 253  *
 254  * Description:
 255  *      This is the "Open" function for the I/O module.
 256  *      It is just a dummy.
 257  */
 258 
 259 /* ARGSUSED */
 260 
 261 static int
 262 _nsc_frz_open(path, flag, cdp)
 263 char *path;
 264 int flag;
 265 blind_t *cdp;
 266 {
 267         *cdp = 0;
 268         return (0);
 269 }
 270 
 271 
 272 /*
 273  * static int
 274  * _nsc_frz_close()
 275  *      Dummy close function.
 276  *
 277  * Description:
 278  *      This is the "Close" function for the I/O module.
 279  *      It is just a dummy.
 280  */
 281 static int
 282 _nsc_frz_close() { return (0); }
 283 
 284 
 285 /*
 286  * static int
 287  * _nsc_frz_attach()
 288  *      Attach a device to this i/o module.
 289  *
 290  * Calling/Exit State:
 291  *      Returns EACCES in all cricumstances.
 292  *
 293  * Description:
 294  *      This function is called by the nsctl module when it wishes
 295  *      to attach the device to this I/O module (ie. as part of
 296  *      nsc_reserve() processing).  This function unconditionally
 297  *      returns an error which forces the nsc_reserve() to fail, and
 298  *      so no access to possible to the underlying device.
 299  */
 300 static int
 301 _nsc_frz_attach() { return (EACCES); }
 302 
 303 
 304 static nsc_def_t _nsc_frz_def[] = {
 305         { "Open",       (uintptr_t)_nsc_frz_open,       0 },
 306         { "Close",      (uintptr_t)_nsc_frz_close,      0 },
 307         { "Attach",     (uintptr_t)_nsc_frz_attach,     0 },
 308         { "Provide",    (uintptr_t)NULL,                0 },
 309         { NULL,         (uintptr_t)NULL,                0 }
 310 };