xref: /titanic_52/usr/src/uts/common/io/dcopy.c (revision b798e010bf650d3252fd767dff39df1ccc12d285)
117169044Sbrutus /*
217169044Sbrutus  * CDDL HEADER START
317169044Sbrutus  *
417169044Sbrutus  * The contents of this file are subject to the terms of the
517169044Sbrutus  * Common Development and Distribution License (the "License").
617169044Sbrutus  * You may not use this file except in compliance with the License.
717169044Sbrutus  *
817169044Sbrutus  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
917169044Sbrutus  * or http://www.opensolaris.org/os/licensing.
1017169044Sbrutus  * See the License for the specific language governing permissions
1117169044Sbrutus  * and limitations under the License.
1217169044Sbrutus  *
1317169044Sbrutus  * When distributing Covered Code, include this CDDL HEADER in each
1417169044Sbrutus  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1517169044Sbrutus  * If applicable, add the following below this CDDL HEADER, with the
1617169044Sbrutus  * fields enclosed by brackets "[]" replaced with your own identifying
1717169044Sbrutus  * information: Portions Copyright [yyyy] [name of copyright owner]
1817169044Sbrutus  *
1917169044Sbrutus  * CDDL HEADER END
2017169044Sbrutus  */
2117169044Sbrutus 
2217169044Sbrutus /*
2317169044Sbrutus  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2417169044Sbrutus  * Use is subject to license terms.
2517169044Sbrutus  */
2617169044Sbrutus 
2717169044Sbrutus /*
2817169044Sbrutus  * dcopy.c
2917169044Sbrutus  *    dcopy misc module
3017169044Sbrutus  */
3117169044Sbrutus 
3217169044Sbrutus #include <sys/conf.h>
3317169044Sbrutus #include <sys/kmem.h>
3417169044Sbrutus #include <sys/ddi.h>
3517169044Sbrutus #include <sys/sunddi.h>
3617169044Sbrutus #include <sys/modctl.h>
3717169044Sbrutus #include <sys/sysmacros.h>
3817169044Sbrutus #include <sys/atomic.h>
3917169044Sbrutus 
4017169044Sbrutus 
4117169044Sbrutus #include <sys/dcopy.h>
4217169044Sbrutus #include <sys/dcopy_device.h>
4317169044Sbrutus 
4417169044Sbrutus 
4517169044Sbrutus /* Number of entries per channel to allocate */
4617169044Sbrutus uint_t dcopy_channel_size = 1024;
4717169044Sbrutus 
4817169044Sbrutus 
4917169044Sbrutus typedef struct dcopy_list_s {
5017169044Sbrutus 	list_t			dl_list;
5117169044Sbrutus 	kmutex_t		dl_mutex;
5217169044Sbrutus 	uint_t			dl_cnt; /* num entries on list */
5317169044Sbrutus } dcopy_list_t;
5417169044Sbrutus 
5517169044Sbrutus /* device state for register/unregister */
5617169044Sbrutus struct dcopy_device_s {
5717169044Sbrutus 	/* DMA device drivers private pointer */
5817169044Sbrutus 	void			*dc_device_private;
5917169044Sbrutus 
6017169044Sbrutus 	/* to track list of channels from this DMA device */
6117169044Sbrutus 	dcopy_list_t		dc_devchan_list;
6217169044Sbrutus 	list_node_t		dc_device_list_node;
6317169044Sbrutus 
6417169044Sbrutus 	/*
6517169044Sbrutus 	 * dc_removing_cnt track how many channels still have to be freed up
6617169044Sbrutus 	 * before it's safe to allow the DMA device driver to detach.
6717169044Sbrutus 	 */
6817169044Sbrutus 	uint_t			dc_removing_cnt;
6917169044Sbrutus 	dcopy_device_cb_t	*dc_cb;
7017169044Sbrutus 
7117169044Sbrutus 	dcopy_device_info_t	dc_info;
7217169044Sbrutus 
7317169044Sbrutus };
7417169044Sbrutus 
7517169044Sbrutus typedef struct dcopy_stats_s {
7617169044Sbrutus 	kstat_named_t	cs_bytes_xfer;
7717169044Sbrutus 	kstat_named_t	cs_cmd_alloc;
7817169044Sbrutus 	kstat_named_t	cs_cmd_post;
7917169044Sbrutus 	kstat_named_t	cs_cmd_poll;
8017169044Sbrutus 	kstat_named_t	cs_notify_poll;
8117169044Sbrutus 	kstat_named_t	cs_notify_pending;
8217169044Sbrutus 	kstat_named_t	cs_id;
8317169044Sbrutus 	kstat_named_t	cs_capabilities;
8417169044Sbrutus } dcopy_stats_t;
8517169044Sbrutus 
8617169044Sbrutus /* DMA channel state */
8717169044Sbrutus struct dcopy_channel_s {
8817169044Sbrutus 	/* DMA driver channel private pointer */
8917169044Sbrutus 	void			*ch_channel_private;
9017169044Sbrutus 
9117169044Sbrutus 	/* shortcut to device callbacks */
9217169044Sbrutus 	dcopy_device_cb_t	*ch_cb;
9317169044Sbrutus 
9417169044Sbrutus 	/*
9517169044Sbrutus 	 * number of outstanding allocs for this channel. used to track when
9617169044Sbrutus 	 * it's safe to free up this channel so the DMA device driver can
9717169044Sbrutus 	 * detach.
9817169044Sbrutus 	 */
9917169044Sbrutus 	uint64_t		ch_ref_cnt;
10017169044Sbrutus 
10117169044Sbrutus 	/* state for if channel needs to be removed when ch_ref_cnt gets to 0 */
10217169044Sbrutus 	boolean_t		ch_removing;
10317169044Sbrutus 
10417169044Sbrutus 	list_node_t		ch_devchan_list_node;
10517169044Sbrutus 	list_node_t		ch_globalchan_list_node;
10617169044Sbrutus 
10717169044Sbrutus 	/*
10817169044Sbrutus 	 * per channel list of commands actively blocking waiting for
10917169044Sbrutus 	 * completion.
11017169044Sbrutus 	 */
11117169044Sbrutus 	dcopy_list_t		ch_poll_list;
11217169044Sbrutus 
11317169044Sbrutus 	/* pointer back to our device */
11417169044Sbrutus 	struct dcopy_device_s	*ch_device;
11517169044Sbrutus 
11617169044Sbrutus 	dcopy_query_channel_t	ch_info;
11717169044Sbrutus 
11817169044Sbrutus 	kstat_t			*ch_kstat;
11917169044Sbrutus 	dcopy_stats_t		ch_stat;
12017169044Sbrutus };
12117169044Sbrutus 
12217169044Sbrutus /*
12317169044Sbrutus  * If grabbing both device_list mutex & globalchan_list mutex,
12417169044Sbrutus  * Always grab globalchan_list mutex before device_list mutex
12517169044Sbrutus  */
12617169044Sbrutus typedef struct dcopy_state_s {
12717169044Sbrutus 	dcopy_list_t		d_device_list;
12817169044Sbrutus 	dcopy_list_t		d_globalchan_list;
12917169044Sbrutus } dcopy_state_t;
13017169044Sbrutus dcopy_state_t *dcopy_statep;
13117169044Sbrutus 
13217169044Sbrutus 
13317169044Sbrutus /* Module Driver Info */
13417169044Sbrutus static struct modlmisc dcopy_modlmisc = {
13517169044Sbrutus 	&mod_miscops,
13617169044Sbrutus 	"dcopy kernel module"
13717169044Sbrutus };
13817169044Sbrutus 
13917169044Sbrutus /* Module Linkage */
14017169044Sbrutus static struct modlinkage dcopy_modlinkage = {
14117169044Sbrutus 	MODREV_1,
14217169044Sbrutus 	&dcopy_modlmisc,
14317169044Sbrutus 	NULL
14417169044Sbrutus };
14517169044Sbrutus 
14617169044Sbrutus static int dcopy_init();
14717169044Sbrutus static void dcopy_fini();
14817169044Sbrutus 
14917169044Sbrutus static int dcopy_list_init(dcopy_list_t *list, size_t node_size,
15017169044Sbrutus     offset_t link_offset);
15117169044Sbrutus static void dcopy_list_fini(dcopy_list_t *list);
15217169044Sbrutus static void dcopy_list_push(dcopy_list_t *list, void *list_node);
15317169044Sbrutus static void *dcopy_list_pop(dcopy_list_t *list);
15417169044Sbrutus 
15517169044Sbrutus static void dcopy_device_cleanup(dcopy_device_handle_t device,
15617169044Sbrutus     boolean_t do_callback);
15717169044Sbrutus 
15817169044Sbrutus static int dcopy_stats_init(dcopy_handle_t channel);
15917169044Sbrutus static void dcopy_stats_fini(dcopy_handle_t channel);
16017169044Sbrutus 
16117169044Sbrutus 
16217169044Sbrutus /*
16317169044Sbrutus  * _init()
16417169044Sbrutus  */
16517169044Sbrutus int
16617169044Sbrutus _init()
16717169044Sbrutus {
16817169044Sbrutus 	int e;
16917169044Sbrutus 
17017169044Sbrutus 	e = dcopy_init();
17117169044Sbrutus 	if (e != 0) {
17217169044Sbrutus 		return (e);
17317169044Sbrutus 	}
17417169044Sbrutus 
17517169044Sbrutus 	return (mod_install(&dcopy_modlinkage));
17617169044Sbrutus }
17717169044Sbrutus 
17817169044Sbrutus 
17917169044Sbrutus /*
18017169044Sbrutus  * _info()
18117169044Sbrutus  */
18217169044Sbrutus int
18317169044Sbrutus _info(struct modinfo *modinfop)
18417169044Sbrutus {
18517169044Sbrutus 	return (mod_info(&dcopy_modlinkage, modinfop));
18617169044Sbrutus }
18717169044Sbrutus 
18817169044Sbrutus 
18917169044Sbrutus /*
19017169044Sbrutus  * _fini()
19117169044Sbrutus  */
19217169044Sbrutus int
19317169044Sbrutus _fini()
19417169044Sbrutus {
19517169044Sbrutus 	int e;
19617169044Sbrutus 
19717169044Sbrutus 	e = mod_remove(&dcopy_modlinkage);
19817169044Sbrutus 	if (e != 0) {
19917169044Sbrutus 		return (e);
20017169044Sbrutus 	}
20117169044Sbrutus 
20217169044Sbrutus 	dcopy_fini();
20317169044Sbrutus 
20417169044Sbrutus 	return (e);
20517169044Sbrutus }
20617169044Sbrutus 
20717169044Sbrutus /*
20817169044Sbrutus  * dcopy_init()
20917169044Sbrutus  */
21017169044Sbrutus static int
21117169044Sbrutus dcopy_init()
21217169044Sbrutus {
21317169044Sbrutus 	int e;
21417169044Sbrutus 
21517169044Sbrutus 
21617169044Sbrutus 	dcopy_statep = kmem_zalloc(sizeof (*dcopy_statep), KM_SLEEP);
21717169044Sbrutus 
21817169044Sbrutus 	/* Initialize the list we use to track device register/unregister */
21917169044Sbrutus 	e = dcopy_list_init(&dcopy_statep->d_device_list,
22017169044Sbrutus 	    sizeof (struct dcopy_device_s),
22117169044Sbrutus 	    offsetof(struct dcopy_device_s, dc_device_list_node));
22217169044Sbrutus 	if (e != DCOPY_SUCCESS) {
22317169044Sbrutus 		goto dcopyinitfail_device;
22417169044Sbrutus 	}
22517169044Sbrutus 
22617169044Sbrutus 	/* Initialize the list we use to track all DMA channels */
22717169044Sbrutus 	e = dcopy_list_init(&dcopy_statep->d_globalchan_list,
22817169044Sbrutus 	    sizeof (struct dcopy_channel_s),
22917169044Sbrutus 	    offsetof(struct dcopy_channel_s, ch_globalchan_list_node));
23017169044Sbrutus 	if (e != DCOPY_SUCCESS) {
23117169044Sbrutus 		goto dcopyinitfail_global;
23217169044Sbrutus 	}
23317169044Sbrutus 
23417169044Sbrutus 	return (0);
23517169044Sbrutus 
23617169044Sbrutus dcopyinitfail_cback:
23717169044Sbrutus 	dcopy_list_fini(&dcopy_statep->d_globalchan_list);
23817169044Sbrutus dcopyinitfail_global:
23917169044Sbrutus 	dcopy_list_fini(&dcopy_statep->d_device_list);
24017169044Sbrutus dcopyinitfail_device:
24117169044Sbrutus 	kmem_free(dcopy_statep, sizeof (*dcopy_statep));
24217169044Sbrutus 
24317169044Sbrutus 	return (-1);
24417169044Sbrutus }
24517169044Sbrutus 
24617169044Sbrutus 
24717169044Sbrutus /*
24817169044Sbrutus  * dcopy_fini()
24917169044Sbrutus  */
25017169044Sbrutus static void
25117169044Sbrutus dcopy_fini()
25217169044Sbrutus {
25317169044Sbrutus 	/*
25417169044Sbrutus 	 * if mod_remove was successfull, we shouldn't have any
25517169044Sbrutus 	 * devices/channels to worry about.
25617169044Sbrutus 	 */
25717169044Sbrutus 	ASSERT(list_head(&dcopy_statep->d_globalchan_list.dl_list) == NULL);
25817169044Sbrutus 	ASSERT(list_head(&dcopy_statep->d_device_list.dl_list) == NULL);
25917169044Sbrutus 
26017169044Sbrutus 	dcopy_list_fini(&dcopy_statep->d_globalchan_list);
26117169044Sbrutus 	dcopy_list_fini(&dcopy_statep->d_device_list);
26217169044Sbrutus 	kmem_free(dcopy_statep, sizeof (*dcopy_statep));
26317169044Sbrutus }
26417169044Sbrutus 
26517169044Sbrutus 
26617169044Sbrutus /* *** EXTERNAL INTERFACE *** */
26717169044Sbrutus /*
26817169044Sbrutus  * dcopy_query()
26917169044Sbrutus  */
27017169044Sbrutus void
27117169044Sbrutus dcopy_query(dcopy_query_t *query)
27217169044Sbrutus {
27317169044Sbrutus 	query->dq_version = DCOPY_QUERY_V0;
27417169044Sbrutus 	query->dq_num_channels = dcopy_statep->d_globalchan_list.dl_cnt;
27517169044Sbrutus }
27617169044Sbrutus 
27717169044Sbrutus 
27817169044Sbrutus /*
27917169044Sbrutus  * dcopy_alloc()
28017169044Sbrutus  */
28117169044Sbrutus /*ARGSUSED*/
28217169044Sbrutus int
28317169044Sbrutus dcopy_alloc(int flags, dcopy_handle_t *handle)
28417169044Sbrutus {
28517169044Sbrutus 	dcopy_handle_t channel;
28617169044Sbrutus 	dcopy_list_t *list;
28717169044Sbrutus 
28817169044Sbrutus 
28917169044Sbrutus 	/*
29017169044Sbrutus 	 * we don't use the dcopy_list_* code here because we need to due
29117169044Sbrutus 	 * some non-standard stuff.
29217169044Sbrutus 	 */
29317169044Sbrutus 
29417169044Sbrutus 	list = &dcopy_statep->d_globalchan_list;
29517169044Sbrutus 
29617169044Sbrutus 	/*
29717169044Sbrutus 	 * if nothing is on the channel list, return DCOPY_NORESOURCES. This
29817169044Sbrutus 	 * can happen if there aren't any DMA device registered.
29917169044Sbrutus 	 */
30017169044Sbrutus 	mutex_enter(&list->dl_mutex);
30117169044Sbrutus 	channel = list_head(&list->dl_list);
30217169044Sbrutus 	if (channel == NULL) {
30317169044Sbrutus 		mutex_exit(&list->dl_mutex);
30417169044Sbrutus 		return (DCOPY_NORESOURCES);
30517169044Sbrutus 	}
30617169044Sbrutus 
30717169044Sbrutus 	/*
30817169044Sbrutus 	 * increment the reference count, and pop the channel off the head and
30917169044Sbrutus 	 * push it on the tail. This ensures we rotate through the channels.
31017169044Sbrutus 	 * DMA channels are shared.
31117169044Sbrutus 	 */
31217169044Sbrutus 	channel->ch_ref_cnt++;
31317169044Sbrutus 	list_remove(&list->dl_list, channel);
31417169044Sbrutus 	list_insert_tail(&list->dl_list, channel);
31517169044Sbrutus 	mutex_exit(&list->dl_mutex);
31617169044Sbrutus 
31717169044Sbrutus 	*handle = (dcopy_handle_t)channel;
31817169044Sbrutus 	return (DCOPY_SUCCESS);
31917169044Sbrutus }
32017169044Sbrutus 
32117169044Sbrutus 
32217169044Sbrutus /*
32317169044Sbrutus  * dcopy_free()
32417169044Sbrutus  */
32517169044Sbrutus void
32617169044Sbrutus dcopy_free(dcopy_handle_t *channel)
32717169044Sbrutus {
32817169044Sbrutus 	dcopy_device_handle_t device;
32917169044Sbrutus 	dcopy_list_t *list;
330*b798e010SRichard Lowe 	boolean_t cleanup = B_FALSE;
33117169044Sbrutus 
33217169044Sbrutus 
33317169044Sbrutus 	ASSERT(*channel != NULL);
33417169044Sbrutus 
33517169044Sbrutus 	/*
33617169044Sbrutus 	 * we don't need to add the channel back to the list since we never
33717169044Sbrutus 	 * removed it. decrement the reference count.
33817169044Sbrutus 	 */
33917169044Sbrutus 	list = &dcopy_statep->d_globalchan_list;
34017169044Sbrutus 	mutex_enter(&list->dl_mutex);
34117169044Sbrutus 	(*channel)->ch_ref_cnt--;
34217169044Sbrutus 
34317169044Sbrutus 	/*
34417169044Sbrutus 	 * if we need to remove this channel, and the reference count is down
34517169044Sbrutus 	 * to 0, decrement the number of channels which still need to be
34617169044Sbrutus 	 * removed on the device.
34717169044Sbrutus 	 */
34817169044Sbrutus 	if ((*channel)->ch_removing && ((*channel)->ch_ref_cnt == 0)) {
34917169044Sbrutus 		device = (*channel)->ch_device;
35017169044Sbrutus 		mutex_enter(&device->dc_devchan_list.dl_mutex);
35117169044Sbrutus 		device->dc_removing_cnt--;
35217169044Sbrutus 		if (device->dc_removing_cnt == 0) {
35317169044Sbrutus 			cleanup = B_TRUE;
35417169044Sbrutus 		}
35517169044Sbrutus 		mutex_exit(&device->dc_devchan_list.dl_mutex);
35617169044Sbrutus 	}
35717169044Sbrutus 	mutex_exit(&list->dl_mutex);
35817169044Sbrutus 
35917169044Sbrutus 	/*
36017169044Sbrutus 	 * if there are no channels which still need to be removed, cleanup the
36117169044Sbrutus 	 * device state and call back into the DMA device driver to tell them
36217169044Sbrutus 	 * the device is free.
36317169044Sbrutus 	 */
36417169044Sbrutus 	if (cleanup) {
36517169044Sbrutus 		dcopy_device_cleanup(device, B_TRUE);
36617169044Sbrutus 	}
36717169044Sbrutus 
36817169044Sbrutus 	*channel = NULL;
36917169044Sbrutus }
37017169044Sbrutus 
37117169044Sbrutus 
37217169044Sbrutus /*
37317169044Sbrutus  * dcopy_query_channel()
37417169044Sbrutus  */
37517169044Sbrutus void
37617169044Sbrutus dcopy_query_channel(dcopy_handle_t channel, dcopy_query_channel_t *query)
37717169044Sbrutus {
37817169044Sbrutus 	*query = channel->ch_info;
37917169044Sbrutus }
38017169044Sbrutus 
38117169044Sbrutus 
38217169044Sbrutus /*
38317169044Sbrutus  * dcopy_cmd_alloc()
38417169044Sbrutus  */
38517169044Sbrutus int
38617169044Sbrutus dcopy_cmd_alloc(dcopy_handle_t handle, int flags, dcopy_cmd_t *cmd)
38717169044Sbrutus {
38817169044Sbrutus 	dcopy_handle_t channel;
38917169044Sbrutus 	dcopy_cmd_priv_t priv;
39017169044Sbrutus 	int e;
39117169044Sbrutus 
39217169044Sbrutus 
39317169044Sbrutus 	channel = handle;
39417169044Sbrutus 
39517169044Sbrutus 	atomic_inc_64(&channel->ch_stat.cs_cmd_alloc.value.ui64);
39617169044Sbrutus 	e = channel->ch_cb->cb_cmd_alloc(channel->ch_channel_private, flags,
39717169044Sbrutus 	    cmd);
39817169044Sbrutus 	if (e == DCOPY_SUCCESS) {
39917169044Sbrutus 		priv = (*cmd)->dp_private;
40017169044Sbrutus 		priv->pr_channel = channel;
40117169044Sbrutus 		/*
40217169044Sbrutus 		 * we won't initialize the blocking state until we actually
40317169044Sbrutus 		 * need to block.
40417169044Sbrutus 		 */
40517169044Sbrutus 		priv->pr_block_init = B_FALSE;
40617169044Sbrutus 	}
40717169044Sbrutus 
40817169044Sbrutus 	return (e);
40917169044Sbrutus }
41017169044Sbrutus 
41117169044Sbrutus 
41217169044Sbrutus /*
41317169044Sbrutus  * dcopy_cmd_free()
41417169044Sbrutus  */
41517169044Sbrutus void
41617169044Sbrutus dcopy_cmd_free(dcopy_cmd_t *cmd)
41717169044Sbrutus {
41817169044Sbrutus 	dcopy_handle_t channel;
41917169044Sbrutus 	dcopy_cmd_priv_t priv;
42017169044Sbrutus 
42117169044Sbrutus 
42217169044Sbrutus 	ASSERT(*cmd != NULL);
42317169044Sbrutus 
42417169044Sbrutus 	priv = (*cmd)->dp_private;
42517169044Sbrutus 	channel = priv->pr_channel;
42617169044Sbrutus 
42717169044Sbrutus 	/* if we initialized the blocking state, clean it up too */
42817169044Sbrutus 	if (priv->pr_block_init) {
42917169044Sbrutus 		cv_destroy(&priv->pr_cv);
43017169044Sbrutus 		mutex_destroy(&priv->pr_mutex);
43117169044Sbrutus 	}
43217169044Sbrutus 
43317169044Sbrutus 	channel->ch_cb->cb_cmd_free(channel->ch_channel_private, cmd);
43417169044Sbrutus }
43517169044Sbrutus 
43617169044Sbrutus 
43717169044Sbrutus /*
43817169044Sbrutus  * dcopy_cmd_post()
43917169044Sbrutus  */
44017169044Sbrutus int
44117169044Sbrutus dcopy_cmd_post(dcopy_cmd_t cmd)
44217169044Sbrutus {
44317169044Sbrutus 	dcopy_handle_t channel;
44417169044Sbrutus 	int e;
44517169044Sbrutus 
44617169044Sbrutus 
44717169044Sbrutus 	channel = cmd->dp_private->pr_channel;
44817169044Sbrutus 
44917169044Sbrutus 	atomic_inc_64(&channel->ch_stat.cs_cmd_post.value.ui64);
45017169044Sbrutus 	if (cmd->dp_cmd == DCOPY_CMD_COPY) {
45117169044Sbrutus 		atomic_add_64(&channel->ch_stat.cs_bytes_xfer.value.ui64,
45217169044Sbrutus 		    cmd->dp.copy.cc_size);
45317169044Sbrutus 	}
45417169044Sbrutus 	e = channel->ch_cb->cb_cmd_post(channel->ch_channel_private, cmd);
45517169044Sbrutus 	if (e != DCOPY_SUCCESS) {
45617169044Sbrutus 		return (e);
45717169044Sbrutus 	}
45817169044Sbrutus 
45917169044Sbrutus 	return (DCOPY_SUCCESS);
46017169044Sbrutus }
46117169044Sbrutus 
46217169044Sbrutus 
46317169044Sbrutus /*
46417169044Sbrutus  * dcopy_cmd_poll()
46517169044Sbrutus  */
46617169044Sbrutus int
46717169044Sbrutus dcopy_cmd_poll(dcopy_cmd_t cmd, int flags)
46817169044Sbrutus {
46917169044Sbrutus 	dcopy_handle_t channel;
47017169044Sbrutus 	dcopy_cmd_priv_t priv;
47117169044Sbrutus 	int e;
47217169044Sbrutus 
47317169044Sbrutus 
47417169044Sbrutus 	priv = cmd->dp_private;
47517169044Sbrutus 	channel = priv->pr_channel;
47617169044Sbrutus 
47717169044Sbrutus 	/*
47817169044Sbrutus 	 * if the caller is trying to block, they needed to post the
47917169044Sbrutus 	 * command with DCOPY_CMD_INTR set.
48017169044Sbrutus 	 */
48117169044Sbrutus 	if ((flags & DCOPY_POLL_BLOCK) && !(cmd->dp_flags & DCOPY_CMD_INTR)) {
48217169044Sbrutus 		return (DCOPY_FAILURE);
48317169044Sbrutus 	}
48417169044Sbrutus 
48517169044Sbrutus 	atomic_inc_64(&channel->ch_stat.cs_cmd_poll.value.ui64);
48617169044Sbrutus 
48717169044Sbrutus repoll:
48817169044Sbrutus 	e = channel->ch_cb->cb_cmd_poll(channel->ch_channel_private, cmd);
48917169044Sbrutus 	if (e == DCOPY_PENDING) {
49017169044Sbrutus 		/*
49117169044Sbrutus 		 * if the command is still active, and the blocking flag
49217169044Sbrutus 		 * is set.
49317169044Sbrutus 		 */
49417169044Sbrutus 		if (flags & DCOPY_POLL_BLOCK) {
49517169044Sbrutus 
49617169044Sbrutus 			/*
49717169044Sbrutus 			 * if we haven't initialized the state, do it now. A
49817169044Sbrutus 			 * command can be re-used, so it's possible it's
49917169044Sbrutus 			 * already been initialized.
50017169044Sbrutus 			 */
50117169044Sbrutus 			if (!priv->pr_block_init) {
50217169044Sbrutus 				priv->pr_block_init = B_TRUE;
50317169044Sbrutus 				mutex_init(&priv->pr_mutex, NULL, MUTEX_DRIVER,
50417169044Sbrutus 				    NULL);
50517169044Sbrutus 				cv_init(&priv->pr_cv, NULL, CV_DRIVER, NULL);
50617169044Sbrutus 				priv->pr_cmd = cmd;
50717169044Sbrutus 			}
50817169044Sbrutus 
50917169044Sbrutus 			/* push it on the list for blocking commands */
51017169044Sbrutus 			priv->pr_wait = B_TRUE;
51117169044Sbrutus 			dcopy_list_push(&channel->ch_poll_list, priv);
51217169044Sbrutus 
51317169044Sbrutus 			mutex_enter(&priv->pr_mutex);
51417169044Sbrutus 			/*
51517169044Sbrutus 			 * it's possible we already cleared pr_wait before we
51617169044Sbrutus 			 * grabbed the mutex.
51717169044Sbrutus 			 */
51817169044Sbrutus 			if (priv->pr_wait) {
51917169044Sbrutus 				cv_wait(&priv->pr_cv, &priv->pr_mutex);
52017169044Sbrutus 			}
52117169044Sbrutus 			mutex_exit(&priv->pr_mutex);
52217169044Sbrutus 
52317169044Sbrutus 			/*
52417169044Sbrutus 			 * the command has completed, go back and poll so we
52517169044Sbrutus 			 * get the status.
52617169044Sbrutus 			 */
52717169044Sbrutus 			goto repoll;
52817169044Sbrutus 		}
52917169044Sbrutus 	}
53017169044Sbrutus 
53117169044Sbrutus 	return (e);
53217169044Sbrutus }
53317169044Sbrutus 
53417169044Sbrutus /* *** END OF EXTERNAL INTERFACE *** */
53517169044Sbrutus 
53617169044Sbrutus /*
53717169044Sbrutus  * dcopy_list_init()
53817169044Sbrutus  */
53917169044Sbrutus static int
54017169044Sbrutus dcopy_list_init(dcopy_list_t *list, size_t node_size, offset_t link_offset)
54117169044Sbrutus {
54217169044Sbrutus 	mutex_init(&list->dl_mutex, NULL, MUTEX_DRIVER, NULL);
54317169044Sbrutus 	list_create(&list->dl_list, node_size, link_offset);
54417169044Sbrutus 	list->dl_cnt = 0;
54517169044Sbrutus 
54617169044Sbrutus 	return (DCOPY_SUCCESS);
54717169044Sbrutus }
54817169044Sbrutus 
54917169044Sbrutus 
55017169044Sbrutus /*
55117169044Sbrutus  * dcopy_list_fini()
55217169044Sbrutus  */
55317169044Sbrutus static void
55417169044Sbrutus dcopy_list_fini(dcopy_list_t *list)
55517169044Sbrutus {
55617169044Sbrutus 	list_destroy(&list->dl_list);
55717169044Sbrutus 	mutex_destroy(&list->dl_mutex);
55817169044Sbrutus }
55917169044Sbrutus 
56017169044Sbrutus 
56117169044Sbrutus /*
56217169044Sbrutus  * dcopy_list_push()
56317169044Sbrutus  */
56417169044Sbrutus static void
56517169044Sbrutus dcopy_list_push(dcopy_list_t *list, void *list_node)
56617169044Sbrutus {
56717169044Sbrutus 	mutex_enter(&list->dl_mutex);
56817169044Sbrutus 	list_insert_tail(&list->dl_list, list_node);
56917169044Sbrutus 	list->dl_cnt++;
57017169044Sbrutus 	mutex_exit(&list->dl_mutex);
57117169044Sbrutus }
57217169044Sbrutus 
57317169044Sbrutus 
57417169044Sbrutus /*
57517169044Sbrutus  * dcopy_list_pop()
57617169044Sbrutus  */
57717169044Sbrutus static void *
57817169044Sbrutus dcopy_list_pop(dcopy_list_t *list)
57917169044Sbrutus {
58017169044Sbrutus 	list_node_t *list_node;
58117169044Sbrutus 
58217169044Sbrutus 	mutex_enter(&list->dl_mutex);
58317169044Sbrutus 	list_node = list_head(&list->dl_list);
58417169044Sbrutus 	if (list_node == NULL) {
58517169044Sbrutus 		mutex_exit(&list->dl_mutex);
58617169044Sbrutus 		return (list_node);
58717169044Sbrutus 	}
58817169044Sbrutus 	list->dl_cnt--;
58917169044Sbrutus 	list_remove(&list->dl_list, list_node);
59017169044Sbrutus 	mutex_exit(&list->dl_mutex);
59117169044Sbrutus 
59217169044Sbrutus 	return (list_node);
59317169044Sbrutus }
59417169044Sbrutus 
59517169044Sbrutus 
59617169044Sbrutus /* *** DEVICE INTERFACE *** */
59717169044Sbrutus /*
59817169044Sbrutus  * dcopy_device_register()
59917169044Sbrutus  */
60017169044Sbrutus int
60117169044Sbrutus dcopy_device_register(void *device_private, dcopy_device_info_t *info,
60217169044Sbrutus     dcopy_device_handle_t *handle)
60317169044Sbrutus {
60417169044Sbrutus 	struct dcopy_channel_s *channel;
60517169044Sbrutus 	struct dcopy_device_s *device;
60617169044Sbrutus 	int e;
60717169044Sbrutus 	int i;
60817169044Sbrutus 
60917169044Sbrutus 
61017169044Sbrutus 	/* initialize the per device state */
61117169044Sbrutus 	device = kmem_zalloc(sizeof (*device), KM_SLEEP);
61217169044Sbrutus 	device->dc_device_private = device_private;
61317169044Sbrutus 	device->dc_info = *info;
61417169044Sbrutus 	device->dc_removing_cnt = 0;
61517169044Sbrutus 	device->dc_cb = info->di_cb;
61617169044Sbrutus 
61717169044Sbrutus 	/*
61817169044Sbrutus 	 * we have a per device channel list so we can remove a device in the
61917169044Sbrutus 	 * future.
62017169044Sbrutus 	 */
62117169044Sbrutus 	e = dcopy_list_init(&device->dc_devchan_list,
62217169044Sbrutus 	    sizeof (struct dcopy_channel_s),
62317169044Sbrutus 	    offsetof(struct dcopy_channel_s, ch_devchan_list_node));
62417169044Sbrutus 	if (e != DCOPY_SUCCESS) {
62517169044Sbrutus 		goto registerfail_devchan;
62617169044Sbrutus 	}
62717169044Sbrutus 
62817169044Sbrutus 	/*
62917169044Sbrutus 	 * allocate state for each channel, allocate the channel,  and then add
63017169044Sbrutus 	 * the devices dma channels to the devices channel list.
63117169044Sbrutus 	 */
63217169044Sbrutus 	for (i = 0; i < info->di_num_dma; i++) {
63317169044Sbrutus 		channel = kmem_zalloc(sizeof (*channel), KM_SLEEP);
63417169044Sbrutus 		channel->ch_device = device;
63517169044Sbrutus 		channel->ch_removing = B_FALSE;
63617169044Sbrutus 		channel->ch_ref_cnt = 0;
63717169044Sbrutus 		channel->ch_cb = info->di_cb;
63817169044Sbrutus 
63917169044Sbrutus 		e = info->di_cb->cb_channel_alloc(device_private, channel,
64017169044Sbrutus 		    DCOPY_SLEEP, dcopy_channel_size, &channel->ch_info,
64117169044Sbrutus 		    &channel->ch_channel_private);
64217169044Sbrutus 		if (e != DCOPY_SUCCESS) {
64317169044Sbrutus 			kmem_free(channel, sizeof (*channel));
64417169044Sbrutus 			goto registerfail_alloc;
64517169044Sbrutus 		}
64617169044Sbrutus 
64717169044Sbrutus 		e = dcopy_stats_init(channel);
64817169044Sbrutus 		if (e != DCOPY_SUCCESS) {
64917169044Sbrutus 			info->di_cb->cb_channel_free(
65017169044Sbrutus 			    &channel->ch_channel_private);
65117169044Sbrutus 			kmem_free(channel, sizeof (*channel));
65217169044Sbrutus 			goto registerfail_alloc;
65317169044Sbrutus 		}
65417169044Sbrutus 
65517169044Sbrutus 		e = dcopy_list_init(&channel->ch_poll_list,
65617169044Sbrutus 		    sizeof (struct dcopy_cmd_priv_s),
65717169044Sbrutus 		    offsetof(struct dcopy_cmd_priv_s, pr_poll_list_node));
65817169044Sbrutus 		if (e != DCOPY_SUCCESS) {
65917169044Sbrutus 			dcopy_stats_fini(channel);
66017169044Sbrutus 			info->di_cb->cb_channel_free(
66117169044Sbrutus 			    &channel->ch_channel_private);
66217169044Sbrutus 			kmem_free(channel, sizeof (*channel));
66317169044Sbrutus 			goto registerfail_alloc;
66417169044Sbrutus 		}
66517169044Sbrutus 
66617169044Sbrutus 		dcopy_list_push(&device->dc_devchan_list, channel);
66717169044Sbrutus 	}
66817169044Sbrutus 
66917169044Sbrutus 	/* add the device to device list */
67017169044Sbrutus 	dcopy_list_push(&dcopy_statep->d_device_list, device);
67117169044Sbrutus 
67217169044Sbrutus 	/*
67317169044Sbrutus 	 * add the device's dma channels to the global channel list (where
67417169044Sbrutus 	 * dcopy_alloc's come from)
67517169044Sbrutus 	 */
67617169044Sbrutus 	mutex_enter(&dcopy_statep->d_globalchan_list.dl_mutex);
67717169044Sbrutus 	mutex_enter(&dcopy_statep->d_device_list.dl_mutex);
67817169044Sbrutus 	channel = list_head(&device->dc_devchan_list.dl_list);
67917169044Sbrutus 	while (channel != NULL) {
68017169044Sbrutus 		list_insert_tail(&dcopy_statep->d_globalchan_list.dl_list,
68117169044Sbrutus 		    channel);
68217169044Sbrutus 		dcopy_statep->d_globalchan_list.dl_cnt++;
68317169044Sbrutus 		channel = list_next(&device->dc_devchan_list.dl_list, channel);
68417169044Sbrutus 	}
68517169044Sbrutus 	mutex_exit(&dcopy_statep->d_device_list.dl_mutex);
68617169044Sbrutus 	mutex_exit(&dcopy_statep->d_globalchan_list.dl_mutex);
68717169044Sbrutus 
68817169044Sbrutus 	*handle = device;
68917169044Sbrutus 
69017169044Sbrutus 	/* last call-back into kernel for dcopy KAPI enabled */
69117169044Sbrutus 	uioa_dcopy_enable();
69217169044Sbrutus 
69317169044Sbrutus 	return (DCOPY_SUCCESS);
69417169044Sbrutus 
69517169044Sbrutus registerfail_alloc:
69617169044Sbrutus 	channel = list_head(&device->dc_devchan_list.dl_list);
69717169044Sbrutus 	while (channel != NULL) {
69817169044Sbrutus 		/* remove from the list */
69917169044Sbrutus 		channel = dcopy_list_pop(&device->dc_devchan_list);
70017169044Sbrutus 		ASSERT(channel != NULL);
70117169044Sbrutus 
70217169044Sbrutus 		dcopy_list_fini(&channel->ch_poll_list);
70317169044Sbrutus 		dcopy_stats_fini(channel);
70417169044Sbrutus 		info->di_cb->cb_channel_free(&channel->ch_channel_private);
70517169044Sbrutus 		kmem_free(channel, sizeof (*channel));
70617169044Sbrutus 	}
70717169044Sbrutus 
70817169044Sbrutus 	dcopy_list_fini(&device->dc_devchan_list);
70917169044Sbrutus registerfail_devchan:
71017169044Sbrutus 	kmem_free(device, sizeof (*device));
71117169044Sbrutus 
71217169044Sbrutus 	return (DCOPY_FAILURE);
71317169044Sbrutus }
71417169044Sbrutus 
71517169044Sbrutus 
71617169044Sbrutus /*
71717169044Sbrutus  * dcopy_device_unregister()
71817169044Sbrutus  */
71917169044Sbrutus /*ARGSUSED*/
72017169044Sbrutus int
72117169044Sbrutus dcopy_device_unregister(dcopy_device_handle_t *handle)
72217169044Sbrutus {
72317169044Sbrutus 	struct dcopy_channel_s *channel;
72417169044Sbrutus 	dcopy_device_handle_t device;
72517169044Sbrutus 	boolean_t device_busy;
72617169044Sbrutus 
72717169044Sbrutus 	/* first call-back into kernel for dcopy KAPI disable */
72817169044Sbrutus 	uioa_dcopy_disable();
72917169044Sbrutus 
73017169044Sbrutus 	device = *handle;
73117169044Sbrutus 	device_busy = B_FALSE;
73217169044Sbrutus 
73317169044Sbrutus 	/*
73417169044Sbrutus 	 * remove the devices dma channels from the global channel list (where
73517169044Sbrutus 	 * dcopy_alloc's come from)
73617169044Sbrutus 	 */
73717169044Sbrutus 	mutex_enter(&dcopy_statep->d_globalchan_list.dl_mutex);
73817169044Sbrutus 	mutex_enter(&device->dc_devchan_list.dl_mutex);
73917169044Sbrutus 	channel = list_head(&device->dc_devchan_list.dl_list);
74017169044Sbrutus 	while (channel != NULL) {
74117169044Sbrutus 		/*
74217169044Sbrutus 		 * if the channel has outstanding allocs, mark it as having
74317169044Sbrutus 		 * to be removed and increment the number of channels which
74417169044Sbrutus 		 * need to be removed in the device state too.
74517169044Sbrutus 		 */
74617169044Sbrutus 		if (channel->ch_ref_cnt != 0) {
74717169044Sbrutus 			channel->ch_removing = B_TRUE;
74817169044Sbrutus 			device_busy = B_TRUE;
74917169044Sbrutus 			device->dc_removing_cnt++;
75017169044Sbrutus 		}
75117169044Sbrutus 		dcopy_statep->d_globalchan_list.dl_cnt--;
75217169044Sbrutus 		list_remove(&dcopy_statep->d_globalchan_list.dl_list, channel);
75317169044Sbrutus 		channel = list_next(&device->dc_devchan_list.dl_list, channel);
75417169044Sbrutus 	}
75517169044Sbrutus 	mutex_exit(&device->dc_devchan_list.dl_mutex);
75617169044Sbrutus 	mutex_exit(&dcopy_statep->d_globalchan_list.dl_mutex);
75717169044Sbrutus 
75817169044Sbrutus 	/*
75917169044Sbrutus 	 * if there are channels which still need to be removed, we will clean
76017169044Sbrutus 	 * up the device state after they are freed up.
76117169044Sbrutus 	 */
76217169044Sbrutus 	if (device_busy) {
76317169044Sbrutus 		return (DCOPY_PENDING);
76417169044Sbrutus 	}
76517169044Sbrutus 
76617169044Sbrutus 	dcopy_device_cleanup(device, B_FALSE);
76717169044Sbrutus 
76817169044Sbrutus 	*handle = NULL;
76917169044Sbrutus 	return (DCOPY_SUCCESS);
77017169044Sbrutus }
77117169044Sbrutus 
77217169044Sbrutus 
77317169044Sbrutus /*
77417169044Sbrutus  * dcopy_device_cleanup()
77517169044Sbrutus  */
77617169044Sbrutus static void
77717169044Sbrutus dcopy_device_cleanup(dcopy_device_handle_t device, boolean_t do_callback)
77817169044Sbrutus {
77917169044Sbrutus 	struct dcopy_channel_s *channel;
78017169044Sbrutus 
78117169044Sbrutus 	/*
78217169044Sbrutus 	 * remove all the channels in the device list, free them, and clean up
78317169044Sbrutus 	 * the state.
78417169044Sbrutus 	 */
78517169044Sbrutus 	mutex_enter(&dcopy_statep->d_device_list.dl_mutex);
78617169044Sbrutus 	channel = list_head(&device->dc_devchan_list.dl_list);
78717169044Sbrutus 	while (channel != NULL) {
78817169044Sbrutus 		device->dc_devchan_list.dl_cnt--;
78917169044Sbrutus 		list_remove(&device->dc_devchan_list.dl_list, channel);
79017169044Sbrutus 		dcopy_list_fini(&channel->ch_poll_list);
79117169044Sbrutus 		dcopy_stats_fini(channel);
79217169044Sbrutus 		channel->ch_cb->cb_channel_free(&channel->ch_channel_private);
79317169044Sbrutus 		kmem_free(channel, sizeof (*channel));
79417169044Sbrutus 		channel = list_head(&device->dc_devchan_list.dl_list);
79517169044Sbrutus 	}
79617169044Sbrutus 
79717169044Sbrutus 	/* remove it from the list of devices */
79817169044Sbrutus 	list_remove(&dcopy_statep->d_device_list.dl_list, device);
79917169044Sbrutus 
80017169044Sbrutus 	mutex_exit(&dcopy_statep->d_device_list.dl_mutex);
80117169044Sbrutus 
80217169044Sbrutus 	/*
80317169044Sbrutus 	 * notify the DMA device driver that the device is free to be
80417169044Sbrutus 	 * detached.
80517169044Sbrutus 	 */
80617169044Sbrutus 	if (do_callback) {
80717169044Sbrutus 		device->dc_cb->cb_unregister_complete(
80817169044Sbrutus 		    device->dc_device_private, DCOPY_SUCCESS);
80917169044Sbrutus 	}
81017169044Sbrutus 
81117169044Sbrutus 	dcopy_list_fini(&device->dc_devchan_list);
81217169044Sbrutus 	kmem_free(device, sizeof (*device));
81317169044Sbrutus }
81417169044Sbrutus 
81517169044Sbrutus 
81617169044Sbrutus /*
81717169044Sbrutus  * dcopy_device_channel_notify()
81817169044Sbrutus  */
81917169044Sbrutus /*ARGSUSED*/
82017169044Sbrutus void
82117169044Sbrutus dcopy_device_channel_notify(dcopy_handle_t handle, int status)
82217169044Sbrutus {
82317169044Sbrutus 	struct dcopy_channel_s *channel;
82417169044Sbrutus 	dcopy_list_t *poll_list;
82517169044Sbrutus 	dcopy_cmd_priv_t priv;
82617169044Sbrutus 	int e;
82717169044Sbrutus 
82817169044Sbrutus 
82917169044Sbrutus 	ASSERT(status == DCOPY_COMPLETION);
83017169044Sbrutus 	channel = handle;
83117169044Sbrutus 
83217169044Sbrutus 	poll_list = &channel->ch_poll_list;
83317169044Sbrutus 
83417169044Sbrutus 	/*
83517169044Sbrutus 	 * when we get a completion notification from the device, go through
83617169044Sbrutus 	 * all of the commands blocking on this channel and see if they have
83717169044Sbrutus 	 * completed. Remove the command and wake up the block thread if they
83817169044Sbrutus 	 * have. Once we hit a command which is still pending, we are done
83917169044Sbrutus 	 * polling since commands in a channel complete in order.
84017169044Sbrutus 	 */
84117169044Sbrutus 	mutex_enter(&poll_list->dl_mutex);
84217169044Sbrutus 	if (poll_list->dl_cnt != 0) {
84317169044Sbrutus 		priv = list_head(&poll_list->dl_list);
84417169044Sbrutus 		while (priv != NULL) {
84517169044Sbrutus 			atomic_inc_64(&channel->
84617169044Sbrutus 			    ch_stat.cs_notify_poll.value.ui64);
84717169044Sbrutus 			e = channel->ch_cb->cb_cmd_poll(
84817169044Sbrutus 			    channel->ch_channel_private,
84917169044Sbrutus 			    priv->pr_cmd);
85017169044Sbrutus 			if (e == DCOPY_PENDING) {
85117169044Sbrutus 				atomic_inc_64(&channel->
85217169044Sbrutus 				    ch_stat.cs_notify_pending.value.ui64);
85317169044Sbrutus 				break;
85417169044Sbrutus 			}
85517169044Sbrutus 
85617169044Sbrutus 			poll_list->dl_cnt--;
85717169044Sbrutus 			list_remove(&poll_list->dl_list, priv);
85817169044Sbrutus 
85917169044Sbrutus 			mutex_enter(&priv->pr_mutex);
86017169044Sbrutus 			priv->pr_wait = B_FALSE;
86117169044Sbrutus 			cv_signal(&priv->pr_cv);
86217169044Sbrutus 			mutex_exit(&priv->pr_mutex);
86317169044Sbrutus 
86417169044Sbrutus 			priv = list_head(&poll_list->dl_list);
86517169044Sbrutus 		}
86617169044Sbrutus 	}
86717169044Sbrutus 
86817169044Sbrutus 	mutex_exit(&poll_list->dl_mutex);
86917169044Sbrutus }
87017169044Sbrutus 
87117169044Sbrutus 
87217169044Sbrutus /*
87317169044Sbrutus  * dcopy_stats_init()
87417169044Sbrutus  */
87517169044Sbrutus static int
87617169044Sbrutus dcopy_stats_init(dcopy_handle_t channel)
87717169044Sbrutus {
87817169044Sbrutus #define	CHANSTRSIZE	20
87917169044Sbrutus 	char chanstr[CHANSTRSIZE];
88017169044Sbrutus 	dcopy_stats_t *stats;
88117169044Sbrutus 	int instance;
88217169044Sbrutus 	char *name;
88317169044Sbrutus 
88417169044Sbrutus 
88517169044Sbrutus 	stats = &channel->ch_stat;
88617169044Sbrutus 	name = (char *)ddi_driver_name(channel->ch_device->dc_info.di_dip);
88717169044Sbrutus 	instance = ddi_get_instance(channel->ch_device->dc_info.di_dip);
88817169044Sbrutus 
88917169044Sbrutus 	(void) snprintf(chanstr, CHANSTRSIZE, "channel%d",
89017169044Sbrutus 	    (uint32_t)channel->ch_info.qc_chan_num);
89117169044Sbrutus 
89217169044Sbrutus 	channel->ch_kstat = kstat_create(name, instance, chanstr, "misc",
89317169044Sbrutus 	    KSTAT_TYPE_NAMED, sizeof (dcopy_stats_t) / sizeof (kstat_named_t),
89417169044Sbrutus 	    KSTAT_FLAG_VIRTUAL);
89517169044Sbrutus 	if (channel->ch_kstat == NULL) {
89617169044Sbrutus 		return (DCOPY_FAILURE);
89717169044Sbrutus 	}
89817169044Sbrutus 	channel->ch_kstat->ks_data = stats;
89917169044Sbrutus 
90017169044Sbrutus 	kstat_named_init(&stats->cs_bytes_xfer, "bytes_xfer",
90117169044Sbrutus 	    KSTAT_DATA_UINT64);
90217169044Sbrutus 	kstat_named_init(&stats->cs_cmd_alloc, "cmd_alloc",
90317169044Sbrutus 	    KSTAT_DATA_UINT64);
90417169044Sbrutus 	kstat_named_init(&stats->cs_cmd_post, "cmd_post",
90517169044Sbrutus 	    KSTAT_DATA_UINT64);
90617169044Sbrutus 	kstat_named_init(&stats->cs_cmd_poll, "cmd_poll",
90717169044Sbrutus 	    KSTAT_DATA_UINT64);
90817169044Sbrutus 	kstat_named_init(&stats->cs_notify_poll, "notify_poll",
90917169044Sbrutus 	    KSTAT_DATA_UINT64);
91017169044Sbrutus 	kstat_named_init(&stats->cs_notify_pending, "notify_pending",
91117169044Sbrutus 	    KSTAT_DATA_UINT64);
91217169044Sbrutus 	kstat_named_init(&stats->cs_id, "id",
91317169044Sbrutus 	    KSTAT_DATA_UINT64);
91417169044Sbrutus 	kstat_named_init(&stats->cs_capabilities, "capabilities",
91517169044Sbrutus 	    KSTAT_DATA_UINT64);
91617169044Sbrutus 
91717169044Sbrutus 	kstat_install(channel->ch_kstat);
91817169044Sbrutus 
91917169044Sbrutus 	channel->ch_stat.cs_id.value.ui64 = channel->ch_info.qc_id;
92017169044Sbrutus 	channel->ch_stat.cs_capabilities.value.ui64 =
92117169044Sbrutus 	    channel->ch_info.qc_capabilities;
92217169044Sbrutus 
92317169044Sbrutus 	return (DCOPY_SUCCESS);
92417169044Sbrutus }
92517169044Sbrutus 
92617169044Sbrutus 
92717169044Sbrutus /*
92817169044Sbrutus  * dcopy_stats_fini()
92917169044Sbrutus  */
93017169044Sbrutus static void
93117169044Sbrutus dcopy_stats_fini(dcopy_handle_t channel)
93217169044Sbrutus {
93317169044Sbrutus 	kstat_delete(channel->ch_kstat);
93417169044Sbrutus }
93517169044Sbrutus /* *** END OF DEVICE INTERFACE *** */
936