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