xref: /illumos-gate/usr/src/uts/sun4v/io/drctl.c (revision 646e55b6807cdf761fecd1e4095d73116cdefdb5)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * DR control module for LDoms
31  */
32 
33 #include <sys/sysmacros.h>
34 #include <sys/modctl.h>
35 #include <sys/conf.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/ddi_impldefs.h>
39 #include <sys/stat.h>
40 #include <sys/door.h>
41 #include <sys/open.h>
42 #include <sys/note.h>
43 #include <sys/ldoms.h>
44 #include <sys/dr_util.h>
45 #include <sys/drctl.h>
46 #include <sys/drctl_impl.h>
47 
48 
49 static int drctl_attach(dev_info_t *, ddi_attach_cmd_t);
50 static int drctl_detach(dev_info_t *, ddi_detach_cmd_t);
51 static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
52 
53 static int drctl_open(dev_t *, int, int, cred_t *);
54 static int drctl_close(dev_t, int, int, cred_t *);
55 static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
56 
57 static void *pack_message(int, int, int, void *, size_t *);
58 static int send_message(void *, size_t, void **, size_t *);
59 
60 
61 /*
62  * Configuration data structures
63  */
64 static struct cb_ops drctl_cb_ops = {
65 	drctl_open,		/* open */
66 	drctl_close,		/* close */
67 	nodev,			/* strategy */
68 	nodev,			/* print */
69 	nodev,			/* dump */
70 	nodev,			/* read */
71 	nodev,			/* write */
72 	drctl_ioctl,		/* ioctl */
73 	nodev,			/* devmap */
74 	nodev,			/* mmap */
75 	nodev,			/* segmap */
76 	nochpoll,		/* poll */
77 	ddi_prop_op,		/* prop_op */
78 	NULL,			/* streamtab */
79 	D_MP | D_NEW,		/* driver compatibility flag */
80 	CB_REV,			/* cb_ops revision */
81 	nodev,			/* async read */
82 	nodev			/* async write */
83 };
84 
85 
86 static struct dev_ops drctl_ops = {
87 	DEVO_REV,		/* devo_rev */
88 	0,			/* refcnt */
89 	drctl_getinfo,		/* info */
90 	nulldev,		/* identify */
91 	nulldev,		/* probe */
92 	drctl_attach,		/* attach */
93 	drctl_detach,		/* detach */
94 	nodev,			/* reset */
95 	&drctl_cb_ops,		/* driver operations */
96 	NULL,			/* bus operations */
97 	NULL,			/* power */
98 };
99 
100 static struct modldrv modldrv = {
101 	&mod_driverops,		/* type of module - driver */
102 	"DR Control pseudo driver v%I%",
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 	drctlp->drc_inst = -1;
157 	mutex_init(&drctlp->drc_lock, NULL, MUTEX_DRIVER, NULL);
158 	return (mod_install(&modlinkage));
159 }
160 
161 
162 int
163 _fini(void)
164 {
165 	mutex_destroy(&drctlp->drc_lock);
166 	return (mod_remove(&modlinkage));
167 }
168 
169 
170 int
171 _info(struct modinfo *modinfop)
172 {
173 	return (mod_info(&modlinkage, modinfop));
174 }
175 
176 
177 /*
178  * Do the attach work
179  */
180 static int
181 drctl_do_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
182 {
183 	_NOTE(ARGUNUSED(cmd))
184 
185 	char *str = "drctl_do_attach";
186 	int retval = DDI_SUCCESS;
187 
188 	if (drctlp->drc_inst != -1) {
189 		cmn_err(CE_WARN, "%s: an instance is already attached!", str);
190 		return (DDI_FAILURE);
191 	}
192 	drctlp->drc_inst = ddi_get_instance(dip);
193 
194 	retval = ddi_create_minor_node(dip, "drctl", S_IFCHR,
195 	    drctlp->drc_inst, DDI_PSEUDO, 0);
196 	if (retval != DDI_SUCCESS) {
197 		cmn_err(CE_WARN, "%s: can't create minor node", str);
198 		drctlp->drc_inst = -1;
199 		return (retval);
200 	}
201 
202 	drctlp->drc_dip = dip;
203 	ddi_report_dev(dip);
204 
205 	return (retval);
206 }
207 
208 
209 static int
210 drctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
211 {
212 	switch (cmd) {
213 	case DDI_ATTACH:
214 		return (drctl_do_attach(dip, cmd));
215 
216 	default:
217 		return (DDI_FAILURE);
218 	}
219 }
220 
221 
222 /* ARGSUSED */
223 static int
224 drctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
225 {
226 	switch (cmd) {
227 	case DDI_DETACH:
228 		drctlp->drc_inst = -1;
229 		ddi_remove_minor_node(dip, "drctl");
230 		return (DDI_SUCCESS);
231 
232 	default:
233 		return (DDI_FAILURE);
234 	}
235 }
236 
237 static int
238 drctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
239 {
240 	_NOTE(ARGUNUSED(dip, cmd, arg, resultp))
241 
242 	return (0);
243 }
244 
245 static int
246 drctl_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
247 {
248 	_NOTE(ARGUNUSED(devp, flag, cred_p))
249 
250 	if (otyp != OTYP_CHR)
251 		return (EINVAL);
252 
253 	return (0);
254 }
255 
256 static int
257 drctl_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
258 {
259 	_NOTE(ARGUNUSED(dev, flag, otyp, cred_p))
260 
261 	return (0);
262 }
263 
264 /*
265  * This driver guarantees that if drctl_config_init returns 0,
266  * a valid response buffer will be passed back to the caller.  This
267  * routine can be used to generate that response in cases where the
268  * upcall has not resulted in a response message from userland.
269  */
270 static drctl_rsrc_t *
271 drctl_generate_resp(drctl_rsrc_t *res,
272     int count, size_t *rsize, drctl_status_t status)
273 {
274 	int		idx;
275 	size_t		size;
276 	drctl_rsrc_t	*rbuf;
277 
278 	size = count * sizeof (*res);
279 	rbuf  = kmem_alloc(size, KM_SLEEP);
280 
281 	bcopy(res, rbuf, size);
282 
283 	for (idx = 0; idx < count; idx++) {
284 		rbuf[idx].status = status;
285 		rbuf[idx].offset = 0;
286 	}
287 
288 	*rsize = size;
289 	return (rbuf);
290 }
291 
292 static int
293 drctl_config_common(int cmd, int flags, drctl_rsrc_t *res,
294     int count, drctl_rsrc_t **rbuf, size_t *rsize)
295 {
296 	int	rv = 0;
297 	size_t	size;
298 	char	*bufp;
299 	static const char me[] = "drctl_config_common";
300 
301 	switch (cmd) {
302 	case DRCTL_CPU_CONFIG_REQUEST:
303 	case DRCTL_CPU_CONFIG_NOTIFY:
304 	case DRCTL_CPU_UNCONFIG_REQUEST:
305 	case DRCTL_CPU_UNCONFIG_NOTIFY:
306 		rv = 0;
307 		break;
308 	case DRCTL_MEM_CONFIG_REQUEST:
309 	case DRCTL_MEM_CONFIG_NOTIFY:
310 	case DRCTL_MEM_UNCONFIG_REQUEST:
311 	case DRCTL_MEM_UNCONFIG_NOTIFY:
312 	case DRCTL_IO_CONFIG_REQUEST:
313 	case DRCTL_IO_CONFIG_NOTIFY:
314 	case DRCTL_IO_UNCONFIG_REQUEST:
315 	case DRCTL_IO_UNCONFIG_NOTIFY:
316 		rv = ENOTSUP;
317 		break;
318 	}
319 
320 	if (rv != 0) {
321 		DR_DBG_CTL("%s: invalid cmd %d\n", me, cmd);
322 		return (rv);
323 	}
324 
325 	/*
326 	 * If the operation is a FORCE, we don't send a message to
327 	 * the daemon.  But, the upstream clients still expect a
328 	 * response, so generate a response with all ops 'allowed'.
329 	 */
330 	if (flags == DRCTL_FLAG_FORCE) {
331 		if (rbuf != NULL) {
332 			*rbuf = drctl_generate_resp(res, count, &size,
333 			    DRCTL_STATUS_ALLOW);
334 			*rsize = size;
335 		}
336 		return (0);
337 	}
338 
339 	bufp = pack_message(cmd, flags, count, (void *)res, &size);
340 	DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n",
341 	    me, (void *)bufp, size);
342 	if (bufp == NULL || size == 0)
343 		return (EIO);
344 
345 	rv = send_message(bufp, size, (void **)rbuf, rsize);
346 
347 	/*
348 	 * For failure, as part of our contract with the caller,
349 	 * generate a response message, but mark all proposed
350 	 * changes as 'denied'.
351 	 */
352 	if (rv != 0) {
353 		*rbuf = drctl_generate_resp(res, count, &size,
354 		    DRCTL_STATUS_DENY);
355 		*rsize = size;
356 	}
357 
358 	return (rv);
359 }
360 
361 /*
362  * Since the response comes from userland, make sure it is
363  * at least the minimum size and, if it contains error
364  * strings, that the string area is null-terminated.
365  */
366 static int
367 verify_response(int count, drctl_rsrc_t *resp, size_t size)
368 {
369 	int idx;
370 	int need_terminator = 0;
371 	static const char me[] = "verify_response";
372 
373 	if (resp == NULL || size < count * sizeof (*resp)) {
374 		DR_DBG_CTL("%s: BAD size - count %d size %ld\n",
375 		    me, count, size);
376 		return (EIO);
377 	}
378 
379 	for (idx = 0; idx < count; idx++) {
380 
381 		if (resp[idx].offset != 0)
382 			need_terminator++;
383 	}
384 
385 	if (need_terminator && *((caddr_t)(resp) + size - 1) != '\0') {
386 		DR_DBG_CTL("%s: unterm. strings: resp %p size %ld char %d\n",
387 		    me, (void *)resp, size, *((caddr_t)(resp) + size - 1));
388 		/* Don't fail the transaction, but don't advertise strings */
389 		for (idx = 0; idx < count; idx++)
390 			resp[idx].offset = 0;
391 	}
392 
393 	return (0);
394 }
395 
396 
397 /*
398  * Prepare for a reconfig operation.
399  */
400 int
401 drctl_config_init(int cmd, int flags, drctl_rsrc_t *res,
402     int count, drctl_rsrc_t **rbuf, size_t *rsize, drctl_cookie_t ck)
403 {
404 	static char me[] = "drctl_config_init";
405 	int idx;
406 	int rv;
407 
408 	if (ck == 0)
409 		return (EINVAL);
410 
411 	mutex_enter(&drctlp->drc_lock);
412 
413 	if (drctlp->drc_busy != NULL) {
414 		mutex_exit(&drctlp->drc_lock);
415 		return (EBUSY);
416 	}
417 
418 	DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n",
419 	    me, cmd, flags, (void *)res, count);
420 
421 	/* Mark the link busy.  Below we will fill in the actual cookie. */
422 	drctlp->drc_busy = (drctl_cookie_t)-1;
423 	mutex_exit(&drctlp->drc_lock);
424 
425 	if ((rv = drctl_config_common(cmd,
426 	    flags, res, count, rbuf, rsize)) == 0 &&
427 	    verify_response(count, *rbuf, *rsize) == 0) {
428 		drctlp->drc_busy = ck;
429 		drctlp->drc_cmd = cmd;
430 		drctlp->drc_flags = flags;
431 
432 		/*
433 		 * If there wasn't a valid response msg passed back,
434 		 * create a response with each resource op denied.
435 		 */
436 		if (*rbuf == NULL || *rsize == 0) {
437 			drctl_rsrc_t *bp = *rbuf;
438 
439 			*rsize = count * sizeof (*bp);
440 			bp = kmem_zalloc(*rsize, KM_SLEEP);
441 			bcopy(res, bp, *rsize);
442 
443 			for (idx = 0; idx < count; idx++) {
444 				bp[idx].status = DRCTL_STATUS_DENY;
445 				bp[idx].offset = 0;
446 			}
447 		}
448 	} else {
449 		drctlp->drc_cmd = -1;
450 		drctlp->drc_flags = 0;
451 		drctlp->drc_busy = NULL;
452 	}
453 
454 	return (rv);
455 }
456 
457 /*
458  * Complete a reconfig operation.
459  */
460 int
461 drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count)
462 {
463 	int rv;
464 	int notify_cmd;
465 	int flags;
466 
467 	mutex_enter(&drctlp->drc_lock);
468 
469 	if (drctlp->drc_busy != ck) {
470 		mutex_exit(&drctlp->drc_lock);
471 		return (EBUSY);
472 	}
473 
474 	mutex_exit(&drctlp->drc_lock);
475 
476 	flags = drctlp->drc_flags;
477 	/*
478 	 * Flip the saved _REQUEST command to its corresponding
479 	 * _NOTIFY command.
480 	 */
481 	switch (drctlp->drc_cmd) {
482 	case DRCTL_CPU_CONFIG_REQUEST:
483 		notify_cmd = DRCTL_CPU_CONFIG_NOTIFY;
484 		break;
485 
486 	case DRCTL_CPU_UNCONFIG_REQUEST:
487 		notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY;
488 		break;
489 
490 	case DRCTL_MEM_CONFIG_REQUEST:
491 	case DRCTL_MEM_CONFIG_NOTIFY:
492 	case DRCTL_MEM_UNCONFIG_REQUEST:
493 	case DRCTL_MEM_UNCONFIG_NOTIFY:
494 	case DRCTL_IO_CONFIG_REQUEST:
495 	case DRCTL_IO_CONFIG_NOTIFY:
496 	case DRCTL_IO_UNCONFIG_REQUEST:
497 	case DRCTL_IO_UNCONFIG_NOTIFY:
498 	default:
499 		/* none of the above should have been accepted in _init */
500 		ASSERT(0);
501 		cmn_err(CE_CONT,
502 		    "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd);
503 		rv = EINVAL;
504 		goto done;
505 	}
506 
507 	rv = drctl_config_common(notify_cmd, flags, res, count, NULL, 0);
508 
509 done:
510 	drctlp->drc_cmd = -1;
511 	drctlp->drc_flags = 0;
512 	drctlp->drc_busy = NULL;
513 
514 	return (rv);
515 }
516 
517 static int
518 drctl_ioctl(dev_t dev,
519     int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
520 {
521 	_NOTE(ARGUNUSED(dev, mode, cred_p, rval_p))
522 
523 	int rv;
524 
525 	switch (cmd) {
526 	case DRCTL_IOCTL_CONNECT_SERVER:
527 		rv = i_drctl_ioctl(cmd, arg);
528 		break;
529 	default:
530 		rv = ENOTSUP;
531 	}
532 
533 	*rval_p = (rv == 0) ? 0 : -1;
534 
535 	return (rv);
536 }
537 
538 /*
539  * Accept a preformatted request from caller and send a message to
540  * the daemon.  A pointer to the daemon's response buffer is passed
541  * back in obufp, its size in osize.
542  */
543 static int
544 send_message(void *msg, size_t size, void **obufp, size_t *osize)
545 {
546 	int rv;
547 
548 	rv = i_drctl_send(msg, size, obufp, osize);
549 
550 	kmem_free(msg, size);
551 
552 	return (rv);
553 }
554 
555 static void *
556 pack_message(int cmd, int flags, int count, void *data, size_t *osize)
557 {
558 	drd_msg_t *msgp;
559 	size_t hdr_size = offsetof(drd_msg_t, data);
560 
561 	switch (cmd) {
562 	case DRCTL_CPU_CONFIG_REQUEST:
563 	case DRCTL_CPU_CONFIG_NOTIFY:
564 	case DRCTL_CPU_UNCONFIG_REQUEST:
565 	case DRCTL_CPU_UNCONFIG_NOTIFY:
566 
567 		*osize = hdr_size + count * sizeof (drctl_rsrc_t);
568 
569 		msgp = kmem_alloc(*osize, KM_SLEEP);
570 		msgp->cmd = cmd;
571 		msgp->count = count;
572 		msgp->flags = flags;
573 		bcopy(data, msgp->data, count * sizeof (drctl_rsrc_t));
574 		break;
575 	default:
576 		cmn_err(CE_WARN,
577 		    "drctl: pack_message received invalid cmd %d", cmd);
578 		msgp = NULL;
579 	}
580 
581 	return (msgp);
582 }
583