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