xref: /titanic_41/usr/src/uts/sun4v/io/drctl.c (revision 7a17cfad7ff3427e1ce7ecdbf566e442a7025ec9)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * DR control module for LDoms
29  */
30 
31 #include <sys/sysmacros.h>
32 #include <sys/modctl.h>
33 #include <sys/conf.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/ddi_impldefs.h>
37 #include <sys/stat.h>
38 #include <sys/door.h>
39 #include <sys/open.h>
40 #include <sys/note.h>
41 #include <sys/ldoms.h>
42 #include <sys/dr_util.h>
43 #include <sys/drctl.h>
44 #include <sys/drctl_impl.h>
45 
46 
47 static int drctl_attach(dev_info_t *, ddi_attach_cmd_t);
48 static int drctl_detach(dev_info_t *, ddi_detach_cmd_t);
49 static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
50 
51 static int drctl_open(dev_t *, int, int, cred_t *);
52 static int drctl_close(dev_t, int, int, cred_t *);
53 static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
54 
55 static void *pack_message(int, int, int, void *, size_t *, size_t *);
56 static int send_message(void *, size_t, drctl_resp_t **, size_t *);
57 
58 
59 /*
60  * Configuration data structures
61  */
62 static struct cb_ops drctl_cb_ops = {
63 	drctl_open,		/* open */
64 	drctl_close,		/* close */
65 	nodev,			/* strategy */
66 	nodev,			/* print */
67 	nodev,			/* dump */
68 	nodev,			/* read */
69 	nodev,			/* write */
70 	drctl_ioctl,		/* ioctl */
71 	nodev,			/* devmap */
72 	nodev,			/* mmap */
73 	nodev,			/* segmap */
74 	nochpoll,		/* poll */
75 	ddi_prop_op,		/* prop_op */
76 	NULL,			/* streamtab */
77 	D_MP | D_NEW,		/* driver compatibility flag */
78 	CB_REV,			/* cb_ops revision */
79 	nodev,			/* async read */
80 	nodev			/* async write */
81 };
82 
83 
84 static struct dev_ops drctl_ops = {
85 	DEVO_REV,		/* devo_rev */
86 	0,			/* refcnt */
87 	drctl_getinfo,		/* info */
88 	nulldev,		/* identify */
89 	nulldev,		/* probe */
90 	drctl_attach,		/* attach */
91 	drctl_detach,		/* detach */
92 	nodev,			/* reset */
93 	&drctl_cb_ops,		/* driver operations */
94 	NULL,			/* bus operations */
95 	NULL,			/* power */
96 	ddi_quiesce_not_needed,		/* quiesce */
97 };
98 
99 static struct modldrv modldrv = {
100 	&mod_driverops,		/* type of module - driver */
101 	"DR Control pseudo driver",
102 	&drctl_ops
103 };
104 
105 static struct modlinkage modlinkage = {
106 	MODREV_1,
107 	&modldrv,
108 	NULL
109 };
110 
111 
112 /*
113  * Locking strategy
114  *
115  * One of the reasons for this module's existence is to serialize
116  * DR requests which might be coming from different sources.  Only
117  * one operation is allowed to be in progress at any given time.
118  *
119  * A single lock word (the 'drc_busy' element below) is NULL
120  * when there is no operation in progress.  When a client of this
121  * module initiates an operation it grabs the mutex 'drc_lock' in
122  * order to examine the lock word ('drc_busy').  If no other
123  * operation is in progress, the lock word will be NULL.  If so,
124  * a cookie which uniquely identifies the requestor is stored in
125  * the lock word, and the mutex is released.  Attempts by other
126  * clients to initiate an operation will fail.
127  *
128  * When the lock-holding client's operation is completed, the
129  * client will call a "finalize" function in this module, providing
130  * the cookie passed with the original request.  Since the cookie
131  * matches, the operation will succeed and the lock word will be
132  * cleared.  At this point, an new operation may be initiated.
133  */
134 
135 /*
136  * Driver private data
137  */
138 static struct drctl_unit {
139 	kmutex_t		drc_lock;	/* global driver lock */
140 	dev_info_t		*drc_dip;	/* dev_info pointer */
141 	kcondvar_t		drc_busy_cv;	/* block for !busy */
142 	drctl_cookie_t		drc_busy;	/* NULL if free else a unique */
143 						/* identifier for caller */
144 	int			drc_cmd;	/* the cmd underway (or -1) */
145 	int			drc_flags;	/* saved flag from above cmd */
146 	int			drc_inst;	/* our single instance */
147 	uint_t			drc_state;	/* driver state */
148 } drctl_state;
149 
150 static struct drctl_unit *drctlp = &drctl_state;
151 
152 int
153 _init(void)
154 {
155 	int rv;
156 
157 	drctlp->drc_inst = -1;
158 	mutex_init(&drctlp->drc_lock, NULL, MUTEX_DRIVER, NULL);
159 	cv_init(&drctlp->drc_busy_cv, NULL, CV_DRIVER, NULL);
160 
161 	if ((rv = mod_install(&modlinkage)) != 0)
162 		mutex_destroy(&drctlp->drc_lock);
163 
164 	return (rv);
165 }
166 
167 
168 int
169 _fini(void)
170 {
171 	int rv;
172 
173 	if ((rv = mod_remove(&modlinkage)) != 0)
174 		return (rv);
175 	cv_destroy(&drctlp->drc_busy_cv);
176 	mutex_destroy(&drctlp->drc_lock);
177 	return (0);
178 }
179 
180 
181 int
182 _info(struct modinfo *modinfop)
183 {
184 	return (mod_info(&modlinkage, modinfop));
185 }
186 
187 
188 /*
189  * Do the attach work
190  */
191 static int
192 drctl_do_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
193 {
194 	_NOTE(ARGUNUSED(cmd))
195 
196 	char *str = "drctl_do_attach";
197 	int retval = DDI_SUCCESS;
198 
199 	if (drctlp->drc_inst != -1) {
200 		cmn_err(CE_WARN, "%s: an instance is already attached!", str);
201 		return (DDI_FAILURE);
202 	}
203 	drctlp->drc_inst = ddi_get_instance(dip);
204 
205 	retval = ddi_create_minor_node(dip, "drctl", S_IFCHR,
206 	    drctlp->drc_inst, DDI_PSEUDO, 0);
207 	if (retval != DDI_SUCCESS) {
208 		cmn_err(CE_WARN, "%s: can't create minor node", str);
209 		drctlp->drc_inst = -1;
210 		return (retval);
211 	}
212 
213 	drctlp->drc_dip = dip;
214 	ddi_report_dev(dip);
215 
216 	return (retval);
217 }
218 
219 
220 static int
221 drctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
222 {
223 	switch (cmd) {
224 	case DDI_ATTACH:
225 		return (drctl_do_attach(dip, cmd));
226 
227 	default:
228 		return (DDI_FAILURE);
229 	}
230 }
231 
232 
233 /* ARGSUSED */
234 static int
235 drctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
236 {
237 	switch (cmd) {
238 	case DDI_DETACH:
239 		drctlp->drc_inst = -1;
240 		ddi_remove_minor_node(dip, "drctl");
241 		return (DDI_SUCCESS);
242 
243 	default:
244 		return (DDI_FAILURE);
245 	}
246 }
247 
248 static int
249 drctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
250 {
251 	_NOTE(ARGUNUSED(dip, cmd, arg, resultp))
252 
253 	return (0);
254 }
255 
256 static int
257 drctl_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
258 {
259 	_NOTE(ARGUNUSED(devp, flag, cred_p))
260 
261 	if (otyp != OTYP_CHR)
262 		return (EINVAL);
263 
264 	return (0);
265 }
266 
267 static int
268 drctl_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
269 {
270 	_NOTE(ARGUNUSED(dev, flag, otyp, cred_p))
271 
272 	return (0);
273 }
274 
275 /*
276  * Create a reponse structure which includes an array of drctl_rsrc_t
277  * structures in which each status element is set to the 'status'
278  * arg.  There is no error text, so set the 'offset' elements to 0.
279  */
280 static drctl_resp_t *
281 drctl_generate_resp(drctl_rsrc_t *res,
282     int count, size_t *rsize, drctl_status_t status)
283 {
284 	int		i;
285 	size_t		size;
286 	drctl_rsrc_t	*rsrc;
287 	drctl_resp_t	*resp;
288 
289 	size = offsetof(drctl_resp_t, resp_resources) + (count * sizeof (*res));
290 	resp  = kmem_alloc(size, KM_SLEEP);
291 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
292 	    __func__, (void *)resp, size);
293 
294 	resp->resp_type = DRCTL_RESP_OK;
295 	rsrc = resp->resp_resources;
296 
297 	bcopy(res, rsrc, count * sizeof (*res));
298 
299 	for (i = 0; i < count; i++) {
300 		rsrc[i].status = status;
301 		rsrc[i].offset = 0;
302 	}
303 
304 	*rsize = size;
305 
306 	return (resp);
307 }
308 
309 /*
310  * Generate an error response message.
311  */
312 static drctl_resp_t *
313 drctl_generate_err_resp(char *msg, size_t *size)
314 {
315 	drctl_resp_t	*resp;
316 
317 	ASSERT(msg != NULL);
318 	ASSERT(size != NULL);
319 
320 	*size = offsetof(drctl_resp_t, resp_err_msg) + strlen(msg) + 1;
321 	resp = kmem_alloc(*size, KM_SLEEP);
322 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
323 	    __func__, (void *)resp, *size);
324 
325 	resp->resp_type = DRCTL_RESP_ERR;
326 	(void) strcpy(resp->resp_err_msg, msg);
327 
328 	return (resp);
329 }
330 
331 /*
332  * Since response comes from userland, verify that it is at least the
333  * minimum size based on the size of the original request.  Verify
334  * that any offsets to error strings are within the string area of
335  * the response and, force the string area to be null-terminated.
336  */
337 static int
338 verify_response(int cmd,
339     int count, drctl_resp_t *resp, size_t sent_len, size_t resp_len)
340 {
341 	drctl_rsrc_t *rsrc = resp->resp_resources;
342 	size_t rcvd_len = resp_len - (offsetof(drctl_resp_t, resp_resources));
343 	int is_cpu = 0;
344 	int i;
345 
346 	switch (cmd) {
347 	case DRCTL_CPU_CONFIG_REQUEST:
348 	case DRCTL_CPU_UNCONFIG_REQUEST:
349 		if (rcvd_len < sent_len)
350 			return (EIO);
351 		is_cpu = 1;
352 		break;
353 	case DRCTL_IO_UNCONFIG_REQUEST:
354 	case DRCTL_IO_CONFIG_REQUEST:
355 		if (count != 1)
356 			return (EIO);
357 		break;
358 	case DRCTL_MEM_CONFIG_REQUEST:
359 	case DRCTL_MEM_UNCONFIG_REQUEST:
360 		break;
361 	default:
362 		return (EIO);
363 	}
364 
365 	for (i = 0; i < count; i++)
366 		if ((rsrc[i].offset > 0) &&
367 		    /* string can't be inside the bounds of original request */
368 		    (((rsrc[i].offset < sent_len) && is_cpu) ||
369 		    /* string must start inside the message */
370 		    (rsrc[i].offset >= rcvd_len)))
371 			return (EIO);
372 
373 	/* If there are any strings, terminate the string area. */
374 	if (rcvd_len > sent_len)
375 		*((char *)rsrc + rcvd_len - 1) = '\0';
376 
377 	return (0);
378 }
379 
380 static int
381 drctl_config_common(int cmd, int flags, drctl_rsrc_t *res,
382     int count, drctl_resp_t **rbuf, size_t *rsize, size_t *rq_size)
383 {
384 	int	rv = 0;
385 	size_t	size;
386 	char	*bufp;
387 
388 	switch (cmd) {
389 	case DRCTL_CPU_CONFIG_REQUEST:
390 	case DRCTL_CPU_CONFIG_NOTIFY:
391 	case DRCTL_CPU_UNCONFIG_REQUEST:
392 	case DRCTL_CPU_UNCONFIG_NOTIFY:
393 	case DRCTL_IO_UNCONFIG_REQUEST:
394 	case DRCTL_IO_UNCONFIG_NOTIFY:
395 	case DRCTL_IO_CONFIG_REQUEST:
396 	case DRCTL_IO_CONFIG_NOTIFY:
397 	case DRCTL_MEM_CONFIG_REQUEST:
398 	case DRCTL_MEM_CONFIG_NOTIFY:
399 	case DRCTL_MEM_UNCONFIG_REQUEST:
400 	case DRCTL_MEM_UNCONFIG_NOTIFY:
401 		rv = 0;
402 		break;
403 	default:
404 		rv = ENOTSUP;
405 		break;
406 	}
407 
408 	if (rv != 0) {
409 		DR_DBG_CTL("%s: invalid cmd %d\n", __func__, cmd);
410 		return (rv);
411 	}
412 
413 	/*
414 	 * If the operation is a FORCE, we don't send a message to
415 	 * the daemon.  But, the upstream clients still expect a
416 	 * response, so generate a response with all ops 'allowed'.
417 	 */
418 	if (flags == DRCTL_FLAG_FORCE) {
419 		if (rbuf != NULL)
420 			*rbuf = drctl_generate_resp(res,
421 			    count, rsize, DRCTL_STATUS_ALLOW);
422 		return (0);
423 	}
424 
425 	bufp = pack_message(cmd, flags, count, (void *)res, &size, rq_size);
426 	DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n",
427 	    __func__, (void *)bufp, size);
428 
429 	if (bufp == NULL || size == 0)
430 		return (EINVAL);
431 
432 	return (send_message(bufp, size, rbuf, rsize));
433 }
434 
435 /*
436  * Prepare for a reconfig operation.
437  */
438 int
439 drctl_config_init(int cmd, int flags, drctl_rsrc_t *res,
440     int count, drctl_resp_t **rbuf, size_t *rsize, drctl_cookie_t ck)
441 {
442 	static char inval_msg[] = "Invalid command format received.\n";
443 	static char unsup_msg[] = "Unsuppported command received.\n";
444 	static char unk_msg  [] = "Failure reason unknown.\n";
445 	static char rsp_msg  [] = "Invalid response from "
446 	    "reconfiguration daemon.\n";
447 	static char drd_msg  [] = "Cannot communicate with reconfiguration "
448 	    "daemon (drd) in target domain.\n"
449 	    "drd(1M) SMF service may not be enabled.\n";
450 	static char busy_msg [] = "Busy executing earlier command; "
451 	    "please try again later.\n";
452 	size_t rq_size;
453 	char *ermsg;
454 	int rv;
455 
456 	if (ck == 0) {
457 		*rbuf = drctl_generate_err_resp(inval_msg, rsize);
458 
459 		return (EINVAL);
460 	}
461 
462 	mutex_enter(&drctlp->drc_lock);
463 	if (drctlp->drc_busy != NULL) {
464 		mutex_exit(&drctlp->drc_lock);
465 		*rbuf = drctl_generate_err_resp(busy_msg, rsize);
466 
467 		return (EBUSY);
468 	}
469 
470 	DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n",
471 	    __func__, cmd, flags, (void *)res, count);
472 
473 	/* Mark the link busy.  Below we will fill in the actual cookie. */
474 	drctlp->drc_busy = (drctl_cookie_t)-1;
475 	mutex_exit(&drctlp->drc_lock);
476 
477 	rv = drctl_config_common(cmd, flags, res, count, rbuf, rsize, &rq_size);
478 	if (rv == 0) {
479 		/*
480 		 * If the upcall to the daemon returned successfully, we
481 		 * still need to validate the format of the returned msg.
482 		 */
483 		if ((rv = verify_response(cmd,
484 		    count, *rbuf, rq_size, *rsize)) != 0) {
485 			DR_DBG_KMEM("%s: free addr %p size %ld\n",
486 			    __func__, (void *)*rbuf, *rsize);
487 			kmem_free(*rbuf, *rsize);
488 			*rbuf = drctl_generate_err_resp(rsp_msg, rsize);
489 			drctlp->drc_busy = NULL;
490 			cv_broadcast(&drctlp->drc_busy_cv);
491 		} else { /* message format is valid */
492 			drctlp->drc_busy = ck;
493 			drctlp->drc_cmd = cmd;
494 			drctlp->drc_flags = flags;
495 		}
496 	} else {
497 		switch (rv) {
498 		case ENOTSUP:
499 			ermsg = unsup_msg;
500 			break;
501 		case EIO:
502 			ermsg = drd_msg;
503 			break;
504 		default:
505 			ermsg = unk_msg;
506 			break;
507 		}
508 
509 		*rbuf = drctl_generate_err_resp(ermsg, rsize);
510 
511 		drctlp->drc_cmd = -1;
512 		drctlp->drc_flags = 0;
513 		drctlp->drc_busy = NULL;
514 		cv_broadcast(&drctlp->drc_busy_cv);
515 	}
516 	return (rv);
517 }
518 
519 /*
520  * Complete a reconfig operation.
521  */
522 int
523 drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count)
524 {
525 	int rv;
526 	int notify_cmd;
527 	int flags;
528 	size_t rq_size;
529 
530 	mutex_enter(&drctlp->drc_lock);
531 	if (drctlp->drc_busy != ck) {
532 		mutex_exit(&drctlp->drc_lock);
533 		return (EBUSY);
534 	}
535 	mutex_exit(&drctlp->drc_lock);
536 
537 	flags = drctlp->drc_flags;
538 	/*
539 	 * Flip the saved _REQUEST command to its corresponding
540 	 * _NOTIFY command.
541 	 */
542 	switch (drctlp->drc_cmd) {
543 	case DRCTL_CPU_CONFIG_REQUEST:
544 		notify_cmd = DRCTL_CPU_CONFIG_NOTIFY;
545 		break;
546 
547 	case DRCTL_CPU_UNCONFIG_REQUEST:
548 		notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY;
549 		break;
550 
551 	case DRCTL_IO_UNCONFIG_REQUEST:
552 		notify_cmd = DRCTL_IO_UNCONFIG_NOTIFY;
553 		break;
554 
555 	case DRCTL_IO_CONFIG_REQUEST:
556 		notify_cmd = DRCTL_IO_CONFIG_NOTIFY;
557 		break;
558 
559 	case DRCTL_MEM_CONFIG_REQUEST:
560 		notify_cmd = DRCTL_MEM_CONFIG_NOTIFY;
561 		break;
562 
563 	case DRCTL_MEM_UNCONFIG_REQUEST:
564 		notify_cmd = DRCTL_MEM_UNCONFIG_NOTIFY;
565 		break;
566 
567 	default:
568 		/* none of the above should have been accepted in _init */
569 		ASSERT(0);
570 		cmn_err(CE_CONT,
571 		    "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd);
572 		rv = EINVAL;
573 		goto done;
574 	}
575 
576 	rv = drctl_config_common(notify_cmd,
577 	    flags, res, count, NULL, 0, &rq_size);
578 
579 done:
580 		drctlp->drc_cmd = -1;
581 		drctlp->drc_flags = 0;
582 		drctlp->drc_busy = NULL;
583 		cv_broadcast(&drctlp->drc_busy_cv);
584 		return (rv);
585 }
586 
587 static int
588 drctl_ioctl(dev_t dev,
589     int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
590 {
591 	_NOTE(ARGUNUSED(dev, mode, cred_p, rval_p))
592 
593 	int rv;
594 
595 	switch (cmd) {
596 	case DRCTL_IOCTL_CONNECT_SERVER:
597 		rv = i_drctl_ioctl(cmd, arg);
598 		break;
599 	default:
600 		rv = ENOTSUP;
601 	}
602 
603 	*rval_p = (rv == 0) ? 0 : -1;
604 
605 	return (rv);
606 }
607 
608 /*
609  * Accept a preformatted request from caller and send a message to
610  * the daemon.  A pointer to the daemon's response buffer is passed
611  * back in obufp, its size in osize.
612  */
613 static int
614 send_message(void *msg, size_t size, drctl_resp_t **obufp, size_t *osize)
615 {
616 	drctl_resp_t *bufp;
617 	drctl_rsrc_t *rsrcs;
618 	size_t rsrcs_size;
619 	int rv;
620 
621 	rv = i_drctl_send(msg, size, (void **)&rsrcs, &rsrcs_size);
622 
623 	if ((rv == 0) && ((rsrcs == NULL) ||(rsrcs_size == 0)))
624 		rv = EINVAL;
625 
626 	if (rv == 0) {
627 		if (obufp != NULL) {
628 			ASSERT(osize != NULL);
629 
630 			*osize =
631 			    offsetof(drctl_resp_t, resp_resources) + rsrcs_size;
632 			bufp =
633 			    kmem_alloc(*osize, KM_SLEEP);
634 			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
635 			    __func__, (void *)bufp, *osize);
636 			bufp->resp_type = DRCTL_RESP_OK;
637 			bcopy(rsrcs, bufp->resp_resources, rsrcs_size);
638 			*obufp = bufp;
639 		}
640 
641 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
642 		    __func__, (void *)rsrcs, rsrcs_size);
643 		kmem_free(rsrcs, rsrcs_size);
644 	}
645 
646 	DR_DBG_KMEM("%s:free addr %p size %ld\n", __func__, msg, size);
647 	kmem_free(msg, size);
648 
649 	return (rv);
650 }
651 
652 static void *
653 pack_message(int cmd,
654     int flags, int count, void *data, size_t *osize, size_t *data_size)
655 {
656 	drd_msg_t *msgp = NULL;
657 	size_t hdr_size = offsetof(drd_msg_t, data);
658 
659 	switch (cmd) {
660 	case DRCTL_CPU_CONFIG_REQUEST:
661 	case DRCTL_CPU_CONFIG_NOTIFY:
662 	case DRCTL_CPU_UNCONFIG_REQUEST:
663 	case DRCTL_CPU_UNCONFIG_NOTIFY:
664 		*data_size = count * sizeof (drctl_rsrc_t);
665 		break;
666 	case DRCTL_MEM_CONFIG_REQUEST:
667 	case DRCTL_MEM_CONFIG_NOTIFY:
668 	case DRCTL_MEM_UNCONFIG_REQUEST:
669 	case DRCTL_MEM_UNCONFIG_NOTIFY:
670 		*data_size = count * sizeof (drctl_rsrc_t);
671 		break;
672 	case DRCTL_IO_CONFIG_REQUEST:
673 	case DRCTL_IO_CONFIG_NOTIFY:
674 	case DRCTL_IO_UNCONFIG_REQUEST:
675 	case DRCTL_IO_UNCONFIG_NOTIFY:
676 		*data_size = sizeof (drctl_rsrc_t) +
677 		    strlen(((drctl_rsrc_t *)data)->res_dev_path);
678 		break;
679 	default:
680 		cmn_err(CE_WARN,
681 		    "drctl: pack_message received invalid cmd %d", cmd);
682 		break;
683 	}
684 
685 	if (data_size) {
686 		*osize = hdr_size + *data_size;
687 		msgp = kmem_alloc(*osize, KM_SLEEP);
688 		DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
689 		    __func__, (void *)msgp, *osize);
690 		msgp->cmd = cmd;
691 		msgp->count = count;
692 		msgp->flags = flags;
693 		bcopy(data, msgp->data, *data_size);
694 	}
695 
696 	return (msgp);
697 }
698 
699 /*
700  * Block DR operations
701  */
702 void
703 drctl_block(void)
704 {
705 	/* Wait for any in progress DR operation to complete */
706 	mutex_enter(&drctlp->drc_lock);
707 	while (drctlp->drc_busy != NULL)
708 		(void) cv_wait_sig(&drctlp->drc_busy_cv, &drctlp->drc_lock);
709 	/* Mark the link busy */
710 	drctlp->drc_busy = (drctl_cookie_t)-1;
711 	drctlp->drc_cmd = DRCTL_DRC_BLOCK;
712 	drctlp->drc_flags = 0;
713 	mutex_exit(&drctlp->drc_lock);
714 }
715 
716 /*
717  * Unblock DR operations
718  */
719 void
720 drctl_unblock(void)
721 {
722 	/* Mark the link free */
723 	mutex_enter(&drctlp->drc_lock);
724 	drctlp->drc_cmd = -1;
725 	drctlp->drc_flags = 0;
726 	drctlp->drc_busy = NULL;
727 	cv_broadcast(&drctlp->drc_busy_cv);
728 	mutex_exit(&drctlp->drc_lock);
729 }
730