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