xref: /titanic_44/usr/src/uts/common/io/srn.c (revision b785f896a48d487b0368ef79094294b7f0e9b1d5)
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 /*
29  * srn	Provide apm-like interfaces to Xorg
30  */
31 
32 #include <sys/types.h>
33 #include <sys/errno.h>
34 #include <sys/modctl.h>
35 #include <sys/conf.h>		/* driver flags and functions */
36 #include <sys/open.h>		/* OTYP_CHR definition */
37 #include <sys/stat.h>		/* S_IFCHR definition */
38 #include <sys/pathname.h>	/* name -> dev_info xlation */
39 #include <sys/kmem.h>		/* memory alloc stuff */
40 #include <sys/debug.h>
41 #include <sys/pm.h>
42 #include <sys/ddi.h>
43 #include <sys/sunddi.h>
44 #include <sys/epm.h>
45 #include <sys/vfs.h>
46 #include <sys/mode.h>
47 #include <sys/mkdev.h>
48 #include <sys/promif.h>
49 #include <sys/consdev.h>
50 #include <sys/ddi_impldefs.h>
51 #include <sys/poll.h>
52 #include <sys/note.h>
53 #include <sys/taskq.h>
54 #include <sys/policy.h>
55 #include <sys/srn.h>
56 
57 /*
58  * Minor number is instance<<8 + clone minor from range 1-255;
59  * But only one will be allocated
60  */
61 #define	SRN_MINOR_TO_CLONE(minor) ((minor) & (SRN_MAX_CLONE - 1))
62 #define	SU		0x002
63 #define	SG		0x004
64 
65 extern kmutex_t	srn_clone_lock;	/* protects srn_clones array */
66 extern kcondvar_t srn_clones_cv[SRN_MAX_CLONE];
67 extern uint_t	srn_poll_cnt[SRN_MAX_CLONE];
68 
69 /*
70  * The soft state of the srn driver.  Since there will only be
71  * one of these, just reference it through a static struct.
72  */
73 static struct srnstate {
74 	dev_info_t	*srn_dip;		/* ptr to our dev_info node */
75 	int		srn_instance;		/* for ddi_get_instance() */
76 	uchar_t		srn_clones[SRN_MAX_CLONE]; /* unique opens	*/
77 	struct cred	*srn_cred[SRN_MAX_CLONE]; /* cred for each open	*/
78 	int		srn_type[SRN_MAX_CLONE]; /* type of handshake */
79 	int		srn_delivered[SRN_MAX_CLONE];
80 	srn_event_info_t srn_pending[SRN_MAX_CLONE];
81 	int		srn_fault[SRN_MAX_CLONE];
82 } srn = { NULL, -1};
83 typedef struct srnstate *srn_state_t;
84 
85 kcondvar_t	srn_clones_cv[SRN_MAX_CLONE];
86 uint_t		srn_poll_cnt[SRN_MAX_CLONE];	/* count of events for poll */
87 int		srn_apm_count;
88 int		srn_autosx_count;
89 /* Number of seconds to wait for clients to ack a poll */
90 int		srn_timeout = 10;
91 
92 struct pollhead	srn_pollhead[SRN_MAX_CLONE];
93 
94 static int	srn_open(dev_t *, int, int, cred_t *);
95 static int	srn_close(dev_t, int, int, cred_t *);
96 static int	srn_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
97 static int	srn_chpoll(dev_t, short, int, short *, struct pollhead **);
98 
99 static struct cb_ops srn_cb_ops = {
100 	srn_open,	/* open */
101 	srn_close,	/* close */
102 	nodev,		/* strategy */
103 	nodev,		/* print */
104 	nodev,		/* dump */
105 	nodev,		/* read */
106 	nodev,		/* write */
107 	srn_ioctl,	/* ioctl */
108 	nodev,		/* devmap */
109 	nodev,		/* mmap */
110 	nodev,		/* segmap */
111 	srn_chpoll,	/* poll */
112 	ddi_prop_op,	/* prop_op */
113 	NULL,		/* streamtab */
114 	D_NEW | D_MP	/* driver compatibility flag */
115 };
116 
117 static int srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
118     void **result);
119 static int srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
120 static int srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
121 static void srn_notify(int type, int event);
122 
123 static struct dev_ops srn_ops = {
124 	DEVO_REV,		/* devo_rev */
125 	0,			/* refcnt */
126 	srn_getinfo,		/* info */
127 	nulldev,		/* identify */
128 	nulldev,		/* probe */
129 	srn_attach,		/* attach */
130 	srn_detach,		/* detach */
131 	nodev,			/* reset */
132 	&srn_cb_ops,		/* driver operations */
133 	NULL,			/* bus operations */
134 	NULL,			/* power */
135 	ddi_quiesce_not_needed,		/* quiesce */
136 };
137 
138 static struct modldrv modldrv = {
139 	&mod_driverops,
140 	"srn driver",
141 	&srn_ops
142 };
143 
144 static struct modlinkage modlinkage = {
145 	MODREV_1, &modldrv, 0
146 };
147 
148 /* Local functions */
149 
150 int
_init(void)151 _init(void)
152 {
153 	return (mod_install(&modlinkage));
154 }
155 
156 int
_fini(void)157 _fini(void)
158 {
159 	return (mod_remove(&modlinkage));
160 }
161 
162 int
_info(struct modinfo * modinfop)163 _info(struct modinfo *modinfop)
164 {
165 	return (mod_info(&modlinkage, modinfop));
166 }
167 
168 static int
srn_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)169 srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
170 {
171 	int		i;
172 	extern void (*srn_signal)(int, int);
173 
174 	switch (cmd) {
175 
176 	case DDI_ATTACH:
177 		if (srn.srn_instance != -1)	/* Only allow one instance */
178 			return (DDI_FAILURE);
179 		srn.srn_instance = ddi_get_instance(dip);
180 		if (ddi_create_minor_node(dip, "srn", S_IFCHR,
181 		    (srn.srn_instance << 8) + 0, DDI_PSEUDO, 0)
182 		    != DDI_SUCCESS) {
183 			return (DDI_FAILURE);
184 		}
185 		srn.srn_dip = dip;	/* srn_init and getinfo depend on it */
186 
187 		for (i = 0; i < SRN_MAX_CLONE; i++)
188 			cv_init(&srn_clones_cv[i], NULL, CV_DEFAULT, NULL);
189 
190 		srn.srn_instance = ddi_get_instance(dip);
191 		mutex_enter(&srn_clone_lock);
192 		srn_signal = srn_notify;
193 		mutex_exit(&srn_clone_lock);
194 		ddi_report_dev(dip);
195 		return (DDI_SUCCESS);
196 
197 	default:
198 		return (DDI_FAILURE);
199 	}
200 }
201 
202 /* ARGSUSED */
203 static int
srn_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)204 srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
205 {
206 	int i;
207 	extern int srn_inuse;
208 	extern void (*srn_signal)(int, int);
209 
210 	switch (cmd) {
211 	case DDI_DETACH:
212 
213 		mutex_enter(&srn_clone_lock);
214 		while (srn_inuse) {
215 			mutex_exit(&srn_clone_lock);
216 			delay(1);
217 			mutex_enter(&srn_clone_lock);
218 		}
219 		srn_signal = NULL;
220 		mutex_exit(&srn_clone_lock);
221 
222 		for (i = 0; i < SRN_MAX_CLONE; i++)
223 			cv_destroy(&srn_clones_cv[i]);
224 
225 		ddi_remove_minor_node(dip, NULL);
226 		srn.srn_instance = -1;
227 		return (DDI_SUCCESS);
228 
229 	default:
230 		return (DDI_FAILURE);
231 	}
232 }
233 
234 
235 #ifdef DEBUG
236 char *srn_cmd_string;
237 int srn_cmd;
238 #endif
239 
240 /*
241  * Returns true if permission granted by credentials
242  * XXX
243  */
244 static int
srn_perms(int perm,cred_t * cr)245 srn_perms(int perm, cred_t *cr)
246 {
247 	if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */
248 		return (1);
249 	if ((perm & SG) && (crgetgid(cr) == 0))	/* group 0 is ok */
250 		return (1);
251 	return (0);
252 }
253 
254 static int
srn_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)255 srn_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
256 	struct pollhead **phpp)
257 {
258 	extern struct pollhead srn_pollhead[];
259 	int	clone;
260 
261 	clone = SRN_MINOR_TO_CLONE(getminor(dev));
262 	if ((events & (POLLIN | POLLRDNORM)) && srn_poll_cnt[clone]) {
263 		*reventsp |= (POLLIN | POLLRDNORM);
264 	} else {
265 		*reventsp = 0;
266 		if (!anyyet) {
267 			*phpp = &srn_pollhead[clone];
268 		}
269 	}
270 	return (0);
271 }
272 
273 /*ARGSUSED*/
274 static int
srn_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)275 srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
276 {
277 	dev_t	dev;
278 	int	instance;
279 
280 	switch (infocmd) {
281 	case DDI_INFO_DEVT2DEVINFO:
282 		if (srn.srn_instance == -1)
283 			return (DDI_FAILURE);
284 		*result = srn.srn_dip;
285 		return (DDI_SUCCESS);
286 
287 	case DDI_INFO_DEVT2INSTANCE:
288 		dev = (dev_t)arg;
289 		instance = getminor(dev) >> 8;
290 		*result = (void *)(uintptr_t)instance;
291 		return (DDI_SUCCESS);
292 
293 	default:
294 		return (DDI_FAILURE);
295 	}
296 }
297 
298 
299 /*ARGSUSED1*/
300 static int
srn_open(dev_t * devp,int flag,int otyp,cred_t * cr)301 srn_open(dev_t *devp, int flag, int otyp, cred_t *cr)
302 {
303 	int		clone;
304 
305 	if (otyp != OTYP_CHR)
306 		return (EINVAL);
307 
308 	mutex_enter(&srn_clone_lock);
309 	for (clone = 1; clone < SRN_MAX_CLONE - 1; clone++)
310 		if (!srn.srn_clones[clone])
311 			break;
312 
313 	if (clone == SRN_MAX_CLONE) {
314 		mutex_exit(&srn_clone_lock);
315 		return (ENXIO);
316 	}
317 	srn.srn_cred[clone] = cr;
318 	ASSERT(srn_apm_count >= 0);
319 	srn_apm_count++;
320 	srn.srn_type[clone] = SRN_TYPE_APM;
321 	crhold(cr);
322 
323 	*devp = makedevice(getmajor(*devp), (srn.srn_instance << 8) +
324 	    clone);
325 	srn.srn_clones[clone] = 1;
326 	srn.srn_cred[clone] = cr;
327 	crhold(cr);
328 	mutex_exit(&srn_clone_lock);
329 	PMD(PMD_SX, ("srn open OK\n"))
330 	return (0);
331 }
332 
333 /*ARGSUSED1*/
334 static int
srn_close(dev_t dev,int flag,int otyp,cred_t * cr)335 srn_close(dev_t dev, int flag, int otyp, cred_t *cr)
336 {
337 	int clone;
338 
339 	if (otyp != OTYP_CHR)
340 		return (EINVAL);
341 
342 	clone = SRN_MINOR_TO_CLONE(getminor(dev));
343 	PMD(PMD_SX, ("srn_close: minor %x, clone %x\n", getminor(dev),
344 	    clone))
345 	mutex_enter(&srn_clone_lock);
346 	crfree(srn.srn_cred[clone]);
347 	srn.srn_cred[clone] = 0;
348 	srn_poll_cnt[clone] = 0;
349 	srn.srn_fault[clone] = 0;
350 	if (srn.srn_pending[clone].ae_type || srn.srn_delivered[clone]) {
351 		srn.srn_pending[clone].ae_type = 0;
352 		srn.srn_delivered[clone] = 0;
353 		cv_signal(&srn_clones_cv[clone]);
354 	}
355 	switch (srn.srn_type[clone]) {
356 	case SRN_TYPE_AUTOSX:
357 		ASSERT(srn_autosx_count);
358 		srn_autosx_count--;
359 		break;
360 	case SRN_TYPE_APM:
361 		ASSERT(srn_apm_count);
362 		srn_apm_count--;
363 		break;
364 	default:
365 		ASSERT(0);
366 		return (EINVAL);
367 	}
368 	srn.srn_clones[clone] = 0;
369 	mutex_exit(&srn_clone_lock);
370 	return (0);
371 }
372 
373 /*ARGSUSED*/
374 static int
srn_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rval_p)375 srn_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
376 {
377 	int clone = SRN_MINOR_TO_CLONE(getminor(dev));
378 
379 	PMD(PMD_SX, ("ioctl: %x: begin\n", cmd))
380 
381 	switch (cmd) {
382 	case SRN_IOC_NEXTEVENT:
383 	case SRN_IOC_SUSPEND:
384 	case SRN_IOC_RESUME:
385 	case SRN_IOC_AUTOSX:
386 		break;
387 	default:
388 		return (ENOTTY);
389 	}
390 
391 	if (!srn_perms(SU | SG, srn.srn_cred[clone])) {
392 		return (EPERM);
393 	}
394 	switch (cmd) {
395 	case SRN_IOC_AUTOSX:
396 		PMD(PMD_SX, ("SRN_IOC_AUTOSX entered\n"))
397 		mutex_enter(&srn_clone_lock);
398 		if (!srn.srn_clones[clone]) {
399 			PMD(PMD_SX, (" ioctl !srn_clones--EINVAL\n"))
400 			mutex_exit(&srn_clone_lock);
401 			return (EINVAL);
402 		}
403 		if (srn.srn_pending[clone].ae_type) {
404 			PMD(PMD_SX, ("AUTOSX while pending--EBUSY\n"))
405 			mutex_exit(&srn_clone_lock);
406 			return (EBUSY);
407 		}
408 		if (srn.srn_type[clone] == SRN_TYPE_AUTOSX) {
409 			PMD(PMD_SX, ("AUTOSX already--EBUSY\n"))
410 			mutex_exit(&srn_clone_lock);
411 			return (EBUSY);
412 		}
413 		ASSERT(srn.srn_type[clone] == SRN_TYPE_APM);
414 		srn.srn_type[clone] = SRN_TYPE_AUTOSX;
415 		srn.srn_fault[clone] = 0;
416 		srn_apm_count--;
417 		ASSERT(srn_apm_count >= 0);
418 		ASSERT(srn_autosx_count >= 0);
419 		srn_autosx_count++;
420 		mutex_exit(&srn_clone_lock);
421 		PMD(PMD_SX, ("SRN_IOC_AUTOSX returns success\n"))
422 		return (0);
423 
424 	case SRN_IOC_NEXTEVENT:
425 		/*
426 		 * return the next suspend or resume event;  there should
427 		 * be one, cause we only get called if we've signalled a
428 		 * poll data completion
429 		 * then wake up the kernel thread sleeping for the delivery
430 		 */
431 		PMD(PMD_SX, ("SRN_IOC_NEXTEVENT entered\n"))
432 		if (srn.srn_fault[clone]) {
433 			PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d fault "
434 			    "cleared\n", clone))
435 			srn.srn_fault[clone] = 0;
436 		}
437 		mutex_enter(&srn_clone_lock);
438 		if (srn_poll_cnt[clone] == 0) {
439 			mutex_exit(&srn_clone_lock);
440 			PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d "
441 			    "EWOULDBLOCK\n", clone))
442 			return (EWOULDBLOCK);
443 		}
444 		ASSERT(srn.srn_pending[clone].ae_type);
445 		if (ddi_copyout(&srn.srn_pending[clone], (void *)arg,
446 		    sizeof (srn_event_info_t), mode) != 0) {
447 			mutex_exit(&srn_clone_lock);
448 			PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d EFAULT\n",
449 			    clone))
450 			return (EFAULT);
451 		}
452 		if (srn.srn_type[clone] == SRN_TYPE_APM)
453 			srn.srn_delivered[clone] =
454 			    srn.srn_pending[clone].ae_type;
455 		PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d delivered %x\n",
456 		    clone, srn.srn_pending[clone].ae_type))
457 		srn_poll_cnt[clone] = 0;
458 		mutex_exit(&srn_clone_lock);
459 		return (0);
460 
461 	case SRN_IOC_SUSPEND:
462 		/* ack suspend */
463 		PMD(PMD_SX, ("SRN_IOC_SUSPEND entered clone %d\n", clone))
464 		if (srn.srn_fault[clone]) {
465 			PMD(PMD_SX, ("SRN_IOC_SUSPEND clone %d fault "
466 			    "cleared\n", clone))
467 			srn.srn_fault[clone] = 0;
468 		}
469 		mutex_enter(&srn_clone_lock);
470 		if (srn.srn_delivered[clone] != SRN_SUSPEND_REQ) {
471 			mutex_exit(&srn_clone_lock);
472 			PMD(PMD_SX, ("SRN_IOC_SUSPEND EINVAL\n"))
473 			return (EINVAL);
474 		}
475 		srn.srn_delivered[clone] = 0;
476 		srn.srn_pending[clone].ae_type = 0;
477 		/* notify the kernel suspend thread  to continue */
478 		PMD(PMD_SX, ("SRN_IOC_SUSPEND clone %d ok\n", clone))
479 		cv_signal(&srn_clones_cv[clone]);
480 		mutex_exit(&srn_clone_lock);
481 		return (0);
482 
483 	case SRN_IOC_RESUME:
484 		/* ack resume */
485 		PMD(PMD_SX, ("SRN_IOC_RESUME entered clone %d\n", clone))
486 		if (srn.srn_fault[clone]) {
487 			PMD(PMD_SX, ("SRN_IOC_RESUME clone %d fault "
488 			    "cleared\n", clone))
489 			srn.srn_fault[clone] = 0;
490 		}
491 		mutex_enter(&srn_clone_lock);
492 		if (srn.srn_delivered[clone] != SRN_NORMAL_RESUME) {
493 			mutex_exit(&srn_clone_lock);
494 			PMD(PMD_SX, ("SRN_IOC_RESUME EINVAL\n"))
495 			return (EINVAL);
496 		}
497 		srn.srn_delivered[clone] = 0;
498 		srn.srn_pending[clone].ae_type = 0;
499 		/* notify the kernel resume thread  to continue */
500 		PMD(PMD_SX, ("SRN_IOC_RESUME ok for clone %d\n", clone))
501 		cv_signal(&srn_clones_cv[clone]);
502 		mutex_exit(&srn_clone_lock);
503 		return (0);
504 
505 	default:
506 		PMD(PMD_SX, ("srn_ioctl unknown cmd EINVAL\n"))
507 		return (EINVAL);
508 	}
509 }
510 /*
511  * A very simple handshake with the srn driver,
512  * only one outstanding event at a time.
513  * The OS delivers the event and depending on type,
514  * either blocks waiting for the ack, or drives on
515  */
516 void
srn_notify(int type,int event)517 srn_notify(int type, int event)
518 {
519 	int clone, count;
520 	PMD(PMD_SX, ("srn_notify entered with type %d, event 0x%x\n",
521 	    type, event));
522 	ASSERT(mutex_owned(&srn_clone_lock));
523 	switch (type) {
524 	case SRN_TYPE_APM:
525 		if (srn_apm_count == 0) {
526 			PMD(PMD_SX, ("no apm types\n"))
527 			return;
528 		}
529 		count = srn_apm_count;
530 		break;
531 	case SRN_TYPE_AUTOSX:
532 		if (srn_autosx_count == 0) {
533 			PMD(PMD_SX, ("no autosx types\n"))
534 			return;
535 		}
536 		count = srn_autosx_count;
537 		break;
538 	default:
539 		ASSERT(0);
540 		break;
541 	}
542 	ASSERT(count > 0);
543 	PMD(PMD_SX, ("count %d\n", count))
544 	for (clone = 0; clone < SRN_MAX_CLONE; clone++) {
545 		if (srn.srn_type[clone] == type) {
546 #ifdef DEBUG
547 			if (type == SRN_TYPE_APM && !srn.srn_fault[clone]) {
548 				ASSERT(srn.srn_pending[clone].ae_type == 0);
549 				ASSERT(srn_poll_cnt[clone] == 0);
550 				ASSERT(srn.srn_delivered[clone] == 0);
551 			}
552 #endif
553 			srn.srn_pending[clone].ae_type = event;
554 			srn_poll_cnt[clone] = 1;
555 			PMD(PMD_SX, ("pollwake %d\n", clone))
556 			pollwakeup(&srn_pollhead[clone], (POLLRDNORM | POLLIN));
557 			count--;
558 			if (count == 0)
559 				break;
560 		}
561 	}
562 	if (type == SRN_TYPE_AUTOSX) {		/* we don't wait */
563 		PMD(PMD_SX, ("Not waiting for AUTOSX ack\n"))
564 		return;
565 	}
566 	ASSERT(type == SRN_TYPE_APM);
567 	/* otherwise wait for acks */
568 restart:
569 	/*
570 	 * We wait until all of the pending events are cleared.
571 	 * We have to start over every time we do a cv_wait because
572 	 * we give up the mutex and can be re-entered
573 	 */
574 	for (clone = 1; clone < SRN_MAX_CLONE; clone++) {
575 		if (srn.srn_clones[clone] == 0 ||
576 		    srn.srn_type[clone] != SRN_TYPE_APM)
577 			continue;
578 		if (srn.srn_pending[clone].ae_type && !srn.srn_fault[clone]) {
579 			PMD(PMD_SX, ("srn_notify waiting for ack for clone %d, "
580 			    "event %x\n", clone, event))
581 			if (cv_timedwait(&srn_clones_cv[clone],
582 			    &srn_clone_lock, ddi_get_lbolt() +
583 			    drv_usectohz(srn_timeout * 1000000)) == -1) {
584 				/*
585 				 * Client didn't respond, mark it as faulted
586 				 * and continue as if a regular signal.
587 				 */
588 				PMD(PMD_SX, ("srn_notify: clone %d did not "
589 				    "ack event %x\n", clone, event))
590 				cmn_err(CE_WARN, "srn_notify: clone %d did "
591 				    "not ack event %x\n", clone, event);
592 				srn.srn_fault[clone] = 1;
593 			}
594 			goto restart;
595 		}
596 	}
597 	PMD(PMD_SX, ("srn_notify done with %x\n", event))
598 }
599