1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Kernel Remote Replication Protocol (KRRP)
  18  */
  19 
  20 #include <sys/types.h>
  21 #include <sys/stat.h>
  22 #include <sys/kmem.h>
  23 #include <sys/ddi.h>
  24 #include <sys/sunddi.h>
  25 #include <sys/sunldi.h>
  26 #include <sys/time.h>
  27 #include <sys/strsubr.h>
  28 #include <sys/sysmacros.h>
  29 #include <sys/sdt.h>
  30 #include <sys/modctl.h>
  31 #include <sys/class.h>
  32 #include <sys/cmn_err.h>
  33 
  34 #include <krrp_error.h>
  35 #include <sys/krrp.h>
  36 
  37 #include "krrp_svc.h"
  38 #include "krrp_ioctl.h"
  39 
  40 static int krrp_open(dev_t *, int, int, cred_t *);
  41 static int krrp_close(dev_t, int, int, cred_t *);
  42 static int krrp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  43 static int krrp_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
  44 static int krrp_attach(dev_info_t *, ddi_attach_cmd_t);
  45 static int krrp_detach(dev_info_t *, ddi_detach_cmd_t);
  46 
  47 static struct cb_ops krrp_cb_ops = {
  48         krrp_open,      /* open */
  49         krrp_close,     /* close */
  50         nodev,          /* strategy */
  51         nodev,          /* print */
  52         nodev,          /* dump */
  53         nodev,          /* read */
  54         nodev,          /* write */
  55         krrp_ioctl,     /* ioctl */
  56         nodev,          /* devmap */
  57         nodev,          /* mmap */
  58         nodev,          /* segmap */
  59         nochpoll,       /* poll */
  60         ddi_prop_op,    /* prop_op */
  61         NULL,           /* streamtab */
  62         D_NEW | D_MP | D_64BIT,         /* Driver compatibility flag */
  63         CB_REV,         /* version */
  64         nodev,          /* async read */
  65         nodev,          /* async write */
  66 };
  67 
  68 static struct dev_ops krrp_dev_ops = {
  69         DEVO_REV,       /* version */
  70         0,              /* refcnt */
  71         krrp_info,      /* info */
  72         nulldev,        /* identify */
  73         nulldev,        /* probe */
  74         krrp_attach,    /* attach */
  75         krrp_detach,    /* detach */
  76         nodev,          /* reset */
  77         &krrp_cb_ops,       /* driver operations */
  78         NULL            /* no bus operations */
  79 };
  80 
  81 static struct modldrv modldrv = {
  82         &mod_driverops,
  83         "kernel remote replication protocol module",
  84         &krrp_dev_ops
  85 };
  86 
  87 static struct modlinkage modlinkage = {
  88         MODREV_1, (void *)&modldrv, NULL
  89 };
  90 
  91 static uint16_t version = 1;
  92 
  93 int
  94 _init(void)
  95 {
  96         int rc;
  97         krrp_svc_t *krrp_svc;
  98 
  99         krrp_svc = krrp_svc_get_instance();
 100 
 101         rc = ldi_ident_from_mod(&modlinkage, &krrp_svc->li);
 102         if (rc != 0)
 103                 return (rc);
 104 
 105         rc = mod_install(&modlinkage);
 106         if (rc) {
 107                 ldi_ident_release(krrp_svc->li);
 108                 return (rc);
 109         }
 110 
 111         krrp_svc_init();
 112 
 113         return (0);
 114 }
 115 
 116 int
 117 _fini(void)
 118 {
 119         int error;
 120         krrp_svc_t *krrp_svc;
 121 
 122         krrp_svc = krrp_svc_get_instance();
 123 
 124         error = mod_remove(&modlinkage);
 125         if (error != 0)
 126                 return (error);
 127 
 128         ldi_ident_release(krrp_svc->li);
 129 
 130         krrp_svc_fini();
 131 
 132         return (0);
 133 }
 134 
 135 int
 136 _info(struct modinfo *modinfop)
 137 {
 138         return (mod_info(&modlinkage, modinfop));
 139 }
 140 
 141 /* ARGSUSED */
 142 static int
 143 krrp_open(dev_t *devp, int flag, int otyp, cred_t *cr)
 144 {
 145         return (0);
 146 }
 147 
 148 /* ARGSUSED */
 149 static int
 150 krrp_close(dev_t dev, int flag, int otyp, cred_t *cr)
 151 {
 152         return (0);
 153 }
 154 
 155 /* ARGSUSED */
 156 static int
 157 krrp_ioctl(dev_t dev, int cmd, intptr_t argp, int flags, cred_t *cr, int *rvalp)
 158 {
 159         int rc = 0;
 160         size_t size = 0;
 161         size_t total_size;
 162         krrp_ioctl_data_t ioctl_data_hdr, *ioctl_data;
 163         nvlist_t *in_nvl = NULL;
 164         nvlist_t *out_nvl = NULL;
 165         nvlist_t *error_nvl = NULL;
 166         krrp_error_t error;
 167         boolean_t ref_cnt_held;
 168 
 169         if (krrp_ioctl_validate_cmd(cmd) != 0) {
 170                 cmn_err(CE_WARN, "Invalid ioctl command (%d) "
 171                     "or version mismatch", cmd);
 172                 return (ENOTSUP);
 173         }
 174 
 175         ref_cnt_held = krrp_svc_ref_cnt_try_hold() == 0;
 176         if (!ref_cnt_held && cmd != KRRP_IOCTL_SVC_ENABLE &&
 177             cmd != KRRP_IOCTL_SVC_STATE) {
 178                 cmn_err(CE_WARN, "KRRP in-kernel service is not enabled");
 179                 return (ENOTACTIVE);
 180         }
 181 
 182         if (ref_cnt_held && cmd == KRRP_IOCTL_SVC_ENABLE) {
 183                 cmn_err(CE_WARN, "KRRP in-kernel service already enabled");
 184                 rc = EALREADY;
 185                 goto out;
 186         }
 187 
 188         (void) memset(&ioctl_data_hdr, 0, sizeof (ioctl_data_hdr));
 189         rc = ddi_copyin((void *) argp, &ioctl_data_hdr,
 190             sizeof (krrp_ioctl_data_t), flags);
 191         if (rc != 0) {
 192                 cmn_err(CE_WARN, "Failed to ddi_copyin() ioctl data hdr");
 193                 rc = EFAULT;
 194                 goto out;
 195         }
 196 
 197         if (ioctl_data_hdr.buf_size == 0) {
 198                 cmn_err(CE_WARN, "No data buffer");
 199                 rc = ENOBUFS;
 200                 goto out;
 201         }
 202 
 203         total_size = sizeof (krrp_ioctl_data_t) + ioctl_data_hdr.buf_size;
 204         ioctl_data = kmem_alloc(total_size, KM_SLEEP);
 205         rc = ddi_copyin((void *) argp, ioctl_data, total_size, flags);
 206         if (rc != 0) {
 207                 cmn_err(CE_WARN, "Failed to ddi_copyin() ioctl data buffer");
 208                 rc = EFAULT;
 209                 goto out;
 210         }
 211 
 212         if (ioctl_data->data_size != 0) {
 213                 rc = nvlist_unpack(ioctl_data->buf,
 214                     ioctl_data->data_size, &in_nvl, KM_SLEEP);
 215                 if (rc != 0) {
 216                         /*
 217                          * Unpacking of userspace data is a fatal error
 218                          * (incorrect use of ioctl API),
 219                          * so we just print error to syslog and return
 220                          */
 221                         cmn_err(CE_WARN, "Failed to unpack nvlist [%d]", rc);
 222                         goto out_kmem_free;
 223                 }
 224         }
 225 
 226         out_nvl = fnvlist_alloc();
 227 
 228         krrp_error_init(&error);
 229         rc = krrp_ioctl_process(cmd, in_nvl, out_nvl, &error);
 230 
 231         /*
 232          * Ioctl has been successfully executed.
 233          * So lets try to pack its result if exists
 234          */
 235         if (rc == 0) {
 236                 VERIFY(error.krrp_errno == 0);
 237 
 238                 if (nvlist_empty(out_nvl)) {
 239                         ioctl_data->data_size = 0;
 240                 } else {
 241                         size = fnvlist_size(out_nvl);
 242                         if (size > ioctl_data->buf_size) {
 243                                 rc = ENOSPC;
 244                                 goto out_nvl_free;
 245                         } else {
 246                                 char *buf;
 247 
 248                                 buf = ioctl_data->buf;
 249                                 VERIFY3U(nvlist_pack(out_nvl, (char **)&buf,
 250                                     &size, NV_ENCODE_NATIVE, KM_SLEEP), ==, 0);
 251                                 ioctl_data->data_size = (uint64_t)size;
 252                                 ioctl_data->out_flags |= KRRP_IOCTL_FLAG_RESULT;
 253                         }
 254                 }
 255         }
 256 
 257         /*
 258          * An error occurred during execution of ioctl or
 259          * during packing of its result
 260          * lets try to copyout the error
 261          */
 262         if (rc != 0 || error.krrp_errno != 0) {
 263                 VERIFY(error.krrp_errno != 0);
 264 
 265                 cmn_err(CE_WARN, "KRRP Error: [cmd: %d] [%s] [%d]",
 266                     cmd, krrp_error_errno_to_str(error.krrp_errno),
 267                     error.unix_errno);
 268 
 269                 krrp_error_to_nvl(&error, &error_nvl);
 270 
 271                 size = fnvlist_size(error_nvl);
 272                 if (size > ioctl_data->buf_size) {
 273                         cmn_err(CE_WARN, "The size of provided buffer "
 274                             "is to small to store the following error:\n"
 275                             "[ke: [%d], ue: [%d]]",
 276                             error.krrp_errno, error.unix_errno);
 277                         rc = ENOSPC;
 278                 } else {
 279                         char *buf;
 280 
 281                         buf = ioctl_data->buf;
 282                         VERIFY3U(nvlist_pack(error_nvl, (char **)&buf,
 283                             &size, NV_ENCODE_NATIVE, KM_SLEEP), ==, 0);
 284                         rc = 0;
 285                         ioctl_data->data_size = (uint64_t)size;
 286                         ioctl_data->out_flags |= KRRP_IOCTL_FLAG_ERROR;
 287                 }
 288 
 289                 fnvlist_free(error_nvl);
 290         }
 291 
 292         /*
 293          * rc == 0 if
 294          *  - successfully executed ioctl
 295          *  - an error occurred and we successfully packed it
 296          *
 297          *  So here we try to copyout the data
 298          */
 299         if (rc == 0) {
 300                 rc = ddi_copyout((void *) ioctl_data, (void *) argp,
 301                     total_size, flags);
 302                 if (rc != 0) {
 303                         cmn_err(CE_WARN, "Failed to ddi_copyout() "
 304                             "ioctl data buffer");
 305                         rc = EFAULT;
 306                 }
 307         }
 308 
 309 out_nvl_free:
 310         fnvlist_free(out_nvl);
 311         fnvlist_free(in_nvl);
 312 
 313 out_kmem_free:
 314         kmem_free(ioctl_data, total_size);
 315 
 316 out:
 317         if (ref_cnt_held)
 318                 krrp_svc_ref_cnt_rele();
 319 
 320         return (rc);
 321 }
 322 
 323 /* ARGSUSED */
 324 static int
 325 krrp_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 326 {
 327         krrp_svc_t *krrp_svc;
 328 
 329         krrp_svc = krrp_svc_get_instance();
 330 
 331         switch (infocmd) {
 332         case DDI_INFO_DEVT2DEVINFO:
 333                 *result = krrp_svc->dip;
 334                 return (DDI_SUCCESS);
 335         case DDI_INFO_DEVT2INSTANCE:
 336                 *result = (void *) 0;
 337                 return (DDI_SUCCESS);
 338         default:
 339                 return (DDI_FAILURE);
 340         }
 341 }
 342 
 343 static int
 344 krrp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 345 {
 346         if (cmd != DDI_ATTACH)
 347                 return (DDI_FAILURE);
 348 
 349         if (ddi_get_instance(dip) != 0)
 350                 return (DDI_FAILURE);
 351 
 352         if (ddi_create_minor_node(dip, KRRP_DRIVER, S_IFCHR, 0,
 353             DDI_PSEUDO, 0) != DDI_SUCCESS) {
 354                 return (DDI_FAILURE);
 355         }
 356 
 357         krrp_svc_attach(dip);
 358         ddi_report_dev(dip);
 359 
 360         return (DDI_SUCCESS);
 361 }
 362 
 363 static int
 364 krrp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 365 {
 366         if (cmd != DDI_DETACH)
 367                 return (DDI_FAILURE);
 368 
 369         if (krrp_svc_detach() != 0)
 370                 return (EBUSY);
 371 
 372         ddi_remove_minor_node(dip, NULL);
 373         ddi_prop_remove_all(dip);
 374 
 375         return (DDI_SUCCESS);
 376 }