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