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