drctl.c revision 7656:2621e50fdf4a
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
48static int drctl_attach(dev_info_t *, ddi_attach_cmd_t);
49static int drctl_detach(dev_info_t *, ddi_detach_cmd_t);
50static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
51
52static int drctl_open(dev_t *, int, int, cred_t *);
53static int drctl_close(dev_t, int, int, cred_t *);
54static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
55
56static void *pack_message(int, int, int, void *, size_t *);
57static int send_message(void *, size_t, void **, size_t *);
58
59
60/*
61 * Configuration data structures
62 */
63static 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
85static 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
100static struct modldrv modldrv = {
101	&mod_driverops,		/* type of module - driver */
102	"DR Control pseudo driver",
103	&drctl_ops
104};
105
106static 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 */
139static 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
151static struct drctl_unit *drctlp = &drctl_state;
152
153int
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
168int
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
181int
182_info(struct modinfo *modinfop)
183{
184	return (mod_info(&modlinkage, modinfop));
185}
186
187
188/*
189 * Do the attach work
190 */
191static int
192drctl_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
220static int
221drctl_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 */
234static int
235drctl_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
248static int
249drctl_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
256static int
257drctl_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
267static int
268drctl_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 */
281static drctl_rsrc_t *
282drctl_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
303static int
304drctl_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 */
377static int
378verify_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 */
411int
412drctl_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 */
471int
472drctl_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
524done:
525	drctlp->drc_cmd = -1;
526	drctlp->drc_flags = 0;
527	drctlp->drc_busy = NULL;
528
529	return (rv);
530}
531
532static int
533drctl_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 */
558static int
559send_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
570static void *
571pack_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