12309Srsmaeda /*
22309Srsmaeda * CDDL HEADER START
32309Srsmaeda *
42309Srsmaeda * The contents of this file are subject to the terms of the
52309Srsmaeda * Common Development and Distribution License (the "License").
62309Srsmaeda * You may not use this file except in compliance with the License.
72309Srsmaeda *
82309Srsmaeda * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92309Srsmaeda * or http://www.opensolaris.org/os/licensing.
102309Srsmaeda * See the License for the specific language governing permissions
112309Srsmaeda * and limitations under the License.
122309Srsmaeda *
132309Srsmaeda * When distributing Covered Code, include this CDDL HEADER in each
142309Srsmaeda * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152309Srsmaeda * If applicable, add the following below this CDDL HEADER, with the
162309Srsmaeda * fields enclosed by brackets "[]" replaced with your own identifying
172309Srsmaeda * information: Portions Copyright [yyyy] [name of copyright owner]
182309Srsmaeda *
192309Srsmaeda * CDDL HEADER END
202309Srsmaeda */
212309Srsmaeda
222309Srsmaeda /*
23*12260SHaik.Aftandilian@Sun.COM * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
242309Srsmaeda */
252309Srsmaeda
262309Srsmaeda /*
272309Srsmaeda * DR control module for LDoms
282309Srsmaeda */
292309Srsmaeda
302309Srsmaeda #include <sys/sysmacros.h>
312309Srsmaeda #include <sys/modctl.h>
322309Srsmaeda #include <sys/conf.h>
332309Srsmaeda #include <sys/ddi.h>
342309Srsmaeda #include <sys/sunddi.h>
352309Srsmaeda #include <sys/ddi_impldefs.h>
362309Srsmaeda #include <sys/stat.h>
372309Srsmaeda #include <sys/door.h>
382309Srsmaeda #include <sys/open.h>
392309Srsmaeda #include <sys/note.h>
402309Srsmaeda #include <sys/ldoms.h>
412309Srsmaeda #include <sys/dr_util.h>
422309Srsmaeda #include <sys/drctl.h>
432309Srsmaeda #include <sys/drctl_impl.h>
442309Srsmaeda
452309Srsmaeda
462309Srsmaeda static int drctl_attach(dev_info_t *, ddi_attach_cmd_t);
472309Srsmaeda static int drctl_detach(dev_info_t *, ddi_detach_cmd_t);
482309Srsmaeda static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
492309Srsmaeda
502309Srsmaeda static int drctl_open(dev_t *, int, int, cred_t *);
512309Srsmaeda static int drctl_close(dev_t, int, int, cred_t *);
522309Srsmaeda static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
532309Srsmaeda
547899SJames.Marks@Sun.COM static void *pack_message(int, int, int, void *, size_t *, size_t *);
557899SJames.Marks@Sun.COM static int send_message(void *, size_t, drctl_resp_t **, size_t *);
562309Srsmaeda
572309Srsmaeda
582309Srsmaeda /*
592309Srsmaeda * Configuration data structures
602309Srsmaeda */
612309Srsmaeda static struct cb_ops drctl_cb_ops = {
622309Srsmaeda drctl_open, /* open */
632309Srsmaeda drctl_close, /* close */
642309Srsmaeda nodev, /* strategy */
652309Srsmaeda nodev, /* print */
662309Srsmaeda nodev, /* dump */
672309Srsmaeda nodev, /* read */
682309Srsmaeda nodev, /* write */
692309Srsmaeda drctl_ioctl, /* ioctl */
702309Srsmaeda nodev, /* devmap */
712309Srsmaeda nodev, /* mmap */
722309Srsmaeda nodev, /* segmap */
732309Srsmaeda nochpoll, /* poll */
742309Srsmaeda ddi_prop_op, /* prop_op */
752309Srsmaeda NULL, /* streamtab */
762309Srsmaeda D_MP | D_NEW, /* driver compatibility flag */
772309Srsmaeda CB_REV, /* cb_ops revision */
782309Srsmaeda nodev, /* async read */
792309Srsmaeda nodev /* async write */
802309Srsmaeda };
812309Srsmaeda
822309Srsmaeda
832309Srsmaeda static struct dev_ops drctl_ops = {
842309Srsmaeda DEVO_REV, /* devo_rev */
852309Srsmaeda 0, /* refcnt */
862309Srsmaeda drctl_getinfo, /* info */
872309Srsmaeda nulldev, /* identify */
882309Srsmaeda nulldev, /* probe */
892309Srsmaeda drctl_attach, /* attach */
902309Srsmaeda drctl_detach, /* detach */
912309Srsmaeda nodev, /* reset */
922309Srsmaeda &drctl_cb_ops, /* driver operations */
932309Srsmaeda NULL, /* bus operations */
942309Srsmaeda NULL, /* power */
957656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
962309Srsmaeda };
972309Srsmaeda
982309Srsmaeda static struct modldrv modldrv = {
992309Srsmaeda &mod_driverops, /* type of module - driver */
1007656SSherry.Moore@Sun.COM "DR Control pseudo driver",
1012309Srsmaeda &drctl_ops
1022309Srsmaeda };
1032309Srsmaeda
1042309Srsmaeda static struct modlinkage modlinkage = {
1052309Srsmaeda MODREV_1,
1062309Srsmaeda &modldrv,
1072309Srsmaeda NULL
1082309Srsmaeda };
1092309Srsmaeda
1102309Srsmaeda
1112309Srsmaeda /*
1122309Srsmaeda * Locking strategy
1132309Srsmaeda *
1142309Srsmaeda * One of the reasons for this module's existence is to serialize
1152309Srsmaeda * DR requests which might be coming from different sources. Only
1162309Srsmaeda * one operation is allowed to be in progress at any given time.
1172309Srsmaeda *
1182309Srsmaeda * A single lock word (the 'drc_busy' element below) is NULL
1192309Srsmaeda * when there is no operation in progress. When a client of this
1202309Srsmaeda * module initiates an operation it grabs the mutex 'drc_lock' in
1212309Srsmaeda * order to examine the lock word ('drc_busy'). If no other
1222309Srsmaeda * operation is in progress, the lock word will be NULL. If so,
1232309Srsmaeda * a cookie which uniquely identifies the requestor is stored in
1242309Srsmaeda * the lock word, and the mutex is released. Attempts by other
1252309Srsmaeda * clients to initiate an operation will fail.
1262309Srsmaeda *
1272309Srsmaeda * When the lock-holding client's operation is completed, the
1282309Srsmaeda * client will call a "finalize" function in this module, providing
1292309Srsmaeda * the cookie passed with the original request. Since the cookie
1302309Srsmaeda * matches, the operation will succeed and the lock word will be
1312309Srsmaeda * cleared. At this point, an new operation may be initiated.
1322309Srsmaeda */
1332309Srsmaeda
1342309Srsmaeda /*
1352309Srsmaeda * Driver private data
1362309Srsmaeda */
1372309Srsmaeda static struct drctl_unit {
1382309Srsmaeda kmutex_t drc_lock; /* global driver lock */
1392309Srsmaeda dev_info_t *drc_dip; /* dev_info pointer */
1402309Srsmaeda kcondvar_t drc_busy_cv; /* block for !busy */
1412309Srsmaeda drctl_cookie_t drc_busy; /* NULL if free else a unique */
1422309Srsmaeda /* identifier for caller */
1432309Srsmaeda int drc_cmd; /* the cmd underway (or -1) */
1442309Srsmaeda int drc_flags; /* saved flag from above cmd */
1452309Srsmaeda int drc_inst; /* our single instance */
1462309Srsmaeda uint_t drc_state; /* driver state */
1472309Srsmaeda } drctl_state;
1482309Srsmaeda
1492309Srsmaeda static struct drctl_unit *drctlp = &drctl_state;
1502309Srsmaeda
1512309Srsmaeda int
_init(void)1522309Srsmaeda _init(void)
1532309Srsmaeda {
1542410Slm66018 int rv;
1552410Slm66018
1562309Srsmaeda drctlp->drc_inst = -1;
1572309Srsmaeda mutex_init(&drctlp->drc_lock, NULL, MUTEX_DRIVER, NULL);
15811185SSean.McEnroe@Sun.COM cv_init(&drctlp->drc_busy_cv, NULL, CV_DRIVER, NULL);
1592410Slm66018
1602410Slm66018 if ((rv = mod_install(&modlinkage)) != 0)
1612410Slm66018 mutex_destroy(&drctlp->drc_lock);
1622410Slm66018
1632410Slm66018 return (rv);
1642309Srsmaeda }
1652309Srsmaeda
1662309Srsmaeda
1672309Srsmaeda int
_fini(void)1682309Srsmaeda _fini(void)
1692309Srsmaeda {
1702410Slm66018 int rv;
1712410Slm66018
1722410Slm66018 if ((rv = mod_remove(&modlinkage)) != 0)
1732410Slm66018 return (rv);
17411185SSean.McEnroe@Sun.COM cv_destroy(&drctlp->drc_busy_cv);
1752309Srsmaeda mutex_destroy(&drctlp->drc_lock);
1762410Slm66018 return (0);
1772309Srsmaeda }
1782309Srsmaeda
1792309Srsmaeda
1802309Srsmaeda int
_info(struct modinfo * modinfop)1812309Srsmaeda _info(struct modinfo *modinfop)
1822309Srsmaeda {
1832309Srsmaeda return (mod_info(&modlinkage, modinfop));
1842309Srsmaeda }
1852309Srsmaeda
1862309Srsmaeda
1872309Srsmaeda /*
1882309Srsmaeda * Do the attach work
1892309Srsmaeda */
1902309Srsmaeda static int
drctl_do_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1912309Srsmaeda drctl_do_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1922309Srsmaeda {
1932309Srsmaeda _NOTE(ARGUNUSED(cmd))
1942309Srsmaeda
1952309Srsmaeda char *str = "drctl_do_attach";
1962309Srsmaeda int retval = DDI_SUCCESS;
1972309Srsmaeda
1982309Srsmaeda if (drctlp->drc_inst != -1) {
1992309Srsmaeda cmn_err(CE_WARN, "%s: an instance is already attached!", str);
2002309Srsmaeda return (DDI_FAILURE);
2012309Srsmaeda }
2022309Srsmaeda drctlp->drc_inst = ddi_get_instance(dip);
2032309Srsmaeda
2042309Srsmaeda retval = ddi_create_minor_node(dip, "drctl", S_IFCHR,
2052309Srsmaeda drctlp->drc_inst, DDI_PSEUDO, 0);
2062309Srsmaeda if (retval != DDI_SUCCESS) {
2072309Srsmaeda cmn_err(CE_WARN, "%s: can't create minor node", str);
2082309Srsmaeda drctlp->drc_inst = -1;
2092309Srsmaeda return (retval);
2102309Srsmaeda }
2112309Srsmaeda
2122309Srsmaeda drctlp->drc_dip = dip;
2132309Srsmaeda ddi_report_dev(dip);
2142309Srsmaeda
2152309Srsmaeda return (retval);
2162309Srsmaeda }
2172309Srsmaeda
2182309Srsmaeda
2192309Srsmaeda static int
drctl_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2202309Srsmaeda drctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2212309Srsmaeda {
2222309Srsmaeda switch (cmd) {
2232309Srsmaeda case DDI_ATTACH:
2242309Srsmaeda return (drctl_do_attach(dip, cmd));
2252309Srsmaeda
2262309Srsmaeda default:
2272309Srsmaeda return (DDI_FAILURE);
2282309Srsmaeda }
2292309Srsmaeda }
2302309Srsmaeda
2312309Srsmaeda
2322309Srsmaeda /* ARGSUSED */
2332309Srsmaeda static int
drctl_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2342309Srsmaeda drctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2352309Srsmaeda {
2362309Srsmaeda switch (cmd) {
2372309Srsmaeda case DDI_DETACH:
2382309Srsmaeda drctlp->drc_inst = -1;
2392309Srsmaeda ddi_remove_minor_node(dip, "drctl");
2402309Srsmaeda return (DDI_SUCCESS);
2412309Srsmaeda
2422309Srsmaeda default:
2432309Srsmaeda return (DDI_FAILURE);
2442309Srsmaeda }
2452309Srsmaeda }
2462309Srsmaeda
2472309Srsmaeda static int
drctl_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)2482309Srsmaeda drctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2492309Srsmaeda {
2502309Srsmaeda _NOTE(ARGUNUSED(dip, cmd, arg, resultp))
2512309Srsmaeda
2522309Srsmaeda return (0);
2532309Srsmaeda }
2542309Srsmaeda
2552309Srsmaeda static int
drctl_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)2562309Srsmaeda drctl_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
2572309Srsmaeda {
2582309Srsmaeda _NOTE(ARGUNUSED(devp, flag, cred_p))
2592309Srsmaeda
2602309Srsmaeda if (otyp != OTYP_CHR)
2612309Srsmaeda return (EINVAL);
2622309Srsmaeda
2632309Srsmaeda return (0);
2642309Srsmaeda }
2652309Srsmaeda
2662309Srsmaeda static int
drctl_close(dev_t dev,int flag,int otyp,cred_t * cred_p)2672309Srsmaeda drctl_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
2682309Srsmaeda {
2692309Srsmaeda _NOTE(ARGUNUSED(dev, flag, otyp, cred_p))
2702309Srsmaeda
2712309Srsmaeda return (0);
2722309Srsmaeda }
2732309Srsmaeda
2742309Srsmaeda /*
2757899SJames.Marks@Sun.COM * Create a reponse structure which includes an array of drctl_rsrc_t
2767899SJames.Marks@Sun.COM * structures in which each status element is set to the 'status'
2777899SJames.Marks@Sun.COM * arg. There is no error text, so set the 'offset' elements to 0.
2782309Srsmaeda */
2797899SJames.Marks@Sun.COM static drctl_resp_t *
drctl_generate_resp(drctl_rsrc_t * res,int count,size_t * rsize,drctl_status_t status)2802309Srsmaeda drctl_generate_resp(drctl_rsrc_t *res,
2812309Srsmaeda int count, size_t *rsize, drctl_status_t status)
2822309Srsmaeda {
2837899SJames.Marks@Sun.COM int i;
2842309Srsmaeda size_t size;
2857899SJames.Marks@Sun.COM drctl_rsrc_t *rsrc;
2867899SJames.Marks@Sun.COM drctl_resp_t *resp;
2872309Srsmaeda
2887899SJames.Marks@Sun.COM size = offsetof(drctl_resp_t, resp_resources) + (count * sizeof (*res));
2897899SJames.Marks@Sun.COM resp = kmem_alloc(size, KM_SLEEP);
2907899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
2917899SJames.Marks@Sun.COM __func__, (void *)resp, size);
2922309Srsmaeda
2937899SJames.Marks@Sun.COM resp->resp_type = DRCTL_RESP_OK;
2947899SJames.Marks@Sun.COM rsrc = resp->resp_resources;
2952309Srsmaeda
2967899SJames.Marks@Sun.COM bcopy(res, rsrc, count * sizeof (*res));
2977899SJames.Marks@Sun.COM
2987899SJames.Marks@Sun.COM for (i = 0; i < count; i++) {
2997899SJames.Marks@Sun.COM rsrc[i].status = status;
3007899SJames.Marks@Sun.COM rsrc[i].offset = 0;
3012309Srsmaeda }
3022309Srsmaeda
3032309Srsmaeda *rsize = size;
3047899SJames.Marks@Sun.COM
3057899SJames.Marks@Sun.COM return (resp);
3067899SJames.Marks@Sun.COM }
3077899SJames.Marks@Sun.COM
3087899SJames.Marks@Sun.COM /*
3097899SJames.Marks@Sun.COM * Generate an error response message.
3107899SJames.Marks@Sun.COM */
3117899SJames.Marks@Sun.COM static drctl_resp_t *
drctl_generate_err_resp(char * msg,size_t * size)3127899SJames.Marks@Sun.COM drctl_generate_err_resp(char *msg, size_t *size)
3137899SJames.Marks@Sun.COM {
3147899SJames.Marks@Sun.COM drctl_resp_t *resp;
3157899SJames.Marks@Sun.COM
3167899SJames.Marks@Sun.COM ASSERT(msg != NULL);
3177899SJames.Marks@Sun.COM ASSERT(size != NULL);
3187899SJames.Marks@Sun.COM
3197899SJames.Marks@Sun.COM *size = offsetof(drctl_resp_t, resp_err_msg) + strlen(msg) + 1;
3207899SJames.Marks@Sun.COM resp = kmem_alloc(*size, KM_SLEEP);
3217899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
3227899SJames.Marks@Sun.COM __func__, (void *)resp, *size);
3237899SJames.Marks@Sun.COM
3247899SJames.Marks@Sun.COM resp->resp_type = DRCTL_RESP_ERR;
3257899SJames.Marks@Sun.COM (void) strcpy(resp->resp_err_msg, msg);
3267899SJames.Marks@Sun.COM
3277899SJames.Marks@Sun.COM return (resp);
3282309Srsmaeda }
3292309Srsmaeda
3307899SJames.Marks@Sun.COM /*
3317899SJames.Marks@Sun.COM * Since response comes from userland, verify that it is at least the
3327899SJames.Marks@Sun.COM * minimum size based on the size of the original request. Verify
3337899SJames.Marks@Sun.COM * that any offsets to error strings are within the string area of
3347899SJames.Marks@Sun.COM * the response and, force the string area to be null-terminated.
3357899SJames.Marks@Sun.COM */
3367899SJames.Marks@Sun.COM static int
verify_response(int cmd,int count,drctl_resp_t * resp,size_t sent_len,size_t resp_len)3377899SJames.Marks@Sun.COM verify_response(int cmd,
3387899SJames.Marks@Sun.COM int count, drctl_resp_t *resp, size_t sent_len, size_t resp_len)
3397899SJames.Marks@Sun.COM {
3407899SJames.Marks@Sun.COM drctl_rsrc_t *rsrc = resp->resp_resources;
3417899SJames.Marks@Sun.COM size_t rcvd_len = resp_len - (offsetof(drctl_resp_t, resp_resources));
3427899SJames.Marks@Sun.COM int is_cpu = 0;
3437899SJames.Marks@Sun.COM int i;
3447899SJames.Marks@Sun.COM
3457899SJames.Marks@Sun.COM switch (cmd) {
3467899SJames.Marks@Sun.COM case DRCTL_CPU_CONFIG_REQUEST:
3477899SJames.Marks@Sun.COM case DRCTL_CPU_UNCONFIG_REQUEST:
3487899SJames.Marks@Sun.COM if (rcvd_len < sent_len)
3497899SJames.Marks@Sun.COM return (EIO);
3507899SJames.Marks@Sun.COM is_cpu = 1;
3517899SJames.Marks@Sun.COM break;
3527899SJames.Marks@Sun.COM case DRCTL_IO_UNCONFIG_REQUEST:
3537899SJames.Marks@Sun.COM case DRCTL_IO_CONFIG_REQUEST:
3547899SJames.Marks@Sun.COM if (count != 1)
3557899SJames.Marks@Sun.COM return (EIO);
3567899SJames.Marks@Sun.COM break;
35710106SJason.Beloro@Sun.COM case DRCTL_MEM_CONFIG_REQUEST:
35810106SJason.Beloro@Sun.COM case DRCTL_MEM_UNCONFIG_REQUEST:
35910106SJason.Beloro@Sun.COM break;
3607899SJames.Marks@Sun.COM default:
3617899SJames.Marks@Sun.COM return (EIO);
3627899SJames.Marks@Sun.COM }
3637899SJames.Marks@Sun.COM
3647899SJames.Marks@Sun.COM for (i = 0; i < count; i++)
3657899SJames.Marks@Sun.COM if ((rsrc[i].offset > 0) &&
3667899SJames.Marks@Sun.COM /* string can't be inside the bounds of original request */
3677899SJames.Marks@Sun.COM (((rsrc[i].offset < sent_len) && is_cpu) ||
3687899SJames.Marks@Sun.COM /* string must start inside the message */
3697899SJames.Marks@Sun.COM (rsrc[i].offset >= rcvd_len)))
3707899SJames.Marks@Sun.COM return (EIO);
3717899SJames.Marks@Sun.COM
3727899SJames.Marks@Sun.COM /* If there are any strings, terminate the string area. */
3737899SJames.Marks@Sun.COM if (rcvd_len > sent_len)
3747899SJames.Marks@Sun.COM *((char *)rsrc + rcvd_len - 1) = '\0';
3757899SJames.Marks@Sun.COM
3767899SJames.Marks@Sun.COM return (0);
3777899SJames.Marks@Sun.COM }
3787899SJames.Marks@Sun.COM
3792309Srsmaeda static int
drctl_config_common(int cmd,int flags,drctl_rsrc_t * res,int count,drctl_resp_t ** rbuf,size_t * rsize,size_t * rq_size)3802309Srsmaeda drctl_config_common(int cmd, int flags, drctl_rsrc_t *res,
3817899SJames.Marks@Sun.COM int count, drctl_resp_t **rbuf, size_t *rsize, size_t *rq_size)
3822309Srsmaeda {
3832309Srsmaeda int rv = 0;
3842309Srsmaeda size_t size;
3852309Srsmaeda char *bufp;
3862309Srsmaeda
3872309Srsmaeda switch (cmd) {
3882309Srsmaeda case DRCTL_CPU_CONFIG_REQUEST:
3892309Srsmaeda case DRCTL_CPU_CONFIG_NOTIFY:
3902309Srsmaeda case DRCTL_CPU_UNCONFIG_REQUEST:
3912309Srsmaeda case DRCTL_CPU_UNCONFIG_NOTIFY:
3926441Sjm22469 case DRCTL_IO_UNCONFIG_REQUEST:
3936441Sjm22469 case DRCTL_IO_UNCONFIG_NOTIFY:
3946441Sjm22469 case DRCTL_IO_CONFIG_REQUEST:
3956441Sjm22469 case DRCTL_IO_CONFIG_NOTIFY:
3962309Srsmaeda case DRCTL_MEM_CONFIG_REQUEST:
3972309Srsmaeda case DRCTL_MEM_CONFIG_NOTIFY:
3982309Srsmaeda case DRCTL_MEM_UNCONFIG_REQUEST:
3992309Srsmaeda case DRCTL_MEM_UNCONFIG_NOTIFY:
40010106SJason.Beloro@Sun.COM rv = 0;
40110106SJason.Beloro@Sun.COM break;
40210106SJason.Beloro@Sun.COM default:
4032309Srsmaeda rv = ENOTSUP;
4042309Srsmaeda break;
4052309Srsmaeda }
4062309Srsmaeda
4072309Srsmaeda if (rv != 0) {
4087899SJames.Marks@Sun.COM DR_DBG_CTL("%s: invalid cmd %d\n", __func__, cmd);
4092309Srsmaeda return (rv);
4102309Srsmaeda }
4112309Srsmaeda
4122309Srsmaeda /*
4132309Srsmaeda * If the operation is a FORCE, we don't send a message to
4142309Srsmaeda * the daemon. But, the upstream clients still expect a
4152309Srsmaeda * response, so generate a response with all ops 'allowed'.
4162309Srsmaeda */
4172309Srsmaeda if (flags == DRCTL_FLAG_FORCE) {
4187899SJames.Marks@Sun.COM if (rbuf != NULL)
4197899SJames.Marks@Sun.COM *rbuf = drctl_generate_resp(res,
4207899SJames.Marks@Sun.COM count, rsize, DRCTL_STATUS_ALLOW);
4212309Srsmaeda return (0);
4222309Srsmaeda }
4232309Srsmaeda
4247899SJames.Marks@Sun.COM bufp = pack_message(cmd, flags, count, (void *)res, &size, rq_size);
4252309Srsmaeda DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n",
4267899SJames.Marks@Sun.COM __func__, (void *)bufp, size);
4272309Srsmaeda
4287899SJames.Marks@Sun.COM if (bufp == NULL || size == 0)
4297899SJames.Marks@Sun.COM return (EINVAL);
4302309Srsmaeda
4317899SJames.Marks@Sun.COM return (send_message(bufp, size, rbuf, rsize));
4322309Srsmaeda }
4332309Srsmaeda
4342309Srsmaeda /*
4352309Srsmaeda * Prepare for a reconfig operation.
4362309Srsmaeda */
4372309Srsmaeda int
drctl_config_init(int cmd,int flags,drctl_rsrc_t * res,int count,drctl_resp_t ** rbuf,size_t * rsize,drctl_cookie_t ck)4382309Srsmaeda drctl_config_init(int cmd, int flags, drctl_rsrc_t *res,
4397899SJames.Marks@Sun.COM int count, drctl_resp_t **rbuf, size_t *rsize, drctl_cookie_t ck)
4402309Srsmaeda {
4417899SJames.Marks@Sun.COM static char inval_msg[] = "Invalid command format received.\n";
4427899SJames.Marks@Sun.COM static char unsup_msg[] = "Unsuppported command received.\n";
4437899SJames.Marks@Sun.COM static char unk_msg [] = "Failure reason unknown.\n";
4447899SJames.Marks@Sun.COM static char rsp_msg [] = "Invalid response from "
4457899SJames.Marks@Sun.COM "reconfiguration daemon.\n";
4467899SJames.Marks@Sun.COM static char drd_msg [] = "Cannot communicate with reconfiguration "
4477899SJames.Marks@Sun.COM "daemon (drd) in target domain.\n"
4487899SJames.Marks@Sun.COM "drd(1M) SMF service may not be enabled.\n";
4497899SJames.Marks@Sun.COM static char busy_msg [] = "Busy executing earlier command; "
4507899SJames.Marks@Sun.COM "please try again later.\n";
4517899SJames.Marks@Sun.COM size_t rq_size;
4527899SJames.Marks@Sun.COM char *ermsg;
4532309Srsmaeda int rv;
4542309Srsmaeda
4557899SJames.Marks@Sun.COM if (ck == 0) {
4567899SJames.Marks@Sun.COM *rbuf = drctl_generate_err_resp(inval_msg, rsize);
4577899SJames.Marks@Sun.COM
4582309Srsmaeda return (EINVAL);
4597899SJames.Marks@Sun.COM }
4602309Srsmaeda
4612309Srsmaeda mutex_enter(&drctlp->drc_lock);
4622309Srsmaeda if (drctlp->drc_busy != NULL) {
4632309Srsmaeda mutex_exit(&drctlp->drc_lock);
4647899SJames.Marks@Sun.COM *rbuf = drctl_generate_err_resp(busy_msg, rsize);
4657899SJames.Marks@Sun.COM
4662309Srsmaeda return (EBUSY);
4672309Srsmaeda }
4682309Srsmaeda
4692309Srsmaeda DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n",
4707899SJames.Marks@Sun.COM __func__, cmd, flags, (void *)res, count);
4712309Srsmaeda
4722309Srsmaeda /* Mark the link busy. Below we will fill in the actual cookie. */
4732309Srsmaeda drctlp->drc_busy = (drctl_cookie_t)-1;
4742309Srsmaeda mutex_exit(&drctlp->drc_lock);
4752309Srsmaeda
4767899SJames.Marks@Sun.COM rv = drctl_config_common(cmd, flags, res, count, rbuf, rsize, &rq_size);
4777899SJames.Marks@Sun.COM if (rv == 0) {
4782309Srsmaeda /*
4797899SJames.Marks@Sun.COM * If the upcall to the daemon returned successfully, we
4807899SJames.Marks@Sun.COM * still need to validate the format of the returned msg.
4812309Srsmaeda */
4827899SJames.Marks@Sun.COM if ((rv = verify_response(cmd,
4837899SJames.Marks@Sun.COM count, *rbuf, rq_size, *rsize)) != 0) {
4847899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: free addr %p size %ld\n",
4857899SJames.Marks@Sun.COM __func__, (void *)*rbuf, *rsize);
4867899SJames.Marks@Sun.COM kmem_free(*rbuf, *rsize);
4877899SJames.Marks@Sun.COM *rbuf = drctl_generate_err_resp(rsp_msg, rsize);
4887899SJames.Marks@Sun.COM drctlp->drc_busy = NULL;
48911185SSean.McEnroe@Sun.COM cv_broadcast(&drctlp->drc_busy_cv);
4907899SJames.Marks@Sun.COM } else { /* message format is valid */
4917899SJames.Marks@Sun.COM drctlp->drc_busy = ck;
4927899SJames.Marks@Sun.COM drctlp->drc_cmd = cmd;
4937899SJames.Marks@Sun.COM drctlp->drc_flags = flags;
4942309Srsmaeda }
4952309Srsmaeda } else {
4967899SJames.Marks@Sun.COM switch (rv) {
4977899SJames.Marks@Sun.COM case ENOTSUP:
4987899SJames.Marks@Sun.COM ermsg = unsup_msg;
4997899SJames.Marks@Sun.COM break;
5007899SJames.Marks@Sun.COM case EIO:
5017899SJames.Marks@Sun.COM ermsg = drd_msg;
5027899SJames.Marks@Sun.COM break;
5037899SJames.Marks@Sun.COM default:
5047899SJames.Marks@Sun.COM ermsg = unk_msg;
5057899SJames.Marks@Sun.COM break;
5067899SJames.Marks@Sun.COM }
5077899SJames.Marks@Sun.COM
5087899SJames.Marks@Sun.COM *rbuf = drctl_generate_err_resp(ermsg, rsize);
5097899SJames.Marks@Sun.COM
5102309Srsmaeda drctlp->drc_cmd = -1;
5112309Srsmaeda drctlp->drc_flags = 0;
5122309Srsmaeda drctlp->drc_busy = NULL;
51311185SSean.McEnroe@Sun.COM cv_broadcast(&drctlp->drc_busy_cv);
5142309Srsmaeda }
5152309Srsmaeda return (rv);
5162309Srsmaeda }
5172309Srsmaeda
5182309Srsmaeda /*
5192309Srsmaeda * Complete a reconfig operation.
5202309Srsmaeda */
5212309Srsmaeda int
drctl_config_fini(drctl_cookie_t ck,drctl_rsrc_t * res,int count)5222309Srsmaeda drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count)
5232309Srsmaeda {
5242309Srsmaeda int rv;
5252309Srsmaeda int notify_cmd;
5262309Srsmaeda int flags;
5277899SJames.Marks@Sun.COM size_t rq_size;
5282309Srsmaeda
5292309Srsmaeda mutex_enter(&drctlp->drc_lock);
5302309Srsmaeda if (drctlp->drc_busy != ck) {
5312309Srsmaeda mutex_exit(&drctlp->drc_lock);
5322309Srsmaeda return (EBUSY);
5332309Srsmaeda }
5342309Srsmaeda mutex_exit(&drctlp->drc_lock);
5352309Srsmaeda
5362309Srsmaeda flags = drctlp->drc_flags;
5372309Srsmaeda /*
5382309Srsmaeda * Flip the saved _REQUEST command to its corresponding
5392309Srsmaeda * _NOTIFY command.
5402309Srsmaeda */
5412309Srsmaeda switch (drctlp->drc_cmd) {
5422309Srsmaeda case DRCTL_CPU_CONFIG_REQUEST:
5432309Srsmaeda notify_cmd = DRCTL_CPU_CONFIG_NOTIFY;
5442309Srsmaeda break;
5452309Srsmaeda
5462309Srsmaeda case DRCTL_CPU_UNCONFIG_REQUEST:
5472309Srsmaeda notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY;
5482309Srsmaeda break;
5492309Srsmaeda
5506441Sjm22469 case DRCTL_IO_UNCONFIG_REQUEST:
5516441Sjm22469 notify_cmd = DRCTL_IO_UNCONFIG_NOTIFY;
5526441Sjm22469 break;
5536441Sjm22469
5546441Sjm22469 case DRCTL_IO_CONFIG_REQUEST:
5556441Sjm22469 notify_cmd = DRCTL_IO_CONFIG_NOTIFY;
5566441Sjm22469 break;
5576441Sjm22469
5582309Srsmaeda case DRCTL_MEM_CONFIG_REQUEST:
55910106SJason.Beloro@Sun.COM notify_cmd = DRCTL_MEM_CONFIG_NOTIFY;
56010106SJason.Beloro@Sun.COM break;
56110106SJason.Beloro@Sun.COM
5622309Srsmaeda case DRCTL_MEM_UNCONFIG_REQUEST:
56310106SJason.Beloro@Sun.COM notify_cmd = DRCTL_MEM_UNCONFIG_NOTIFY;
56410106SJason.Beloro@Sun.COM break;
56510106SJason.Beloro@Sun.COM
5662309Srsmaeda default:
5672309Srsmaeda /* none of the above should have been accepted in _init */
5682309Srsmaeda ASSERT(0);
5692309Srsmaeda cmn_err(CE_CONT,
5702309Srsmaeda "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd);
5712309Srsmaeda rv = EINVAL;
5722309Srsmaeda goto done;
5732309Srsmaeda }
5742309Srsmaeda
5757899SJames.Marks@Sun.COM rv = drctl_config_common(notify_cmd,
5767899SJames.Marks@Sun.COM flags, res, count, NULL, 0, &rq_size);
5772309Srsmaeda
5782309Srsmaeda done:
57911185SSean.McEnroe@Sun.COM drctlp->drc_cmd = -1;
58011185SSean.McEnroe@Sun.COM drctlp->drc_flags = 0;
58111185SSean.McEnroe@Sun.COM drctlp->drc_busy = NULL;
58211185SSean.McEnroe@Sun.COM cv_broadcast(&drctlp->drc_busy_cv);
58311185SSean.McEnroe@Sun.COM return (rv);
5842309Srsmaeda }
5852309Srsmaeda
5862309Srsmaeda static int
drctl_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)5872309Srsmaeda drctl_ioctl(dev_t dev,
5882309Srsmaeda int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
5892309Srsmaeda {
5902309Srsmaeda _NOTE(ARGUNUSED(dev, mode, cred_p, rval_p))
5912309Srsmaeda
5922309Srsmaeda int rv;
5932309Srsmaeda
5942309Srsmaeda switch (cmd) {
5952309Srsmaeda case DRCTL_IOCTL_CONNECT_SERVER:
5962309Srsmaeda rv = i_drctl_ioctl(cmd, arg);
5972309Srsmaeda break;
5982309Srsmaeda default:
5992309Srsmaeda rv = ENOTSUP;
6002309Srsmaeda }
6012309Srsmaeda
6022309Srsmaeda *rval_p = (rv == 0) ? 0 : -1;
6032309Srsmaeda
6042309Srsmaeda return (rv);
6052309Srsmaeda }
6062309Srsmaeda
6072309Srsmaeda /*
6082309Srsmaeda * Accept a preformatted request from caller and send a message to
6092309Srsmaeda * the daemon. A pointer to the daemon's response buffer is passed
6102309Srsmaeda * back in obufp, its size in osize.
6112309Srsmaeda */
6122309Srsmaeda static int
send_message(void * msg,size_t size,drctl_resp_t ** obufp,size_t * osize)6137899SJames.Marks@Sun.COM send_message(void *msg, size_t size, drctl_resp_t **obufp, size_t *osize)
6142309Srsmaeda {
6157899SJames.Marks@Sun.COM drctl_resp_t *bufp;
6167899SJames.Marks@Sun.COM drctl_rsrc_t *rsrcs;
6177899SJames.Marks@Sun.COM size_t rsrcs_size;
6182309Srsmaeda int rv;
6192309Srsmaeda
6207899SJames.Marks@Sun.COM rv = i_drctl_send(msg, size, (void **)&rsrcs, &rsrcs_size);
6217899SJames.Marks@Sun.COM
6227899SJames.Marks@Sun.COM if ((rv == 0) && ((rsrcs == NULL) ||(rsrcs_size == 0)))
6237899SJames.Marks@Sun.COM rv = EINVAL;
6247899SJames.Marks@Sun.COM
6257899SJames.Marks@Sun.COM if (rv == 0) {
6267899SJames.Marks@Sun.COM if (obufp != NULL) {
6277899SJames.Marks@Sun.COM ASSERT(osize != NULL);
6282309Srsmaeda
6297899SJames.Marks@Sun.COM *osize =
6307899SJames.Marks@Sun.COM offsetof(drctl_resp_t, resp_resources) + rsrcs_size;
6317899SJames.Marks@Sun.COM bufp =
6327899SJames.Marks@Sun.COM kmem_alloc(*osize, KM_SLEEP);
6337899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
6347899SJames.Marks@Sun.COM __func__, (void *)bufp, *osize);
6357899SJames.Marks@Sun.COM bufp->resp_type = DRCTL_RESP_OK;
6367899SJames.Marks@Sun.COM bcopy(rsrcs, bufp->resp_resources, rsrcs_size);
6377899SJames.Marks@Sun.COM *obufp = bufp;
6387899SJames.Marks@Sun.COM }
6397899SJames.Marks@Sun.COM
6407899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: free addr %p size %ld\n",
6417899SJames.Marks@Sun.COM __func__, (void *)rsrcs, rsrcs_size);
6427899SJames.Marks@Sun.COM kmem_free(rsrcs, rsrcs_size);
6437899SJames.Marks@Sun.COM }
6447899SJames.Marks@Sun.COM
6457899SJames.Marks@Sun.COM DR_DBG_KMEM("%s:free addr %p size %ld\n", __func__, msg, size);
6462309Srsmaeda kmem_free(msg, size);
6472309Srsmaeda
6482309Srsmaeda return (rv);
6492309Srsmaeda }
6502309Srsmaeda
6512309Srsmaeda static void *
pack_message(int cmd,int flags,int count,void * data,size_t * osize,size_t * data_size)6527899SJames.Marks@Sun.COM pack_message(int cmd,
6537899SJames.Marks@Sun.COM int flags, int count, void *data, size_t *osize, size_t *data_size)
6542309Srsmaeda {
6556441Sjm22469 drd_msg_t *msgp = NULL;
6562309Srsmaeda size_t hdr_size = offsetof(drd_msg_t, data);
6572309Srsmaeda
6582309Srsmaeda switch (cmd) {
6592309Srsmaeda case DRCTL_CPU_CONFIG_REQUEST:
6602309Srsmaeda case DRCTL_CPU_CONFIG_NOTIFY:
6612309Srsmaeda case DRCTL_CPU_UNCONFIG_REQUEST:
6622309Srsmaeda case DRCTL_CPU_UNCONFIG_NOTIFY:
6637899SJames.Marks@Sun.COM *data_size = count * sizeof (drctl_rsrc_t);
6646441Sjm22469 break;
66510106SJason.Beloro@Sun.COM case DRCTL_MEM_CONFIG_REQUEST:
66610106SJason.Beloro@Sun.COM case DRCTL_MEM_CONFIG_NOTIFY:
66710106SJason.Beloro@Sun.COM case DRCTL_MEM_UNCONFIG_REQUEST:
66810106SJason.Beloro@Sun.COM case DRCTL_MEM_UNCONFIG_NOTIFY:
66910106SJason.Beloro@Sun.COM *data_size = count * sizeof (drctl_rsrc_t);
67010106SJason.Beloro@Sun.COM break;
6716441Sjm22469 case DRCTL_IO_CONFIG_REQUEST:
6726441Sjm22469 case DRCTL_IO_CONFIG_NOTIFY:
6736441Sjm22469 case DRCTL_IO_UNCONFIG_REQUEST:
6746441Sjm22469 case DRCTL_IO_UNCONFIG_NOTIFY:
6757899SJames.Marks@Sun.COM *data_size = sizeof (drctl_rsrc_t) +
6766441Sjm22469 strlen(((drctl_rsrc_t *)data)->res_dev_path);
6776441Sjm22469 break;
6786441Sjm22469 default:
6796441Sjm22469 cmn_err(CE_WARN,
6806441Sjm22469 "drctl: pack_message received invalid cmd %d", cmd);
6816441Sjm22469 break;
6826441Sjm22469 }
6832309Srsmaeda
6846441Sjm22469 if (data_size) {
6857899SJames.Marks@Sun.COM *osize = hdr_size + *data_size;
6862309Srsmaeda msgp = kmem_alloc(*osize, KM_SLEEP);
6877899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
6887899SJames.Marks@Sun.COM __func__, (void *)msgp, *osize);
6892309Srsmaeda msgp->cmd = cmd;
6902309Srsmaeda msgp->count = count;
6912309Srsmaeda msgp->flags = flags;
6927899SJames.Marks@Sun.COM bcopy(data, msgp->data, *data_size);
6932309Srsmaeda }
6942309Srsmaeda
6952309Srsmaeda return (msgp);
6962309Srsmaeda }
69711185SSean.McEnroe@Sun.COM
69811185SSean.McEnroe@Sun.COM /*
699*12260SHaik.Aftandilian@Sun.COM * Depending on the should_block argument, either wait for ongoing DR
700*12260SHaik.Aftandilian@Sun.COM * operations to finish and then block subsequent operations, or if a DR
701*12260SHaik.Aftandilian@Sun.COM * operation is already in progress, return EBUSY immediately without
702*12260SHaik.Aftandilian@Sun.COM * blocking subsequent DR operations.
70311185SSean.McEnroe@Sun.COM */
704*12260SHaik.Aftandilian@Sun.COM static int
drctl_block_conditional(boolean_t should_block)705*12260SHaik.Aftandilian@Sun.COM drctl_block_conditional(boolean_t should_block)
70611185SSean.McEnroe@Sun.COM {
707*12260SHaik.Aftandilian@Sun.COM mutex_enter(&drctlp->drc_lock);
708*12260SHaik.Aftandilian@Sun.COM /* If DR in progress and should_block is false, return */
709*12260SHaik.Aftandilian@Sun.COM if (!should_block && drctlp->drc_busy != NULL) {
710*12260SHaik.Aftandilian@Sun.COM mutex_exit(&drctlp->drc_lock);
711*12260SHaik.Aftandilian@Sun.COM return (EBUSY);
712*12260SHaik.Aftandilian@Sun.COM }
713*12260SHaik.Aftandilian@Sun.COM
71411185SSean.McEnroe@Sun.COM /* Wait for any in progress DR operation to complete */
71511185SSean.McEnroe@Sun.COM while (drctlp->drc_busy != NULL)
71611185SSean.McEnroe@Sun.COM (void) cv_wait_sig(&drctlp->drc_busy_cv, &drctlp->drc_lock);
717*12260SHaik.Aftandilian@Sun.COM
71811185SSean.McEnroe@Sun.COM /* Mark the link busy */
71911185SSean.McEnroe@Sun.COM drctlp->drc_busy = (drctl_cookie_t)-1;
72011185SSean.McEnroe@Sun.COM drctlp->drc_cmd = DRCTL_DRC_BLOCK;
72111185SSean.McEnroe@Sun.COM drctlp->drc_flags = 0;
72211185SSean.McEnroe@Sun.COM mutex_exit(&drctlp->drc_lock);
723*12260SHaik.Aftandilian@Sun.COM return (0);
724*12260SHaik.Aftandilian@Sun.COM }
725*12260SHaik.Aftandilian@Sun.COM
726*12260SHaik.Aftandilian@Sun.COM /*
727*12260SHaik.Aftandilian@Sun.COM * Wait for ongoing DR operations to finish, block subsequent operations.
728*12260SHaik.Aftandilian@Sun.COM */
729*12260SHaik.Aftandilian@Sun.COM void
drctl_block(void)730*12260SHaik.Aftandilian@Sun.COM drctl_block(void)
731*12260SHaik.Aftandilian@Sun.COM {
732*12260SHaik.Aftandilian@Sun.COM (void) drctl_block_conditional(B_TRUE);
733*12260SHaik.Aftandilian@Sun.COM }
734*12260SHaik.Aftandilian@Sun.COM
735*12260SHaik.Aftandilian@Sun.COM /*
736*12260SHaik.Aftandilian@Sun.COM * If a DR operation is already in progress, return EBUSY immediately
737*12260SHaik.Aftandilian@Sun.COM * without blocking subsequent DR operations.
738*12260SHaik.Aftandilian@Sun.COM */
739*12260SHaik.Aftandilian@Sun.COM int
drctl_tryblock(void)740*12260SHaik.Aftandilian@Sun.COM drctl_tryblock(void)
741*12260SHaik.Aftandilian@Sun.COM {
742*12260SHaik.Aftandilian@Sun.COM return (drctl_block_conditional(B_FALSE));
74311185SSean.McEnroe@Sun.COM }
74411185SSean.McEnroe@Sun.COM
74511185SSean.McEnroe@Sun.COM /*
74611185SSean.McEnroe@Sun.COM * Unblock DR operations
74711185SSean.McEnroe@Sun.COM */
74811185SSean.McEnroe@Sun.COM void
drctl_unblock(void)74911185SSean.McEnroe@Sun.COM drctl_unblock(void)
75011185SSean.McEnroe@Sun.COM {
75111185SSean.McEnroe@Sun.COM /* Mark the link free */
75211185SSean.McEnroe@Sun.COM mutex_enter(&drctlp->drc_lock);
75311185SSean.McEnroe@Sun.COM drctlp->drc_cmd = -1;
75411185SSean.McEnroe@Sun.COM drctlp->drc_flags = 0;
75511185SSean.McEnroe@Sun.COM drctlp->drc_busy = NULL;
75611185SSean.McEnroe@Sun.COM cv_broadcast(&drctlp->drc_busy_cv);
75711185SSean.McEnroe@Sun.COM mutex_exit(&drctlp->drc_lock);
75811185SSean.McEnroe@Sun.COM }
759