xref: /illumos-gate/usr/src/uts/sun4v/io/drctl.c (revision 0bb073995ac5a95bd35f2dd790df1ea3d8c2d507)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 /*
29  * DR control module for LDoms
30  */
31 
32 #include <sys/sysmacros.h>
33 #include <sys/modctl.h>
34 #include <sys/conf.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/ddi_impldefs.h>
38 #include <sys/stat.h>
39 #include <sys/door.h>
40 #include <sys/open.h>
41 #include <sys/note.h>
42 #include <sys/ldoms.h>
43 #include <sys/dr_util.h>
44 #include <sys/drctl.h>
45 #include <sys/drctl_impl.h>
46 
47 
48 static int drctl_attach(dev_info_t *, ddi_attach_cmd_t);
49 static int drctl_detach(dev_info_t *, ddi_detach_cmd_t);
50 static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
51 
52 static int drctl_open(dev_t *, int, int, cred_t *);
53 static int drctl_close(dev_t, int, int, cred_t *);
54 static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
55 
56 static void *pack_message(int, int, int, void *, size_t *);
57 static int send_message(void *, size_t, void **, size_t *);
58 
59 
60 /*
61  * Configuration data structures
62  */
63 static struct cb_ops drctl_cb_ops = {
64 	drctl_open,		/* open */
65 	drctl_close,		/* close */
66 	nodev,			/* strategy */
67 	nodev,			/* print */
68 	nodev,			/* dump */
69 	nodev,			/* read */
70 	nodev,			/* write */
71 	drctl_ioctl,		/* ioctl */
72 	nodev,			/* devmap */
73 	nodev,			/* mmap */
74 	nodev,			/* segmap */
75 	nochpoll,		/* poll */
76 	ddi_prop_op,		/* prop_op */
77 	NULL,			/* streamtab */
78 	D_MP | D_NEW,		/* driver compatibility flag */
79 	CB_REV,			/* cb_ops revision */
80 	nodev,			/* async read */
81 	nodev			/* async write */
82 };
83 
84 
85 static struct dev_ops drctl_ops = {
86 	DEVO_REV,		/* devo_rev */
87 	0,			/* refcnt */
88 	drctl_getinfo,		/* info */
89 	nulldev,		/* identify */
90 	nulldev,		/* probe */
91 	drctl_attach,		/* attach */
92 	drctl_detach,		/* detach */
93 	nodev,			/* reset */
94 	&drctl_cb_ops,		/* driver operations */
95 	NULL,			/* bus operations */
96 	NULL,			/* power */
97 	ddi_quiesce_not_needed,		/* quiesce */
98 };
99 
100 static struct modldrv modldrv = {
101 	&mod_driverops,		/* type of module - driver */
102 	"DR Control pseudo driver",
103 	&drctl_ops
104 };
105 
106 static struct modlinkage modlinkage = {
107 	MODREV_1,
108 	&modldrv,
109 	NULL
110 };
111 
112 
113 /*
114  * Locking strategy
115  *
116  * One of the reasons for this module's existence is to serialize
117  * DR requests which might be coming from different sources.  Only
118  * one operation is allowed to be in progress at any given time.
119  *
120  * A single lock word (the 'drc_busy' element below) is NULL
121  * when there is no operation in progress.  When a client of this
122  * module initiates an operation it grabs the mutex 'drc_lock' in
123  * order to examine the lock word ('drc_busy').  If no other
124  * operation is in progress, the lock word will be NULL.  If so,
125  * a cookie which uniquely identifies the requestor is stored in
126  * the lock word, and the mutex is released.  Attempts by other
127  * clients to initiate an operation will fail.
128  *
129  * When the lock-holding client's operation is completed, the
130  * client will call a "finalize" function in this module, providing
131  * the cookie passed with the original request.  Since the cookie
132  * matches, the operation will succeed and the lock word will be
133  * cleared.  At this point, an new operation may be initiated.
134  */
135 
136 /*
137  * Driver private data
138  */
139 static struct drctl_unit {
140 	kmutex_t		drc_lock;	/* global driver lock */
141 	dev_info_t		*drc_dip;	/* dev_info pointer */
142 	kcondvar_t		drc_busy_cv;	/* block for !busy */
143 	drctl_cookie_t		drc_busy;	/* NULL if free else a unique */
144 						/* identifier for caller */
145 	int			drc_cmd;	/* the cmd underway (or -1) */
146 	int			drc_flags;	/* saved flag from above cmd */
147 	int			drc_inst;	/* our single instance */
148 	uint_t			drc_state;	/* driver state */
149 } drctl_state;
150 
151 static struct drctl_unit *drctlp = &drctl_state;
152 
153 int
154 _init(void)
155 {
156 	int rv;
157 
158 	drctlp->drc_inst = -1;
159 	mutex_init(&drctlp->drc_lock, NULL, MUTEX_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 
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  * This driver guarantees that if drctl_config_init returns 0,
277  * a valid response buffer will be passed back to the caller.  This
278  * routine can be used to generate that response in cases where the
279  * upcall has not resulted in a response message from userland.
280  */
281 static drctl_rsrc_t *
282 drctl_generate_resp(drctl_rsrc_t *res,
283     int count, size_t *rsize, drctl_status_t status)
284 {
285 	int		idx;
286 	size_t		size;
287 	drctl_rsrc_t	*rbuf;
288 
289 	size = count * sizeof (*res);
290 	rbuf  = kmem_alloc(size, KM_SLEEP);
291 
292 	bcopy(res, rbuf, size);
293 
294 	for (idx = 0; idx < count; idx++) {
295 		rbuf[idx].status = status;
296 		rbuf[idx].offset = 0;
297 	}
298 
299 	*rsize = size;
300 	return (rbuf);
301 }
302 
303 static int
304 drctl_config_common(int cmd, int flags, drctl_rsrc_t *res,
305     int count, drctl_rsrc_t **rbuf, size_t *rsize)
306 {
307 	int	rv = 0;
308 	size_t	size;
309 	char	*bufp;
310 	static const char me[] = "drctl_config_common";
311 
312 	switch (cmd) {
313 	case DRCTL_CPU_CONFIG_REQUEST:
314 	case DRCTL_CPU_CONFIG_NOTIFY:
315 	case DRCTL_CPU_UNCONFIG_REQUEST:
316 	case DRCTL_CPU_UNCONFIG_NOTIFY:
317 	case DRCTL_IO_UNCONFIG_REQUEST:
318 	case DRCTL_IO_UNCONFIG_NOTIFY:
319 	case DRCTL_IO_CONFIG_REQUEST:
320 	case DRCTL_IO_CONFIG_NOTIFY:
321 		rv = 0;
322 		break;
323 	case DRCTL_MEM_CONFIG_REQUEST:
324 	case DRCTL_MEM_CONFIG_NOTIFY:
325 	case DRCTL_MEM_UNCONFIG_REQUEST:
326 	case DRCTL_MEM_UNCONFIG_NOTIFY:
327 		rv = ENOTSUP;
328 		break;
329 	}
330 
331 	if (rv != 0) {
332 		DR_DBG_CTL("%s: invalid cmd %d\n", me, cmd);
333 		return (rv);
334 	}
335 
336 	/*
337 	 * If the operation is a FORCE, we don't send a message to
338 	 * the daemon.  But, the upstream clients still expect a
339 	 * response, so generate a response with all ops 'allowed'.
340 	 */
341 	if (flags == DRCTL_FLAG_FORCE) {
342 		if (rbuf != NULL) {
343 			*rbuf = drctl_generate_resp(res, count, &size,
344 			    DRCTL_STATUS_ALLOW);
345 			*rsize = size;
346 		}
347 		return (0);
348 	}
349 
350 	bufp = pack_message(cmd, flags, count, (void *)res, &size);
351 	DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n",
352 	    me, (void *)bufp, size);
353 	if (bufp == NULL || size == 0)
354 		return (EIO);
355 
356 	rv = send_message(bufp, size, (void **)rbuf, rsize);
357 
358 	/*
359 	 * For failure, as part of our contract with the caller,
360 	 * generate a response message, but mark all proposed
361 	 * changes as 'denied'.
362 	 */
363 	if (rv != 0 && rbuf != NULL) {
364 		*rbuf = drctl_generate_resp(res, count, &size,
365 		    DRCTL_STATUS_DENY);
366 		*rsize = size;
367 	}
368 
369 	return (rv);
370 }
371 
372 /*
373  * Since the response comes from userland, make sure it is
374  * at least the minimum size and, if it contains error
375  * strings, that the string area is null-terminated.
376  */
377 static int
378 verify_response(int count, drctl_rsrc_t *resp, size_t size)
379 {
380 	int idx;
381 	int need_terminator = 0;
382 	static const char me[] = "verify_response";
383 
384 	if (resp == NULL || size < count * sizeof (*resp)) {
385 		DR_DBG_CTL("%s: BAD size - count %d size %ld\n",
386 		    me, count, size);
387 		return (EIO);
388 	}
389 
390 	for (idx = 0; idx < count; idx++) {
391 
392 		if (resp[idx].offset != 0)
393 			need_terminator++;
394 	}
395 
396 	if (need_terminator && *((caddr_t)(resp) + size - 1) != '\0') {
397 		DR_DBG_CTL("%s: unterm. strings: resp %p size %ld char %d\n",
398 		    me, (void *)resp, size, *((caddr_t)(resp) + size - 1));
399 		/* Don't fail the transaction, but don't advertise strings */
400 		for (idx = 0; idx < count; idx++)
401 			resp[idx].offset = 0;
402 	}
403 
404 	return (0);
405 }
406 
407 
408 /*
409  * Prepare for a reconfig operation.
410  */
411 int
412 drctl_config_init(int cmd, int flags, drctl_rsrc_t *res,
413     int count, drctl_rsrc_t **rbuf, size_t *rsize, drctl_cookie_t ck)
414 {
415 	static char me[] = "drctl_config_init";
416 	int idx;
417 	int rv;
418 
419 	if (ck == 0)
420 		return (EINVAL);
421 
422 	mutex_enter(&drctlp->drc_lock);
423 
424 	if (drctlp->drc_busy != NULL) {
425 		mutex_exit(&drctlp->drc_lock);
426 		return (EBUSY);
427 	}
428 
429 	DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n",
430 	    me, cmd, flags, (void *)res, count);
431 
432 	/* Mark the link busy.  Below we will fill in the actual cookie. */
433 	drctlp->drc_busy = (drctl_cookie_t)-1;
434 	mutex_exit(&drctlp->drc_lock);
435 
436 	if ((rv = drctl_config_common(cmd,
437 	    flags, res, count, rbuf, rsize)) == 0 &&
438 	    verify_response(count, *rbuf, *rsize) == 0) {
439 		drctlp->drc_busy = ck;
440 		drctlp->drc_cmd = cmd;
441 		drctlp->drc_flags = flags;
442 
443 		/*
444 		 * If there wasn't a valid response msg passed back,
445 		 * create a response with each resource op denied.
446 		 */
447 		if (*rbuf == NULL || *rsize == 0) {
448 			drctl_rsrc_t *bp = *rbuf;
449 
450 			*rsize = count * sizeof (*bp);
451 			bp = kmem_zalloc(*rsize, KM_SLEEP);
452 			bcopy(res, bp, *rsize);
453 
454 			for (idx = 0; idx < count; idx++) {
455 				bp[idx].status = DRCTL_STATUS_DENY;
456 				bp[idx].offset = 0;
457 			}
458 		}
459 	} else {
460 		drctlp->drc_cmd = -1;
461 		drctlp->drc_flags = 0;
462 		drctlp->drc_busy = NULL;
463 	}
464 
465 	return (rv);
466 }
467 
468 /*
469  * Complete a reconfig operation.
470  */
471 int
472 drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count)
473 {
474 	int rv;
475 	int notify_cmd;
476 	int flags;
477 
478 	mutex_enter(&drctlp->drc_lock);
479 
480 	if (drctlp->drc_busy != ck) {
481 		mutex_exit(&drctlp->drc_lock);
482 		return (EBUSY);
483 	}
484 
485 	mutex_exit(&drctlp->drc_lock);
486 
487 	flags = drctlp->drc_flags;
488 	/*
489 	 * Flip the saved _REQUEST command to its corresponding
490 	 * _NOTIFY command.
491 	 */
492 	switch (drctlp->drc_cmd) {
493 	case DRCTL_CPU_CONFIG_REQUEST:
494 		notify_cmd = DRCTL_CPU_CONFIG_NOTIFY;
495 		break;
496 
497 	case DRCTL_CPU_UNCONFIG_REQUEST:
498 		notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY;
499 		break;
500 
501 	case DRCTL_IO_UNCONFIG_REQUEST:
502 		notify_cmd = DRCTL_IO_UNCONFIG_NOTIFY;
503 		break;
504 
505 	case DRCTL_IO_CONFIG_REQUEST:
506 		notify_cmd = DRCTL_IO_CONFIG_NOTIFY;
507 		break;
508 
509 	case DRCTL_MEM_CONFIG_REQUEST:
510 	case DRCTL_MEM_CONFIG_NOTIFY:
511 	case DRCTL_MEM_UNCONFIG_REQUEST:
512 	case DRCTL_MEM_UNCONFIG_NOTIFY:
513 	default:
514 		/* none of the above should have been accepted in _init */
515 		ASSERT(0);
516 		cmn_err(CE_CONT,
517 		    "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd);
518 		rv = EINVAL;
519 		goto done;
520 	}
521 
522 	rv = drctl_config_common(notify_cmd, flags, res, count, NULL, 0);
523 
524 done:
525 	drctlp->drc_cmd = -1;
526 	drctlp->drc_flags = 0;
527 	drctlp->drc_busy = NULL;
528 
529 	return (rv);
530 }
531 
532 static int
533 drctl_ioctl(dev_t dev,
534     int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
535 {
536 	_NOTE(ARGUNUSED(dev, mode, cred_p, rval_p))
537 
538 	int rv;
539 
540 	switch (cmd) {
541 	case DRCTL_IOCTL_CONNECT_SERVER:
542 		rv = i_drctl_ioctl(cmd, arg);
543 		break;
544 	default:
545 		rv = ENOTSUP;
546 	}
547 
548 	*rval_p = (rv == 0) ? 0 : -1;
549 
550 	return (rv);
551 }
552 
553 /*
554  * Accept a preformatted request from caller and send a message to
555  * the daemon.  A pointer to the daemon's response buffer is passed
556  * back in obufp, its size in osize.
557  */
558 static int
559 send_message(void *msg, size_t size, void **obufp, size_t *osize)
560 {
561 	int rv;
562 
563 	rv = i_drctl_send(msg, size, obufp, osize);
564 
565 	kmem_free(msg, size);
566 
567 	return (rv);
568 }
569 
570 static void *
571 pack_message(int cmd, int flags, int count, void *data, size_t *osize)
572 {
573 	drd_msg_t *msgp = NULL;
574 	size_t hdr_size = offsetof(drd_msg_t, data);
575 	size_t data_size = 0;
576 
577 	switch (cmd) {
578 	case DRCTL_CPU_CONFIG_REQUEST:
579 	case DRCTL_CPU_CONFIG_NOTIFY:
580 	case DRCTL_CPU_UNCONFIG_REQUEST:
581 	case DRCTL_CPU_UNCONFIG_NOTIFY:
582 		data_size = count * sizeof (drctl_rsrc_t);
583 		break;
584 	case DRCTL_IO_CONFIG_REQUEST:
585 	case DRCTL_IO_CONFIG_NOTIFY:
586 	case DRCTL_IO_UNCONFIG_REQUEST:
587 	case DRCTL_IO_UNCONFIG_NOTIFY:
588 		data_size = sizeof (drctl_rsrc_t) +
589 		    strlen(((drctl_rsrc_t *)data)->res_dev_path);
590 		break;
591 	default:
592 		cmn_err(CE_WARN,
593 		    "drctl: pack_message received invalid cmd %d", cmd);
594 		break;
595 	}
596 
597 	if (data_size) {
598 		*osize = hdr_size + data_size;
599 		msgp = kmem_alloc(*osize, KM_SLEEP);
600 		msgp->cmd = cmd;
601 		msgp->count = count;
602 		msgp->flags = flags;
603 		bcopy(data, msgp->data, data_size);
604 	}
605 
606 	return (msgp);
607 }
608