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