xref: /titanic_50/usr/src/uts/sun4v/io/ds_pri.c (revision e1c679fa4b0ab8c4bcaa6263974ca0c46e5b027f)
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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * sun4v domain services PRI driver
30  */
31 
32 #include <sys/types.h>
33 #include <sys/file.h>
34 #include <sys/errno.h>
35 #include <sys/open.h>
36 #include <sys/cred.h>
37 #include <sys/uio.h>
38 #include <sys/stat.h>
39 #include <sys/ksynch.h>
40 #include <sys/modctl.h>
41 #include <sys/conf.h>
42 #include <sys/devops.h>
43 #include <sys/debug.h>
44 #include <sys/cmn_err.h>
45 #include <sys/ddi.h>
46 #include <sys/sunddi.h>
47 #include <sys/ds.h>
48 
49 #include <sys/ds_pri.h>
50 
51 static uint_t ds_pri_debug = 0;
52 #define	DS_PRI_DBG	if (ds_pri_debug) printf
53 
54 #define	DS_PRI_NAME	"ds_pri"
55 
56 #define	TEST_HARNESS
57 #ifdef TEST_HARNESS
58 #define	DS_PRI_MAX_PRI_SIZE	(64 * 1024)
59 
60 #define	DSIOC_TEST_REG	97
61 #define	DSIOC_TEST_UNREG	98
62 #define	DSIOC_TEST_DATA	99
63 
64 struct ds_pri_test_data {
65 	size_t		size;
66 	void		*data;
67 };
68 
69 struct ds_pri_test_data32 {
70 	size32_t	size;
71 	caddr32_t	data;
72 };
73 #endif /* TEST_HARNESS */
74 
75 typedef	enum {
76 	DS_PRI_REQUEST	= 0,
77 	DS_PRI_DATA	= 1,
78 	DS_PRI_UPDATE	= 2
79 } ds_pri_msg_type_t;
80 
81 typedef	struct {
82 	struct {
83 		uint64_t	seq_num;
84 		uint64_t	type;
85 	} hdr;
86 	uint8_t		data[1];
87 } ds_pri_msg_t;
88 
89 	/* The following are bit field flags */
90 	/* No service implies no PRI and no outstanding request */
91 typedef enum {
92 	DS_PRI_NO_SERVICE = 0x0,
93 	DS_PRI_HAS_SERVICE = 0x1,
94 	DS_PRI_REQUESTED = 0x2,
95 	DS_PRI_HAS_PRI = 0x4
96 } ds_pri_flags_t;
97 
98 struct ds_pri_state {
99 	dev_info_t	*dip;
100 	int		instance;
101 
102 	kmutex_t	lock;
103 	kcondvar_t	cv;
104 
105 	/* PRI/DS */
106 	ds_pri_flags_t	state;
107 	uint64_t	gencount;
108 	ds_svc_hdl_t	ds_pri_handle;
109 	void		*ds_pri;
110 	size_t		ds_pri_len;
111 	uint64_t	req_id;
112 	uint64_t	last_req_id;
113 };
114 
115 typedef struct ds_pri_state ds_pri_state_t;
116 
117 static void *ds_pri_statep;
118 
119 static void request_pri(ds_pri_state_t *sp);
120 
121 static int ds_pri_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
122 static int ds_pri_attach(dev_info_t *, ddi_attach_cmd_t);
123 static int ds_pri_detach(dev_info_t *, ddi_detach_cmd_t);
124 static int ds_pri_open(dev_t *, int, int, cred_t *);
125 static int ds_pri_close(dev_t, int, int, cred_t *);
126 static int ds_pri_read(dev_t, struct uio *, cred_t *);
127 static int ds_pri_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
128 
129 /*
130  * DS Callbacks
131  */
132 static void ds_pri_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
133 static void ds_pri_unreg_handler(ds_cb_arg_t arg);
134 static void ds_pri_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
135 
136 /*
137  * PRI DS capability registration
138  */
139 
140 static ds_ver_t ds_pri_ver_1_0 = { 1, 0 };
141 
142 static ds_capability_t ds_pri_cap = {
143 	"pri",
144 	&ds_pri_ver_1_0,
145 	1
146 };
147 
148 /*
149  * PRI DS Client callback vector
150  */
151 static ds_clnt_ops_t ds_pri_ops = {
152 	ds_pri_reg_handler,	/* ds_reg_cb */
153 	ds_pri_unreg_handler,	/* ds_unreg_cb */
154 	ds_pri_data_handler,	/* ds_data_cb */
155 	NULL			/* cb_arg */
156 };
157 
158 /*
159  * DS PRI driver Ops Vector
160  */
161 static struct cb_ops ds_pri_cb_ops = {
162 	ds_pri_open,		/* cb_open */
163 	ds_pri_close,		/* cb_close */
164 	nodev,			/* cb_strategy */
165 	nodev,			/* cb_print */
166 	nodev,			/* cb_dump */
167 	ds_pri_read,		/* cb_read */
168 	nodev,			/* cb_write */
169 	ds_pri_ioctl,		/* cb_ioctl */
170 	nodev,			/* cb_devmap */
171 	nodev,			/* cb_mmap */
172 	nodev,			/* cb_segmap */
173 	nochpoll,		/* cb_chpoll */
174 	ddi_prop_op,		/* cb_prop_op */
175 	(struct streamtab *)NULL, /* cb_str */
176 	D_MP | D_64BIT,		/* cb_flag */
177 	CB_REV,			/* cb_rev */
178 	nodev,			/* cb_aread */
179 	nodev			/* cb_awrite */
180 };
181 
182 static struct dev_ops ds_pri_dev_ops = {
183 	DEVO_REV,		/* devo_rev */
184 	0,			/* devo_refcnt */
185 	ds_pri_getinfo,		/* devo_getinfo */
186 	nulldev,		/* devo_identify */
187 	nulldev,		/* devo_probe */
188 	ds_pri_attach,		/* devo_attach */
189 	ds_pri_detach,		/* devo_detach */
190 	nodev,			/* devo_reset */
191 	&ds_pri_cb_ops,		/* devo_cb_ops */
192 	(struct bus_ops *)NULL,	/* devo_bus_ops */
193 	nulldev			/* devo_power */
194 };
195 
196 static struct modldrv modldrv = {
197 	&mod_driverops,
198 	"Domain Services PRI Driver 1.0",
199 	&ds_pri_dev_ops
200 };
201 
202 static struct modlinkage modlinkage = {
203 	MODREV_1,
204 	(void *)&modldrv,
205 	NULL
206 };
207 
208 
209 int
210 _init(void)
211 {
212 	int retval;
213 
214 	retval = ddi_soft_state_init(&ds_pri_statep,
215 	    sizeof (ds_pri_state_t), 0);
216 	if (retval != 0)
217 		return (retval);
218 
219 	retval = mod_install(&modlinkage);
220 	if (retval != 0) {
221 		ddi_soft_state_fini(&ds_pri_statep);
222 		return (retval);
223 	}
224 
225 	return (retval);
226 }
227 
228 
229 int
230 _info(struct modinfo *modinfop)
231 {
232 	return (mod_info(&modlinkage, modinfop));
233 }
234 
235 
236 int
237 _fini(void)
238 {
239 	int retval;
240 
241 	if ((retval = mod_remove(&modlinkage)) != 0)
242 		return (retval);
243 
244 	ddi_soft_state_fini(&ds_pri_statep);
245 
246 	return (retval);
247 }
248 
249 
250 /*ARGSUSED*/
251 static int
252 ds_pri_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
253 {
254 	ds_pri_state_t *sp;
255 	int retval = DDI_FAILURE;
256 
257 	ASSERT(resultp != NULL);
258 
259 	switch (cmd) {
260 	case DDI_INFO_DEVT2DEVINFO:
261 		sp = ddi_get_soft_state(ds_pri_statep, getminor((dev_t)arg));
262 		if (sp != NULL) {
263 			*resultp = sp->dip;
264 			retval = DDI_SUCCESS;
265 		} else
266 			*resultp = NULL;
267 		break;
268 
269 	case DDI_INFO_DEVT2INSTANCE:
270 		*resultp = (void *)(uintptr_t)getminor((dev_t)arg);
271 		retval = DDI_SUCCESS;
272 		break;
273 
274 	default:
275 		break;
276 	}
277 
278 	return (retval);
279 }
280 
281 
282 static int
283 ds_pri_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
284 {
285 	int instance;
286 	ds_pri_state_t *sp;
287 	int rv;
288 
289 	switch (cmd) {
290 	case DDI_ATTACH:
291 		break;
292 
293 	case DDI_RESUME:
294 		return (DDI_SUCCESS);
295 
296 	default:
297 		return (DDI_FAILURE);
298 	}
299 
300 	instance = ddi_get_instance(dip);
301 
302 	if (ddi_soft_state_zalloc(ds_pri_statep, instance) !=
303 	    DDI_SUCCESS) {
304 		cmn_err(CE_WARN, "%s@%d: Unable to allocate state",
305 		    DS_PRI_NAME, instance);
306 		return (DDI_FAILURE);
307 	}
308 	sp = ddi_get_soft_state(ds_pri_statep, instance);
309 
310 	mutex_init(&sp->lock, NULL, MUTEX_DEFAULT, NULL);
311 	cv_init(&sp->cv, NULL, CV_DEFAULT, NULL);
312 
313 	if (ddi_create_minor_node(dip, DS_PRI_NAME, S_IFCHR, instance,
314 		DDI_PSEUDO, 0) != DDI_SUCCESS) {
315 		cmn_err(CE_WARN, "%s@%d: Unable to create minor node",
316 		    DS_PRI_NAME, instance);
317 		goto fail;
318 	}
319 
320 	if (ds_pri_ops.cb_arg != NULL)
321 		goto fail;
322 	ds_pri_ops.cb_arg = dip;
323 
324 	sp->state = DS_PRI_NO_SERVICE;
325 
326 	/* Until the service registers the handle is invalid */
327 	sp->ds_pri_handle = DS_INVALID_HDL;
328 
329 	sp->ds_pri = NULL;
330 	sp->ds_pri_len = 0;
331 	sp->req_id = 0;
332 
333 	if ((rv = ds_cap_init(&ds_pri_cap, &ds_pri_ops)) != 0) {
334 		cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
335 		goto fail;
336 	}
337 
338 	ddi_report_dev(dip);
339 
340 	return (DDI_SUCCESS);
341 
342 fail:
343 	ddi_remove_minor_node(dip, NULL);
344 	cv_destroy(&sp->cv);
345 	mutex_destroy(&sp->lock);
346 	ddi_soft_state_free(ds_pri_statep, instance);
347 	return (DDI_FAILURE);
348 
349 }
350 
351 
352 /*ARGSUSED*/
353 static int
354 ds_pri_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
355 {
356 	ds_pri_state_t *sp;
357 	int instance;
358 	int rv;
359 
360 	instance = ddi_get_instance(dip);
361 	sp = ddi_get_soft_state(ds_pri_statep, instance);
362 
363 	switch (cmd) {
364 	case DDI_DETACH:
365 		break;
366 
367 	case DDI_SUSPEND:
368 		return (DDI_SUCCESS);
369 
370 	default:
371 		return (DDI_FAILURE);
372 	}
373 
374 	/* This really shouldn't fail - but check anyway */
375 	if ((rv = ds_cap_fini(&ds_pri_cap)) != 0) {
376 		cmn_err(CE_WARN, "ds_cap_fini failed: %d", rv);
377 	}
378 
379 	if (sp != NULL && sp->ds_pri_len != 0)
380 		kmem_free(sp->ds_pri, sp->ds_pri_len);
381 
382 	ddi_remove_minor_node(dip, NULL);
383 	cv_destroy(&sp->cv);
384 	mutex_destroy(&sp->lock);
385 	ddi_soft_state_free(ds_pri_statep, instance);
386 
387 	return (DDI_SUCCESS);
388 }
389 
390 
391 /*ARGSUSED*/
392 static int
393 ds_pri_open(dev_t *devp, int flag, int otyp, cred_t *credp)
394 {
395 	ds_pri_state_t *sp;
396 	int instance;
397 
398 	if (otyp != OTYP_CHR)
399 		return (EINVAL);
400 
401 	instance = getminor(*devp);
402 	sp = ddi_get_soft_state(ds_pri_statep, instance);
403 	if (sp == NULL)
404 		return (ENXIO);
405 
406 	/*
407 	 * On open we dont fetch the PRI even if we have a valid service
408 	 * handle. PRI fetch is essentially lazy and on-demand.
409 	 */
410 
411 	DS_PRI_DBG("ds_pri_open: state = 0x%x\n", sp->state);
412 
413 	return (0);
414 }
415 
416 
417 /*ARGSUSED*/
418 static int
419 ds_pri_close(dev_t dev, int flag, int otyp, cred_t *credp)
420 {
421 	int instance;
422 
423 	if (otyp != OTYP_CHR)
424 		return (EINVAL);
425 
426 	DS_PRI_DBG("ds_pri_close\n");
427 
428 	instance = getminor(dev);
429 	if (ddi_get_soft_state(ds_pri_statep, instance) == NULL)
430 		return (ENXIO);
431 
432 	return (0);
433 }
434 
435 
436 /*ARGSUSED*/
437 static int
438 ds_pri_read(dev_t dev, struct uio *uiop, cred_t *credp)
439 {
440 	ds_pri_state_t *sp;
441 	int instance;
442 	size_t len;
443 	int retval;
444 	caddr_t tmpbufp;
445 
446 	instance = getminor(dev);
447 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
448 		return (ENXIO);
449 
450 	len = uiop->uio_resid;
451 
452 	if (len == 0)
453 		return (0);
454 
455 	mutex_enter(&sp->lock);
456 
457 	DS_PRI_DBG("ds_pri_read: state = 0x%x\n", sp->state);
458 
459 	/* block or bail if there is no current PRI */
460 	if (!(sp->state & DS_PRI_HAS_PRI)) {
461 		DS_PRI_DBG("ds_pri_read: no PRI held\n");
462 
463 		if (uiop->uio_fmode & (FNDELAY | FNONBLOCK)) {
464 			mutex_exit(&sp->lock);
465 			return (EAGAIN);
466 		}
467 
468 		while (!(sp->state & DS_PRI_HAS_PRI)) {
469 			DS_PRI_DBG("ds_pri_read: state = 0x%x\n", sp->state);
470 			request_pri(sp);
471 			if (cv_wait_sig(&sp->cv, &sp->lock) == 0) {
472 				mutex_exit(&sp->lock);
473 				return (EINTR);
474 			}
475 		}
476 	}
477 
478 	if (uiop->uio_offset < 0 || uiop->uio_offset > sp->ds_pri_len) {
479 		mutex_exit(&sp->lock);
480 		return (EINVAL);
481 	}
482 
483 	if (len > (sp->ds_pri_len - uiop->uio_offset))
484 		len = sp->ds_pri_len - uiop->uio_offset;
485 
486 	/* already checked that offset < ds_pri_len above */
487 	if (len == 0) {
488 		mutex_exit(&sp->lock);
489 		return (0);
490 	}
491 
492 	/*
493 	 * We're supposed to move the data out to userland, but
494 	 * that can suspend because of page faults etc., and meanwhile
495 	 * other parts of this driver want to update the PRI buffer ...
496 	 * we could hold the data buffer locked with a flag etc.,
497 	 * but that's still a lock ... a simpler mechanism - if not quite
498 	 * as performance efficient is to simply clone here the part of
499 	 * the buffer we care about and then the original can be released
500 	 * for further updates while the uiomove continues.
501 	 */
502 
503 	tmpbufp = kmem_alloc(len, KM_SLEEP);
504 	bcopy(((caddr_t)sp->ds_pri) + uiop->uio_offset, tmpbufp, len);
505 	mutex_exit(&sp->lock);
506 
507 	retval = uiomove(tmpbufp, len, UIO_READ, uiop);
508 
509 	kmem_free(tmpbufp, len);
510 
511 	return (retval);
512 }
513 
514 
515 /*ARGSUSED*/
516 static int
517 ds_pri_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
518     int *rvalp)
519 {
520 	ds_pri_state_t *sp;
521 	int instance;
522 
523 	instance = getminor(dev);
524 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
525 		return (ENXIO);
526 
527 	switch (cmd) {
528 	case DSPRI_GETINFO: {
529 		struct dspri_info info;
530 
531 		if (!(mode & FREAD))
532 			return (EACCES);
533 
534 		/*
535 		 * We are not guaranteed that ddi_copyout(9F) will read
536 		 * atomically anything larger than a byte.  Therefore we
537 		 * must duplicate the size before copying it out to the user.
538 		 */
539 		mutex_enter(&sp->lock);
540 
541 loop:;
542 		if (sp->state & DS_PRI_HAS_PRI) {
543 			/* If we have a PRI simply return the info */
544 			info.size = sp->ds_pri_len;
545 			info.token = sp->gencount;
546 		} else
547 		if (!(sp->state & DS_PRI_HAS_SERVICE)) {
548 			/* If we have no service return a nil response */
549 			info.size = 0;
550 			info.token = 0;
551 		} else {
552 			request_pri(sp);
553 			/* wait for something & check again */
554 			if (cv_wait_sig(&sp->cv, &sp->lock) == 0) {
555 				mutex_exit(&sp->lock);
556 				return (EINTR);
557 			}
558 			goto loop;
559 		}
560 		DS_PRI_DBG("ds_pri_ioctl: DSPRI_GETINFO sz=0x%lx tok=0x%lx\n",
561 			info.size, info.token);
562 		mutex_exit(&sp->lock);
563 
564 		if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0)
565 			return (EFAULT);
566 		break;
567 	}
568 
569 	case DSPRI_WAIT: {
570 		uint64_t gencount;
571 
572 		if (ddi_copyin((void *)arg, &gencount, sizeof (gencount),
573 		    mode) != 0)
574 			return (EFAULT);
575 
576 		mutex_enter(&sp->lock);
577 
578 		DS_PRI_DBG("ds_pri_ioctl: DSPRI_WAIT gen=0x%lx sp->gen=0x%lx\n",
579 			gencount, sp->gencount);
580 
581 		while ((sp->state & DS_PRI_HAS_PRI) == 0 ||
582 			gencount == sp->gencount) {
583 			if (cv_wait_sig(&sp->cv, &sp->lock) == 0) {
584 				mutex_exit(&sp->lock);
585 				return (EINTR);
586 			}
587 		}
588 		mutex_exit(&sp->lock);
589 		break;
590 	}
591 
592 	default:
593 		return (ENOTTY);
594 	}
595 	return (0);
596 }
597 
598 
599 	/* assumes sp->lock is held when called */
600 static void
601 request_pri(ds_pri_state_t *sp)
602 {
603 	ds_pri_msg_t reqmsg;
604 
605 	ASSERT(MUTEX_HELD(&sp->lock));
606 
607 	/* If a request is already pending we're done */
608 	if (!(sp->state & DS_PRI_HAS_SERVICE))
609 		return;
610 	if (sp->state & DS_PRI_REQUESTED)
611 		return;
612 
613 	/* If we have an old PRI - remove it */
614 	if (sp->state & DS_PRI_HAS_PRI) {
615 		ASSERT(sp->ds_pri_len != 0);
616 		ASSERT(sp->ds_pri != NULL);
617 
618 		/* remove the old data if we have an outstanding request */
619 		kmem_free(sp->ds_pri, sp->ds_pri_len);
620 		sp->ds_pri_len = 0;
621 		sp->ds_pri = NULL;
622 		sp->state &= ~DS_PRI_HAS_PRI;
623 	} else {
624 		ASSERT(sp->ds_pri == NULL);
625 		ASSERT(sp->ds_pri_len == 0);
626 	}
627 
628 	reqmsg.hdr.seq_num = ++(sp->req_id);
629 	reqmsg.hdr.type = DS_PRI_REQUEST;
630 
631 	DS_PRI_DBG("request_pri: request id 0x%lx\n", sp->req_id);
632 
633 		/*
634 		 * Request consists of header only.
635 		 * We don't care about fail status for ds_send;
636 		 * if it does fail we will get an unregister callback
637 		 * from the DS framework and we handle the state change
638 		 * there.
639 		 */
640 	(void) ds_cap_send(sp->ds_pri_handle, &reqmsg, sizeof (reqmsg.hdr));
641 
642 	sp->state |= DS_PRI_REQUESTED;
643 	sp->last_req_id = sp->req_id;
644 }
645 
646 /*
647  * DS Callbacks
648  */
649 /*ARGSUSED*/
650 static void
651 ds_pri_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
652 {
653 	dev_info_t *dip = arg;
654 	ds_pri_state_t *sp;
655 	int instance;
656 
657 	instance = ddi_get_instance(dip);
658 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
659 		return;
660 
661 	DS_PRI_DBG("ds_pri_reg_handler: registering handle 0x%lx for version "
662 		"0x%x:0x%x\n", (uint64_t)hdl, ver->major, ver->minor);
663 
664 	/* When the domain service comes up automatically req the pri */
665 	mutex_enter(&sp->lock);
666 
667 	ASSERT(sp->ds_pri_handle == DS_INVALID_HDL);
668 	sp->ds_pri_handle = hdl;
669 
670 	ASSERT(sp->state == DS_PRI_NO_SERVICE);
671 	ASSERT(sp->ds_pri == NULL);
672 	ASSERT(sp->ds_pri_len == 0);
673 
674 	/* have service, but no PRI */
675 	sp->state |= DS_PRI_HAS_SERVICE;
676 
677 		/*
678 		 * Cannot request a PRI here, because the reg handler cannot
679 		 * do a DS send operation - we take care of this later.
680 		 */
681 	mutex_exit(&sp->lock);
682 }
683 
684 
685 static void
686 ds_pri_unreg_handler(ds_cb_arg_t arg)
687 {
688 	dev_info_t *dip = arg;
689 	ds_pri_state_t *sp;
690 	int instance;
691 
692 	instance = ddi_get_instance(dip);
693 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
694 		return;
695 
696 	DS_PRI_DBG("ds_pri_unreg_handler: un-registering ds_pri service\n");
697 
698 	mutex_enter(&sp->lock);
699 
700 	/* Once the service goes - if we have a PRI at hand free it up */
701 	if (sp->ds_pri_len != 0) {
702 		kmem_free(sp->ds_pri, sp->ds_pri_len);
703 		sp->ds_pri_len = 0;
704 		sp->ds_pri = NULL;
705 	}
706 	sp->ds_pri_handle = DS_INVALID_HDL;
707 	sp->state = DS_PRI_NO_SERVICE;
708 
709 	mutex_exit(&sp->lock);
710 }
711 
712 
713 static void
714 ds_pri_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
715 {
716 	dev_info_t *dip = arg;
717 	ds_pri_state_t *sp;
718 	int instance;
719 	void *data;
720 	ds_pri_msg_t	*msgp;
721 	size_t	pri_size;
722 
723 	msgp = (ds_pri_msg_t *)buf;
724 
725 	/* make sure the header is at least valid */
726 	if (buflen < sizeof (msgp->hdr))
727 		return;
728 
729 	DS_PRI_DBG("ds_pri_data_handler: msg buf len 0x%lx : type 0x%lx, "
730 		"seqn 0x%lx\n", buflen, msgp->hdr.type, msgp->hdr.seq_num);
731 
732 	instance = ddi_get_instance(dip);
733 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
734 		return;
735 
736 	mutex_enter(&sp->lock);
737 
738 	ASSERT(sp->state & DS_PRI_HAS_SERVICE);
739 
740 	switch (msgp->hdr.type) {
741 	case DS_PRI_DATA:	/* in response to a request from us */
742 		break;
743 	case DS_PRI_UPDATE:	/* aynch notification */
744 			/* our default response to this is to request the PRI */
745 		/* simply issue a request for the new PRI */
746 		request_pri(sp);
747 		goto done;
748 	default:	/* ignore garbage or unknown message types */
749 		goto done;
750 	}
751 
752 	/*
753 	 * If there is no pending PRI request, then we've received a
754 	 * bogus data message ... so ignore it.
755 	 */
756 
757 	if (!(sp->state & DS_PRI_REQUESTED)) {
758 		cmn_err(CE_WARN, "Received DS pri data without request");
759 		goto done;
760 	}
761 
762 	/* response to a request therefore old PRI must be gone */
763 	ASSERT(!(sp->state & DS_PRI_HAS_PRI));
764 	ASSERT(sp->ds_pri_len == 0);
765 	ASSERT(sp->ds_pri == NULL);
766 
767 	/* response seq_num should match our request seq_num */
768 	if (msgp->hdr.seq_num != sp->last_req_id) {
769 		cmn_err(CE_WARN, "Received DS pri data out of sequence with "
770 			"request");
771 		goto done;
772 	}
773 
774 	pri_size = buflen - sizeof (msgp->hdr);
775 	data = kmem_alloc(pri_size, KM_SLEEP);
776 	sp->ds_pri = data;
777 	sp->ds_pri_len = pri_size;
778 	bcopy(msgp->data, data, sp->ds_pri_len);
779 	sp->state &= ~DS_PRI_REQUESTED;
780 	sp->state |= DS_PRI_HAS_PRI;
781 
782 	sp->gencount++;
783 	cv_broadcast(&sp->cv);
784 
785 done:;
786 	mutex_exit(&sp->lock);
787 }
788