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/ksynch.h>
  28 #include <sys/errno.h>
  29 #include <sys/ddi.h>
  30 #include <sys/sunddi.h>
  31 
  32 #include "../nsctl.h"
  33 #include "../nsctl/nsc_ioctl.h"
  34 #include "nskernd.h"
  35 
  36 void *proc_nskernd;
  37 int nskernd_iscluster;
  38 
  39 static kmutex_t nskernd_lock;
  40 
  41 static kcondvar_t nskernd_ask_cv;
  42 static kcondvar_t nskernd_k_cv;
  43 static kcondvar_t nskernd_u_cv;
  44 
  45 static volatile int nskernd_k_wait;
  46 static volatile int nskernd_u_wait;
  47 
  48 static int nskernd_norun;
  49 
  50 static volatile int nskernd_ask;
  51 static struct nskernd nskernd_kdata;
  52 
  53 void
  54 nskernd_init(void)
  55 {
  56         mutex_init(&nskernd_lock, NULL, MUTEX_DRIVER, NULL);
  57         cv_init(&nskernd_ask_cv, NULL, CV_DRIVER, NULL);
  58         cv_init(&nskernd_k_cv, NULL, CV_DRIVER, NULL);
  59         cv_init(&nskernd_u_cv, NULL, CV_DRIVER, NULL);
  60 
  61         nskernd_norun = 0;
  62 }
  63 
  64 
  65 void
  66 nskernd_deinit(void)
  67 {
  68         mutex_destroy(&nskernd_lock);
  69         cv_destroy(&nskernd_ask_cv);
  70         cv_destroy(&nskernd_k_cv);
  71         cv_destroy(&nskernd_u_cv);
  72 }
  73 
  74 
  75 static int
  76 nskernd_start(const int iscluster)
  77 {
  78         int rc = 0;
  79 
  80         mutex_enter(&nskernd_lock);
  81 
  82         if (proc_nskernd != NULL) {
  83                 rc = 1;
  84         } else if (nskernd_norun != 0) {
  85                 rc = 2;
  86         } else {
  87                 (void) drv_getparm(UPROCP, (void *)&proc_nskernd);
  88                 nskernd_iscluster = iscluster;
  89         }
  90 
  91         mutex_exit(&nskernd_lock);
  92 
  93         return (rc);
  94 }
  95 
  96 
  97 /*
  98  * must be called with nskernd_lock held.
  99  */
 100 void
 101 nskernd_cleanup(void)
 102 {
 103         proc_nskernd = NULL;
 104         cv_broadcast(&nskernd_ask_cv);
 105         cv_broadcast(&nskernd_k_cv);
 106 }
 107 
 108 
 109 void
 110 nskernd_stop(void)
 111 {
 112         mutex_enter(&nskernd_lock);
 113 
 114         if (proc_nskernd == NULL) {
 115                 nskernd_norun = 1;
 116                 mutex_exit(&nskernd_lock);
 117                 return;
 118         }
 119 
 120         while (nskernd_u_wait == 0) {
 121                 nskernd_k_wait++;
 122                 cv_wait(&nskernd_k_cv, &nskernd_lock);
 123                 nskernd_k_wait--;
 124 
 125                 if (proc_nskernd == NULL) {
 126                         mutex_exit(&nskernd_lock);
 127                         return;
 128                 }
 129         }
 130 
 131         nskernd_kdata.command = NSKERND_STOP;
 132         nskernd_kdata.data1 = (uint64_t)1;      /* kernel has done cleanup */
 133 
 134         nskernd_cleanup();
 135 
 136         cv_signal(&nskernd_u_cv);
 137         mutex_exit(&nskernd_lock);
 138 }
 139 
 140 
 141 int
 142 nskernd_get(struct nskernd *nskp)
 143 {
 144         mutex_enter(&nskernd_lock);
 145 
 146         if (proc_nskernd == NULL) {
 147                 mutex_exit(&nskernd_lock);
 148                 return (ENXIO);
 149         }
 150 
 151         while (nskernd_u_wait == 0 || nskernd_ask) {
 152                 nskernd_k_wait++;
 153                 cv_wait(&nskernd_k_cv, &nskernd_lock);
 154                 nskernd_k_wait--;
 155 
 156                 if (proc_nskernd == NULL) {
 157                         mutex_exit(&nskernd_lock);
 158                         return (ENXIO);
 159                 }
 160         }
 161 
 162         bcopy(nskp, &nskernd_kdata, sizeof (*nskp));
 163         nskernd_ask++;
 164 
 165         cv_signal(&nskernd_u_cv);
 166 
 167         cv_wait(&nskernd_ask_cv, &nskernd_lock);
 168 
 169         if (proc_nskernd == NULL) {
 170                 nskernd_ask--;
 171                 mutex_exit(&nskernd_lock);
 172                 return (ENXIO);
 173         }
 174 
 175         bcopy(&nskernd_kdata, nskp, sizeof (*nskp));
 176         nskernd_ask--;
 177 
 178         if (nskernd_k_wait > 0)
 179                 cv_signal(&nskernd_k_cv);
 180 
 181         mutex_exit(&nskernd_lock);
 182         return (0);
 183 }
 184 
 185 
 186 int
 187 nskernd_command(intptr_t arg, int mode, int *rvalp)
 188 {
 189         struct nskernd *udata = NULL;
 190         uint64_t arg1, arg2;
 191         int rc;
 192 
 193         *rvalp = 0;
 194         rc = 0;
 195 
 196         udata = kmem_alloc(sizeof (*udata), KM_SLEEP);
 197         if (ddi_copyin((void *)arg, udata, sizeof (*udata), mode) < 0) {
 198                 kmem_free(udata, sizeof (*udata));
 199                 return (EFAULT);
 200         }
 201 
 202         switch (udata->command) {
 203         case NSKERND_START:             /* User program start */
 204                 *rvalp = nskernd_start(udata->data1);
 205                 break;
 206 
 207         case NSKERND_STOP:              /* User program requesting stop */
 208                 mutex_enter(&nskernd_lock);
 209                 nskernd_cleanup();
 210                 mutex_exit(&nskernd_lock);
 211                 break;
 212 
 213         case NSKERND_WAIT:
 214                 mutex_enter(&nskernd_lock);
 215 
 216                 bcopy(udata, &nskernd_kdata, sizeof (*udata));
 217 
 218                 if (nskernd_ask > 0)
 219                         cv_signal(&nskernd_ask_cv);
 220 
 221                 nskernd_u_wait++;
 222 
 223                 if (cv_wait_sig(&nskernd_u_cv, &nskernd_lock) != 0) {
 224                         /*
 225                          * woken by cv_signal() or cv_broadcast()
 226                          */
 227                         bcopy(&nskernd_kdata, udata, sizeof (*udata));
 228                 } else {
 229                         /*
 230                          * signal - the user process has blocked all
 231                          * signals except for SIGTERM and the
 232                          * uncatchables, so the process is about to die
 233                          * and we need to clean up.
 234                          */
 235                         udata->command = NSKERND_STOP;
 236                         udata->data1 = (uint64_t)1;   /* cleanup done */
 237 
 238                         nskernd_cleanup();
 239                 }
 240 
 241                 nskernd_u_wait--;
 242 
 243                 mutex_exit(&nskernd_lock);
 244 
 245                 if (ddi_copyout(udata, (void *)arg,
 246                     sizeof (*udata), mode) < 0) {
 247                         rc = EFAULT;
 248                         break;
 249                 }
 250 
 251                 break;
 252 
 253         case NSKERND_NEWLWP:
 254                 /* save kmem by freeing the udata structure */
 255                 arg1 = udata->data1;
 256                 kmem_free(udata, sizeof (*udata));
 257                 udata = NULL;
 258                 nsc_runlwp(arg1);
 259                 break;
 260 
 261         case NSKERND_LOCK:
 262                 /* save kmem by freeing the udata structure */
 263                 arg1 = udata->data1;
 264                 arg2 = udata->data2;
 265                 kmem_free(udata, sizeof (*udata));
 266                 udata = NULL;
 267                 nsc_lockchild(arg1, arg2);
 268                 break;
 269 
 270         default:
 271                 cmn_err(CE_WARN, "nskernd: unknown command %d", udata->command);
 272                 rc = EINVAL;
 273                 break;
 274         }
 275 
 276         if (udata != NULL) {
 277                 kmem_free(udata, sizeof (*udata));
 278                 udata = NULL;
 279         }
 280 
 281         return (rc);
 282 }
 283 
 284 /*
 285  * This function is included for SV ioctl processing only.
 286  */
 287 
 288 int
 289 nskernd_isdaemon(void)
 290 {
 291         void *this_proc;
 292 
 293         if (proc_nskernd == NULL)
 294                 return (0);
 295         if (drv_getparm(UPROCP, (void *)&this_proc) != 0)
 296                 return (0);
 297         return (proc_nskernd == this_proc);
 298 }