xref: /illumos-gate/usr/src/uts/common/io/dcopy.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * dcopy.c
31  *    dcopy misc module
32  */
33 
34 #include <sys/conf.h>
35 #include <sys/kmem.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/modctl.h>
39 #include <sys/sysmacros.h>
40 #include <sys/atomic.h>
41 
42 
43 #include <sys/dcopy.h>
44 #include <sys/dcopy_device.h>
45 
46 
47 /* Number of entries per channel to allocate */
48 uint_t dcopy_channel_size = 1024;
49 
50 
51 typedef struct dcopy_list_s {
52 	list_t			dl_list;
53 	kmutex_t		dl_mutex;
54 	uint_t			dl_cnt; /* num entries on list */
55 } dcopy_list_t;
56 
57 /* device state for register/unregister */
58 struct dcopy_device_s {
59 	/* DMA device drivers private pointer */
60 	void			*dc_device_private;
61 
62 	/* to track list of channels from this DMA device */
63 	dcopy_list_t		dc_devchan_list;
64 	list_node_t		dc_device_list_node;
65 
66 	/*
67 	 * dc_removing_cnt track how many channels still have to be freed up
68 	 * before it's safe to allow the DMA device driver to detach.
69 	 */
70 	uint_t			dc_removing_cnt;
71 	dcopy_device_cb_t	*dc_cb;
72 
73 	dcopy_device_info_t	dc_info;
74 
75 };
76 
77 typedef struct dcopy_stats_s {
78 	kstat_named_t	cs_bytes_xfer;
79 	kstat_named_t	cs_cmd_alloc;
80 	kstat_named_t	cs_cmd_post;
81 	kstat_named_t	cs_cmd_poll;
82 	kstat_named_t	cs_notify_poll;
83 	kstat_named_t	cs_notify_pending;
84 	kstat_named_t	cs_id;
85 	kstat_named_t	cs_capabilities;
86 } dcopy_stats_t;
87 
88 /* DMA channel state */
89 struct dcopy_channel_s {
90 	/* DMA driver channel private pointer */
91 	void			*ch_channel_private;
92 
93 	/* shortcut to device callbacks */
94 	dcopy_device_cb_t	*ch_cb;
95 
96 	/*
97 	 * number of outstanding allocs for this channel. used to track when
98 	 * it's safe to free up this channel so the DMA device driver can
99 	 * detach.
100 	 */
101 	uint64_t		ch_ref_cnt;
102 
103 	/* state for if channel needs to be removed when ch_ref_cnt gets to 0 */
104 	boolean_t		ch_removing;
105 
106 	list_node_t		ch_devchan_list_node;
107 	list_node_t		ch_globalchan_list_node;
108 
109 	/*
110 	 * per channel list of commands actively blocking waiting for
111 	 * completion.
112 	 */
113 	dcopy_list_t		ch_poll_list;
114 
115 	/* pointer back to our device */
116 	struct dcopy_device_s	*ch_device;
117 
118 	dcopy_query_channel_t	ch_info;
119 
120 	kstat_t			*ch_kstat;
121 	dcopy_stats_t		ch_stat;
122 };
123 
124 /*
125  * If grabbing both device_list mutex & globalchan_list mutex,
126  * Always grab globalchan_list mutex before device_list mutex
127  */
128 typedef struct dcopy_state_s {
129 	dcopy_list_t		d_device_list;
130 	dcopy_list_t		d_globalchan_list;
131 } dcopy_state_t;
132 dcopy_state_t *dcopy_statep;
133 
134 
135 /* Module Driver Info */
136 static struct modlmisc dcopy_modlmisc = {
137 	&mod_miscops,
138 	"dcopy kernel module"
139 };
140 
141 /* Module Linkage */
142 static struct modlinkage dcopy_modlinkage = {
143 	MODREV_1,
144 	&dcopy_modlmisc,
145 	NULL
146 };
147 
148 static int dcopy_init();
149 static void dcopy_fini();
150 
151 static int dcopy_list_init(dcopy_list_t *list, size_t node_size,
152     offset_t link_offset);
153 static void dcopy_list_fini(dcopy_list_t *list);
154 static void dcopy_list_push(dcopy_list_t *list, void *list_node);
155 static void *dcopy_list_pop(dcopy_list_t *list);
156 
157 static void dcopy_device_cleanup(dcopy_device_handle_t device,
158     boolean_t do_callback);
159 
160 static int dcopy_stats_init(dcopy_handle_t channel);
161 static void dcopy_stats_fini(dcopy_handle_t channel);
162 
163 
164 /*
165  * _init()
166  */
167 int
168 _init()
169 {
170 	int e;
171 
172 	e = dcopy_init();
173 	if (e != 0) {
174 		return (e);
175 	}
176 
177 	return (mod_install(&dcopy_modlinkage));
178 }
179 
180 
181 /*
182  * _info()
183  */
184 int
185 _info(struct modinfo *modinfop)
186 {
187 	return (mod_info(&dcopy_modlinkage, modinfop));
188 }
189 
190 
191 /*
192  * _fini()
193  */
194 int
195 _fini()
196 {
197 	int e;
198 
199 	e = mod_remove(&dcopy_modlinkage);
200 	if (e != 0) {
201 		return (e);
202 	}
203 
204 	dcopy_fini();
205 
206 	return (e);
207 }
208 
209 /*
210  * dcopy_init()
211  */
212 static int
213 dcopy_init()
214 {
215 	int e;
216 
217 
218 	dcopy_statep = kmem_zalloc(sizeof (*dcopy_statep), KM_SLEEP);
219 
220 	/* Initialize the list we use to track device register/unregister */
221 	e = dcopy_list_init(&dcopy_statep->d_device_list,
222 	    sizeof (struct dcopy_device_s),
223 	    offsetof(struct dcopy_device_s, dc_device_list_node));
224 	if (e != DCOPY_SUCCESS) {
225 		goto dcopyinitfail_device;
226 	}
227 
228 	/* Initialize the list we use to track all DMA channels */
229 	e = dcopy_list_init(&dcopy_statep->d_globalchan_list,
230 	    sizeof (struct dcopy_channel_s),
231 	    offsetof(struct dcopy_channel_s, ch_globalchan_list_node));
232 	if (e != DCOPY_SUCCESS) {
233 		goto dcopyinitfail_global;
234 	}
235 
236 	return (0);
237 
238 dcopyinitfail_cback:
239 	dcopy_list_fini(&dcopy_statep->d_globalchan_list);
240 dcopyinitfail_global:
241 	dcopy_list_fini(&dcopy_statep->d_device_list);
242 dcopyinitfail_device:
243 	kmem_free(dcopy_statep, sizeof (*dcopy_statep));
244 
245 	return (-1);
246 }
247 
248 
249 /*
250  * dcopy_fini()
251  */
252 static void
253 dcopy_fini()
254 {
255 	/*
256 	 * if mod_remove was successfull, we shouldn't have any
257 	 * devices/channels to worry about.
258 	 */
259 	ASSERT(list_head(&dcopy_statep->d_globalchan_list.dl_list) == NULL);
260 	ASSERT(list_head(&dcopy_statep->d_device_list.dl_list) == NULL);
261 
262 	dcopy_list_fini(&dcopy_statep->d_globalchan_list);
263 	dcopy_list_fini(&dcopy_statep->d_device_list);
264 	kmem_free(dcopy_statep, sizeof (*dcopy_statep));
265 }
266 
267 
268 /* *** EXTERNAL INTERFACE *** */
269 /*
270  * dcopy_query()
271  */
272 void
273 dcopy_query(dcopy_query_t *query)
274 {
275 	query->dq_version = DCOPY_QUERY_V0;
276 	query->dq_num_channels = dcopy_statep->d_globalchan_list.dl_cnt;
277 }
278 
279 
280 /*
281  * dcopy_alloc()
282  */
283 /*ARGSUSED*/
284 int
285 dcopy_alloc(int flags, dcopy_handle_t *handle)
286 {
287 	dcopy_handle_t channel;
288 	dcopy_list_t *list;
289 
290 
291 	/*
292 	 * we don't use the dcopy_list_* code here because we need to due
293 	 * some non-standard stuff.
294 	 */
295 
296 	list = &dcopy_statep->d_globalchan_list;
297 
298 	/*
299 	 * if nothing is on the channel list, return DCOPY_NORESOURCES. This
300 	 * can happen if there aren't any DMA device registered.
301 	 */
302 	mutex_enter(&list->dl_mutex);
303 	channel = list_head(&list->dl_list);
304 	if (channel == NULL) {
305 		mutex_exit(&list->dl_mutex);
306 		return (DCOPY_NORESOURCES);
307 	}
308 
309 	/*
310 	 * increment the reference count, and pop the channel off the head and
311 	 * push it on the tail. This ensures we rotate through the channels.
312 	 * DMA channels are shared.
313 	 */
314 	channel->ch_ref_cnt++;
315 	list_remove(&list->dl_list, channel);
316 	list_insert_tail(&list->dl_list, channel);
317 	mutex_exit(&list->dl_mutex);
318 
319 	*handle = (dcopy_handle_t)channel;
320 	return (DCOPY_SUCCESS);
321 }
322 
323 
324 /*
325  * dcopy_free()
326  */
327 void
328 dcopy_free(dcopy_handle_t *channel)
329 {
330 	dcopy_device_handle_t device;
331 	dcopy_list_t *list;
332 	boolean_t cleanup;
333 
334 
335 	ASSERT(*channel != NULL);
336 
337 	/*
338 	 * we don't need to add the channel back to the list since we never
339 	 * removed it. decrement the reference count.
340 	 */
341 	list = &dcopy_statep->d_globalchan_list;
342 	mutex_enter(&list->dl_mutex);
343 	(*channel)->ch_ref_cnt--;
344 
345 	/*
346 	 * if we need to remove this channel, and the reference count is down
347 	 * to 0, decrement the number of channels which still need to be
348 	 * removed on the device.
349 	 */
350 	if ((*channel)->ch_removing && ((*channel)->ch_ref_cnt == 0)) {
351 		cleanup = B_FALSE;
352 		device = (*channel)->ch_device;
353 		mutex_enter(&device->dc_devchan_list.dl_mutex);
354 		device->dc_removing_cnt--;
355 		if (device->dc_removing_cnt == 0) {
356 			cleanup = B_TRUE;
357 		}
358 		mutex_exit(&device->dc_devchan_list.dl_mutex);
359 	}
360 	mutex_exit(&list->dl_mutex);
361 
362 	/*
363 	 * if there are no channels which still need to be removed, cleanup the
364 	 * device state and call back into the DMA device driver to tell them
365 	 * the device is free.
366 	 */
367 	if (cleanup) {
368 		dcopy_device_cleanup(device, B_TRUE);
369 	}
370 
371 	*channel = NULL;
372 }
373 
374 
375 /*
376  * dcopy_query_channel()
377  */
378 void
379 dcopy_query_channel(dcopy_handle_t channel, dcopy_query_channel_t *query)
380 {
381 	*query = channel->ch_info;
382 }
383 
384 
385 /*
386  * dcopy_cmd_alloc()
387  */
388 int
389 dcopy_cmd_alloc(dcopy_handle_t handle, int flags, dcopy_cmd_t *cmd)
390 {
391 	dcopy_handle_t channel;
392 	dcopy_cmd_priv_t priv;
393 	int e;
394 
395 
396 	channel = handle;
397 
398 	atomic_inc_64(&channel->ch_stat.cs_cmd_alloc.value.ui64);
399 	e = channel->ch_cb->cb_cmd_alloc(channel->ch_channel_private, flags,
400 	    cmd);
401 	if (e == DCOPY_SUCCESS) {
402 		priv = (*cmd)->dp_private;
403 		priv->pr_channel = channel;
404 		/*
405 		 * we won't initialize the blocking state until we actually
406 		 * need to block.
407 		 */
408 		priv->pr_block_init = B_FALSE;
409 	}
410 
411 	return (e);
412 }
413 
414 
415 /*
416  * dcopy_cmd_free()
417  */
418 void
419 dcopy_cmd_free(dcopy_cmd_t *cmd)
420 {
421 	dcopy_handle_t channel;
422 	dcopy_cmd_priv_t priv;
423 
424 
425 	ASSERT(*cmd != NULL);
426 
427 	priv = (*cmd)->dp_private;
428 	channel = priv->pr_channel;
429 
430 	/* if we initialized the blocking state, clean it up too */
431 	if (priv->pr_block_init) {
432 		cv_destroy(&priv->pr_cv);
433 		mutex_destroy(&priv->pr_mutex);
434 	}
435 
436 	channel->ch_cb->cb_cmd_free(channel->ch_channel_private, cmd);
437 }
438 
439 
440 /*
441  * dcopy_cmd_post()
442  */
443 int
444 dcopy_cmd_post(dcopy_cmd_t cmd)
445 {
446 	dcopy_handle_t channel;
447 	int e;
448 
449 
450 	channel = cmd->dp_private->pr_channel;
451 
452 	atomic_inc_64(&channel->ch_stat.cs_cmd_post.value.ui64);
453 	if (cmd->dp_cmd == DCOPY_CMD_COPY) {
454 		atomic_add_64(&channel->ch_stat.cs_bytes_xfer.value.ui64,
455 		    cmd->dp.copy.cc_size);
456 	}
457 	e = channel->ch_cb->cb_cmd_post(channel->ch_channel_private, cmd);
458 	if (e != DCOPY_SUCCESS) {
459 		return (e);
460 	}
461 
462 	return (DCOPY_SUCCESS);
463 }
464 
465 
466 /*
467  * dcopy_cmd_poll()
468  */
469 int
470 dcopy_cmd_poll(dcopy_cmd_t cmd, int flags)
471 {
472 	dcopy_handle_t channel;
473 	dcopy_cmd_priv_t priv;
474 	int e;
475 
476 
477 	priv = cmd->dp_private;
478 	channel = priv->pr_channel;
479 
480 	/*
481 	 * if the caller is trying to block, they needed to post the
482 	 * command with DCOPY_CMD_INTR set.
483 	 */
484 	if ((flags & DCOPY_POLL_BLOCK) && !(cmd->dp_flags & DCOPY_CMD_INTR)) {
485 		return (DCOPY_FAILURE);
486 	}
487 
488 	atomic_inc_64(&channel->ch_stat.cs_cmd_poll.value.ui64);
489 
490 repoll:
491 	e = channel->ch_cb->cb_cmd_poll(channel->ch_channel_private, cmd);
492 	if (e == DCOPY_PENDING) {
493 		/*
494 		 * if the command is still active, and the blocking flag
495 		 * is set.
496 		 */
497 		if (flags & DCOPY_POLL_BLOCK) {
498 
499 			/*
500 			 * if we haven't initialized the state, do it now. A
501 			 * command can be re-used, so it's possible it's
502 			 * already been initialized.
503 			 */
504 			if (!priv->pr_block_init) {
505 				priv->pr_block_init = B_TRUE;
506 				mutex_init(&priv->pr_mutex, NULL, MUTEX_DRIVER,
507 				    NULL);
508 				cv_init(&priv->pr_cv, NULL, CV_DRIVER, NULL);
509 				priv->pr_cmd = cmd;
510 			}
511 
512 			/* push it on the list for blocking commands */
513 			priv->pr_wait = B_TRUE;
514 			dcopy_list_push(&channel->ch_poll_list, priv);
515 
516 			mutex_enter(&priv->pr_mutex);
517 			/*
518 			 * it's possible we already cleared pr_wait before we
519 			 * grabbed the mutex.
520 			 */
521 			if (priv->pr_wait) {
522 				cv_wait(&priv->pr_cv, &priv->pr_mutex);
523 			}
524 			mutex_exit(&priv->pr_mutex);
525 
526 			/*
527 			 * the command has completed, go back and poll so we
528 			 * get the status.
529 			 */
530 			goto repoll;
531 		}
532 	}
533 
534 	return (e);
535 }
536 
537 /* *** END OF EXTERNAL INTERFACE *** */
538 
539 /*
540  * dcopy_list_init()
541  */
542 static int
543 dcopy_list_init(dcopy_list_t *list, size_t node_size, offset_t link_offset)
544 {
545 	mutex_init(&list->dl_mutex, NULL, MUTEX_DRIVER, NULL);
546 	list_create(&list->dl_list, node_size, link_offset);
547 	list->dl_cnt = 0;
548 
549 	return (DCOPY_SUCCESS);
550 }
551 
552 
553 /*
554  * dcopy_list_fini()
555  */
556 static void
557 dcopy_list_fini(dcopy_list_t *list)
558 {
559 	list_destroy(&list->dl_list);
560 	mutex_destroy(&list->dl_mutex);
561 }
562 
563 
564 /*
565  * dcopy_list_push()
566  */
567 static void
568 dcopy_list_push(dcopy_list_t *list, void *list_node)
569 {
570 	mutex_enter(&list->dl_mutex);
571 	list_insert_tail(&list->dl_list, list_node);
572 	list->dl_cnt++;
573 	mutex_exit(&list->dl_mutex);
574 }
575 
576 
577 /*
578  * dcopy_list_pop()
579  */
580 static void *
581 dcopy_list_pop(dcopy_list_t *list)
582 {
583 	list_node_t *list_node;
584 
585 	mutex_enter(&list->dl_mutex);
586 	list_node = list_head(&list->dl_list);
587 	if (list_node == NULL) {
588 		mutex_exit(&list->dl_mutex);
589 		return (list_node);
590 	}
591 	list->dl_cnt--;
592 	list_remove(&list->dl_list, list_node);
593 	mutex_exit(&list->dl_mutex);
594 
595 	return (list_node);
596 }
597 
598 
599 /* *** DEVICE INTERFACE *** */
600 /*
601  * dcopy_device_register()
602  */
603 int
604 dcopy_device_register(void *device_private, dcopy_device_info_t *info,
605     dcopy_device_handle_t *handle)
606 {
607 	struct dcopy_channel_s *channel;
608 	struct dcopy_device_s *device;
609 	int e;
610 	int i;
611 
612 
613 	/* initialize the per device state */
614 	device = kmem_zalloc(sizeof (*device), KM_SLEEP);
615 	device->dc_device_private = device_private;
616 	device->dc_info = *info;
617 	device->dc_removing_cnt = 0;
618 	device->dc_cb = info->di_cb;
619 
620 	/*
621 	 * we have a per device channel list so we can remove a device in the
622 	 * future.
623 	 */
624 	e = dcopy_list_init(&device->dc_devchan_list,
625 	    sizeof (struct dcopy_channel_s),
626 	    offsetof(struct dcopy_channel_s, ch_devchan_list_node));
627 	if (e != DCOPY_SUCCESS) {
628 		goto registerfail_devchan;
629 	}
630 
631 	/*
632 	 * allocate state for each channel, allocate the channel,  and then add
633 	 * the devices dma channels to the devices channel list.
634 	 */
635 	for (i = 0; i < info->di_num_dma; i++) {
636 		channel = kmem_zalloc(sizeof (*channel), KM_SLEEP);
637 		channel->ch_device = device;
638 		channel->ch_removing = B_FALSE;
639 		channel->ch_ref_cnt = 0;
640 		channel->ch_cb = info->di_cb;
641 
642 		e = info->di_cb->cb_channel_alloc(device_private, channel,
643 		    DCOPY_SLEEP, dcopy_channel_size, &channel->ch_info,
644 		    &channel->ch_channel_private);
645 		if (e != DCOPY_SUCCESS) {
646 			kmem_free(channel, sizeof (*channel));
647 			goto registerfail_alloc;
648 		}
649 
650 		e = dcopy_stats_init(channel);
651 		if (e != DCOPY_SUCCESS) {
652 			info->di_cb->cb_channel_free(
653 			    &channel->ch_channel_private);
654 			kmem_free(channel, sizeof (*channel));
655 			goto registerfail_alloc;
656 		}
657 
658 		e = dcopy_list_init(&channel->ch_poll_list,
659 		    sizeof (struct dcopy_cmd_priv_s),
660 		    offsetof(struct dcopy_cmd_priv_s, pr_poll_list_node));
661 		if (e != DCOPY_SUCCESS) {
662 			dcopy_stats_fini(channel);
663 			info->di_cb->cb_channel_free(
664 			    &channel->ch_channel_private);
665 			kmem_free(channel, sizeof (*channel));
666 			goto registerfail_alloc;
667 		}
668 
669 		dcopy_list_push(&device->dc_devchan_list, channel);
670 	}
671 
672 	/* add the device to device list */
673 	dcopy_list_push(&dcopy_statep->d_device_list, device);
674 
675 	/*
676 	 * add the device's dma channels to the global channel list (where
677 	 * dcopy_alloc's come from)
678 	 */
679 	mutex_enter(&dcopy_statep->d_globalchan_list.dl_mutex);
680 	mutex_enter(&dcopy_statep->d_device_list.dl_mutex);
681 	channel = list_head(&device->dc_devchan_list.dl_list);
682 	while (channel != NULL) {
683 		list_insert_tail(&dcopy_statep->d_globalchan_list.dl_list,
684 		    channel);
685 		dcopy_statep->d_globalchan_list.dl_cnt++;
686 		channel = list_next(&device->dc_devchan_list.dl_list, channel);
687 	}
688 	mutex_exit(&dcopy_statep->d_device_list.dl_mutex);
689 	mutex_exit(&dcopy_statep->d_globalchan_list.dl_mutex);
690 
691 	*handle = device;
692 
693 	/* last call-back into kernel for dcopy KAPI enabled */
694 	uioa_dcopy_enable();
695 
696 	return (DCOPY_SUCCESS);
697 
698 registerfail_alloc:
699 	channel = list_head(&device->dc_devchan_list.dl_list);
700 	while (channel != NULL) {
701 		/* remove from the list */
702 		channel = dcopy_list_pop(&device->dc_devchan_list);
703 		ASSERT(channel != NULL);
704 
705 		dcopy_list_fini(&channel->ch_poll_list);
706 		dcopy_stats_fini(channel);
707 		info->di_cb->cb_channel_free(&channel->ch_channel_private);
708 		kmem_free(channel, sizeof (*channel));
709 	}
710 
711 	dcopy_list_fini(&device->dc_devchan_list);
712 registerfail_devchan:
713 	kmem_free(device, sizeof (*device));
714 
715 	return (DCOPY_FAILURE);
716 }
717 
718 
719 /*
720  * dcopy_device_unregister()
721  */
722 /*ARGSUSED*/
723 int
724 dcopy_device_unregister(dcopy_device_handle_t *handle)
725 {
726 	struct dcopy_channel_s *channel;
727 	dcopy_device_handle_t device;
728 	boolean_t device_busy;
729 
730 	/* first call-back into kernel for dcopy KAPI disable */
731 	uioa_dcopy_disable();
732 
733 	device = *handle;
734 	device_busy = B_FALSE;
735 
736 	/*
737 	 * remove the devices dma channels from the global channel list (where
738 	 * dcopy_alloc's come from)
739 	 */
740 	mutex_enter(&dcopy_statep->d_globalchan_list.dl_mutex);
741 	mutex_enter(&device->dc_devchan_list.dl_mutex);
742 	channel = list_head(&device->dc_devchan_list.dl_list);
743 	while (channel != NULL) {
744 		/*
745 		 * if the channel has outstanding allocs, mark it as having
746 		 * to be removed and increment the number of channels which
747 		 * need to be removed in the device state too.
748 		 */
749 		if (channel->ch_ref_cnt != 0) {
750 			channel->ch_removing = B_TRUE;
751 			device_busy = B_TRUE;
752 			device->dc_removing_cnt++;
753 		}
754 		dcopy_statep->d_globalchan_list.dl_cnt--;
755 		list_remove(&dcopy_statep->d_globalchan_list.dl_list, channel);
756 		channel = list_next(&device->dc_devchan_list.dl_list, channel);
757 	}
758 	mutex_exit(&device->dc_devchan_list.dl_mutex);
759 	mutex_exit(&dcopy_statep->d_globalchan_list.dl_mutex);
760 
761 	/*
762 	 * if there are channels which still need to be removed, we will clean
763 	 * up the device state after they are freed up.
764 	 */
765 	if (device_busy) {
766 		return (DCOPY_PENDING);
767 	}
768 
769 	dcopy_device_cleanup(device, B_FALSE);
770 
771 	*handle = NULL;
772 	return (DCOPY_SUCCESS);
773 }
774 
775 
776 /*
777  * dcopy_device_cleanup()
778  */
779 static void
780 dcopy_device_cleanup(dcopy_device_handle_t device, boolean_t do_callback)
781 {
782 	struct dcopy_channel_s *channel;
783 
784 	/*
785 	 * remove all the channels in the device list, free them, and clean up
786 	 * the state.
787 	 */
788 	mutex_enter(&dcopy_statep->d_device_list.dl_mutex);
789 	channel = list_head(&device->dc_devchan_list.dl_list);
790 	while (channel != NULL) {
791 		device->dc_devchan_list.dl_cnt--;
792 		list_remove(&device->dc_devchan_list.dl_list, channel);
793 		dcopy_list_fini(&channel->ch_poll_list);
794 		dcopy_stats_fini(channel);
795 		channel->ch_cb->cb_channel_free(&channel->ch_channel_private);
796 		kmem_free(channel, sizeof (*channel));
797 		channel = list_head(&device->dc_devchan_list.dl_list);
798 	}
799 
800 	/* remove it from the list of devices */
801 	list_remove(&dcopy_statep->d_device_list.dl_list, device);
802 
803 	mutex_exit(&dcopy_statep->d_device_list.dl_mutex);
804 
805 	/*
806 	 * notify the DMA device driver that the device is free to be
807 	 * detached.
808 	 */
809 	if (do_callback) {
810 		device->dc_cb->cb_unregister_complete(
811 		    device->dc_device_private, DCOPY_SUCCESS);
812 	}
813 
814 	dcopy_list_fini(&device->dc_devchan_list);
815 	kmem_free(device, sizeof (*device));
816 }
817 
818 
819 /*
820  * dcopy_device_channel_notify()
821  */
822 /*ARGSUSED*/
823 void
824 dcopy_device_channel_notify(dcopy_handle_t handle, int status)
825 {
826 	struct dcopy_channel_s *channel;
827 	dcopy_list_t *poll_list;
828 	dcopy_cmd_priv_t priv;
829 	int e;
830 
831 
832 	ASSERT(status == DCOPY_COMPLETION);
833 	channel = handle;
834 
835 	poll_list = &channel->ch_poll_list;
836 
837 	/*
838 	 * when we get a completion notification from the device, go through
839 	 * all of the commands blocking on this channel and see if they have
840 	 * completed. Remove the command and wake up the block thread if they
841 	 * have. Once we hit a command which is still pending, we are done
842 	 * polling since commands in a channel complete in order.
843 	 */
844 	mutex_enter(&poll_list->dl_mutex);
845 	if (poll_list->dl_cnt != 0) {
846 		priv = list_head(&poll_list->dl_list);
847 		while (priv != NULL) {
848 			atomic_inc_64(&channel->
849 			    ch_stat.cs_notify_poll.value.ui64);
850 			e = channel->ch_cb->cb_cmd_poll(
851 			    channel->ch_channel_private,
852 			    priv->pr_cmd);
853 			if (e == DCOPY_PENDING) {
854 				atomic_inc_64(&channel->
855 				    ch_stat.cs_notify_pending.value.ui64);
856 				break;
857 			}
858 
859 			poll_list->dl_cnt--;
860 			list_remove(&poll_list->dl_list, priv);
861 
862 			mutex_enter(&priv->pr_mutex);
863 			priv->pr_wait = B_FALSE;
864 			cv_signal(&priv->pr_cv);
865 			mutex_exit(&priv->pr_mutex);
866 
867 			priv = list_head(&poll_list->dl_list);
868 		}
869 	}
870 
871 	mutex_exit(&poll_list->dl_mutex);
872 }
873 
874 
875 /*
876  * dcopy_stats_init()
877  */
878 static int
879 dcopy_stats_init(dcopy_handle_t channel)
880 {
881 #define	CHANSTRSIZE	20
882 	char chanstr[CHANSTRSIZE];
883 	dcopy_stats_t *stats;
884 	int instance;
885 	char *name;
886 
887 
888 	stats = &channel->ch_stat;
889 	name = (char *)ddi_driver_name(channel->ch_device->dc_info.di_dip);
890 	instance = ddi_get_instance(channel->ch_device->dc_info.di_dip);
891 
892 	(void) snprintf(chanstr, CHANSTRSIZE, "channel%d",
893 	    (uint32_t)channel->ch_info.qc_chan_num);
894 
895 	channel->ch_kstat = kstat_create(name, instance, chanstr, "misc",
896 	    KSTAT_TYPE_NAMED, sizeof (dcopy_stats_t) / sizeof (kstat_named_t),
897 	    KSTAT_FLAG_VIRTUAL);
898 	if (channel->ch_kstat == NULL) {
899 		return (DCOPY_FAILURE);
900 	}
901 	channel->ch_kstat->ks_data = stats;
902 
903 	kstat_named_init(&stats->cs_bytes_xfer, "bytes_xfer",
904 	    KSTAT_DATA_UINT64);
905 	kstat_named_init(&stats->cs_cmd_alloc, "cmd_alloc",
906 	    KSTAT_DATA_UINT64);
907 	kstat_named_init(&stats->cs_cmd_post, "cmd_post",
908 	    KSTAT_DATA_UINT64);
909 	kstat_named_init(&stats->cs_cmd_poll, "cmd_poll",
910 	    KSTAT_DATA_UINT64);
911 	kstat_named_init(&stats->cs_notify_poll, "notify_poll",
912 	    KSTAT_DATA_UINT64);
913 	kstat_named_init(&stats->cs_notify_pending, "notify_pending",
914 	    KSTAT_DATA_UINT64);
915 	kstat_named_init(&stats->cs_id, "id",
916 	    KSTAT_DATA_UINT64);
917 	kstat_named_init(&stats->cs_capabilities, "capabilities",
918 	    KSTAT_DATA_UINT64);
919 
920 	kstat_install(channel->ch_kstat);
921 
922 	channel->ch_stat.cs_id.value.ui64 = channel->ch_info.qc_id;
923 	channel->ch_stat.cs_capabilities.value.ui64 =
924 	    channel->ch_info.qc_capabilities;
925 
926 	return (DCOPY_SUCCESS);
927 }
928 
929 
930 /*
931  * dcopy_stats_fini()
932  */
933 static void
934 dcopy_stats_fini(dcopy_handle_t channel)
935 {
936 	kstat_delete(channel->ch_kstat);
937 }
938 /* *** END OF DEVICE INTERFACE *** */
939