1 /**
2 * @file daemon/opd_ibs.c
3 * AMD Family10h Instruction Based Sampling (IBS) handling.
4 *
5 * @remark Copyright 2007 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Jason Yeh <jason.yeh@amd.com>
9 * @author Paul Drongowski <paul.drongowski@amd.com>
10 * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
11 * Copyright (c) 2008 Advanced Micro Devices, Inc.
12 */
13
14 #include "op_hw_config.h"
15 #include "op_events.h"
16 #include "op_string.h"
17 #include "op_libiberty.h"
18 #include "opd_printf.h"
19 #include "opd_trans.h"
20 #include "opd_events.h"
21 #include "opd_kernel.h"
22 #include "opd_anon.h"
23 #include "opd_sfile.h"
24 #include "opd_interface.h"
25 #include "opd_mangling.h"
26 #include "opd_extended.h"
27 #include "opd_ibs.h"
28 #include "opd_ibs_trans.h"
29 #include "opd_ibs_macro.h"
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <string.h>
35
36 extern op_cpu cpu_type;
37 extern int no_event_ok;
38 extern int sfile_equal(struct sfile const * sf, struct sfile const * sf2);
39 extern void sfile_dup(struct sfile * to, struct sfile * from);
40
41 /* IBS Select Arrays/Counters */
42 static unsigned int ibs_selected_size;
43 static unsigned int ibs_fetch_selected_flag;
44 static unsigned int ibs_fetch_selected_size;
45 static unsigned int ibs_op_selected_flag;
46 static unsigned int ibs_op_selected_size;
47 static unsigned int ibs_op_ls_selected_flag;
48 static unsigned int ibs_op_ls_selected_size;
49 static unsigned int ibs_op_nb_selected_flag;
50 static unsigned int ibs_op_nb_selected_size;
51
52 /* IBS Statistics */
53 static unsigned long ibs_fetch_sample_stats;
54 static unsigned long ibs_fetch_incomplete_stats;
55 static unsigned long ibs_op_sample_stats;
56 static unsigned long ibs_op_incomplete_stats;
57 static unsigned long ibs_derived_event_stats;
58
59 /*
60 * IBS Virtual Counter
61 */
62 struct opd_event ibs_vc[OP_MAX_IBS_COUNTERS];
63
64 /* IBS Virtual Counter Index(VCI) Map*/
65 unsigned int ibs_vci_map[OP_MAX_IBS_COUNTERS];
66
67 /**
68 * This function converts IBS fetch event flags and values into
69 * derived events. If the tagged (sampled) fetched caused a derived
70 * event, the derived event is tallied.
71 */
opd_log_ibs_fetch(struct transient * trans)72 static void opd_log_ibs_fetch(struct transient * trans)
73 {
74 struct ibs_fetch_sample * trans_fetch = ((struct ibs_sample*)(trans->ext))->fetch;
75 if (!trans_fetch)
76 return;
77
78 trans_ibs_fetch(trans, ibs_fetch_selected_flag, ibs_fetch_selected_size);
79 }
80
81
82 /**
83 * This function translates the IBS op event flags and values into
84 * IBS op derived events. If an op derived event occured, it's tallied.
85 */
opd_log_ibs_op(struct transient * trans)86 static void opd_log_ibs_op(struct transient * trans)
87 {
88 struct ibs_op_sample * trans_op = ((struct ibs_sample*)(trans->ext))->op;
89 if (!trans_op)
90 return;
91
92 trans_ibs_op(trans, ibs_op_selected_flag, ibs_op_selected_size);
93 trans_ibs_op_ls(trans, ibs_op_ls_selected_flag, ibs_op_ls_selected_size);
94 trans_ibs_op_nb(trans, ibs_op_nb_selected_flag, ibs_op_nb_selected_size);
95 }
96
97
opd_put_ibs_sample(struct transient * trans)98 static void opd_put_ibs_sample(struct transient * trans)
99 {
100 unsigned long long event = 0;
101 struct kernel_image * k_image = NULL;
102 struct ibs_fetch_sample * trans_fetch = ((struct ibs_sample*)(trans->ext))->fetch;
103
104 if (!enough_remaining(trans, 1)) {
105 trans->remaining = 0;
106 return;
107 }
108
109 /* IBS can generate samples with invalid dcookie and
110 * in kernel address range. Map such samples to vmlinux
111 * only if the user either specifies a range, or vmlinux.
112 */
113 if (trans->cookie == INVALID_COOKIE
114 && (k_image = find_kernel_image(trans)) != NULL
115 && (k_image->start != 0 && k_image->end != 0)
116 && trans->in_kernel == 0)
117 trans->in_kernel = 1;
118
119 if (trans->tracing != TRACING_ON)
120 trans->event = event;
121
122 /* sfile can change at each sample for kernel */
123 if (trans->in_kernel != 0)
124 clear_trans_current(trans);
125
126 if (!trans->in_kernel && trans->cookie == NO_COOKIE)
127 trans->anon = find_anon_mapping(trans);
128
129 /* get the current sfile if needed */
130 if (!trans->current)
131 trans->current = sfile_find(trans);
132
133 /*
134 * can happen if kernel sample falls through the cracks, or if
135 * it's a sample from an anon region we couldn't find
136 */
137 if (!trans->current)
138 goto out;
139
140 if (trans_fetch)
141 opd_log_ibs_fetch(trans);
142 else
143 opd_log_ibs_op(trans);
144 out:
145 /* switch to trace mode */
146 if (trans->tracing == TRACING_START)
147 trans->tracing = TRACING_ON;
148
149 update_trans_last(trans);
150 }
151
152
code_ibs_fetch_sample(struct transient * trans)153 void code_ibs_fetch_sample(struct transient * trans)
154 {
155 struct ibs_fetch_sample * trans_fetch = NULL;
156
157 if (!enough_remaining(trans, 7)) {
158 verbprintf(vext, "not enough remaining\n");
159 trans->remaining = 0;
160 ibs_fetch_incomplete_stats++;
161 return;
162 }
163
164 ibs_fetch_sample_stats++;
165
166 trans->ext = xmalloc(sizeof(struct ibs_sample));
167 ((struct ibs_sample*)(trans->ext))->fetch = xmalloc(sizeof(struct ibs_fetch_sample));
168 trans_fetch = ((struct ibs_sample*)(trans->ext))->fetch;
169
170 trans_fetch->rip = pop_buffer_value(trans);
171
172 trans_fetch->ibs_fetch_lin_addr_low = pop_buffer_value(trans);
173 trans_fetch->ibs_fetch_lin_addr_high = pop_buffer_value(trans);
174
175 trans_fetch->ibs_fetch_ctl_low = pop_buffer_value(trans);
176 trans_fetch->ibs_fetch_ctl_high = pop_buffer_value(trans);
177 trans_fetch->ibs_fetch_phys_addr_low = pop_buffer_value(trans);
178 trans_fetch->ibs_fetch_phys_addr_high = pop_buffer_value(trans);
179
180 verbprintf(vsamples,
181 "FETCH_X CPU:%ld PID:%ld RIP:%lx CTL_H:%x LAT:%d P_HI:%x P_LO:%x L_HI:%x L_LO:%x\n",
182 trans->cpu,
183 (long)trans->tgid,
184 trans_fetch->rip,
185 (trans_fetch->ibs_fetch_ctl_high >> 16) & 0x3ff,
186 (trans_fetch->ibs_fetch_ctl_high) & 0xffff,
187 trans_fetch->ibs_fetch_phys_addr_high,
188 trans_fetch->ibs_fetch_phys_addr_low,
189 trans_fetch->ibs_fetch_lin_addr_high,
190 trans_fetch->ibs_fetch_lin_addr_low) ;
191
192 /* Overwrite the trans->pc with the more accurate trans_fetch->rip */
193 trans->pc = trans_fetch->rip;
194
195 opd_put_ibs_sample(trans);
196
197 free(trans_fetch);
198 free(trans->ext);
199 trans->ext = NULL;
200 }
201
202
code_ibs_op_sample(struct transient * trans)203 void code_ibs_op_sample(struct transient * trans)
204 {
205 struct ibs_op_sample * trans_op= NULL;
206
207 if (!enough_remaining(trans, 13)) {
208 verbprintf(vext, "not enough remaining\n");
209 trans->remaining = 0;
210 ibs_op_incomplete_stats++;
211 return;
212 }
213
214 ibs_op_sample_stats++;
215
216 trans->ext = xmalloc(sizeof(struct ibs_sample));
217 ((struct ibs_sample*)(trans->ext))->op = xmalloc(sizeof(struct ibs_op_sample));
218 trans_op = ((struct ibs_sample*)(trans->ext))->op;
219
220 trans_op->rip = pop_buffer_value(trans);
221
222 trans_op->ibs_op_lin_addr_low = pop_buffer_value(trans);
223 trans_op->ibs_op_lin_addr_high = pop_buffer_value(trans);
224
225 trans_op->ibs_op_data1_low = pop_buffer_value(trans);
226 trans_op->ibs_op_data1_high = pop_buffer_value(trans);
227 trans_op->ibs_op_data2_low = pop_buffer_value(trans);
228 trans_op->ibs_op_data2_high = pop_buffer_value(trans);
229 trans_op->ibs_op_data3_low = pop_buffer_value(trans);
230 trans_op->ibs_op_data3_high = pop_buffer_value(trans);
231 trans_op->ibs_op_ldst_linaddr_low = pop_buffer_value(trans);
232 trans_op->ibs_op_ldst_linaddr_high = pop_buffer_value(trans);
233 trans_op->ibs_op_phys_addr_low = pop_buffer_value(trans);
234 trans_op->ibs_op_phys_addr_high = pop_buffer_value(trans);
235
236 verbprintf(vsamples,
237 "IBS_OP_X CPU:%ld PID:%d RIP:%lx D1HI:%x D1LO:%x D2LO:%x D3HI:%x D3LO:%x L_LO:%x P_LO:%x\n",
238 trans->cpu,
239 trans->tgid,
240 trans_op->rip,
241 trans_op->ibs_op_data1_high,
242 trans_op->ibs_op_data1_low,
243 trans_op->ibs_op_data2_low,
244 trans_op->ibs_op_data3_high,
245 trans_op->ibs_op_data3_low,
246 trans_op->ibs_op_ldst_linaddr_low,
247 trans_op->ibs_op_phys_addr_low);
248
249 /* Overwrite the trans->pc with the more accurate trans_op->rip */
250 trans->pc = trans_op->rip;
251
252 opd_put_ibs_sample(trans);
253
254 free(trans_op);
255 free(trans->ext);
256 trans->ext = NULL;
257 }
258
259
260 /** Convert IBS event to value used for data structure indexing */
ibs_event_to_counter(unsigned long x)261 static unsigned long ibs_event_to_counter(unsigned long x)
262 {
263 unsigned long ret = ~0UL;
264
265 if (IS_IBS_FETCH(x))
266 ret = (x - IBS_FETCH_BASE);
267 else if (IS_IBS_OP(x))
268 ret = (x - IBS_OP_BASE + IBS_FETCH_MAX);
269 else if (IS_IBS_OP_LS(x))
270 ret = (x - IBS_OP_LS_BASE + IBS_OP_MAX + IBS_FETCH_MAX);
271 else if (IS_IBS_OP_NB(x))
272 ret = (x - IBS_OP_NB_BASE + IBS_OP_LS_MAX + IBS_OP_MAX + IBS_FETCH_MAX);
273
274 return (ret != ~0UL) ? ret + OP_MAX_COUNTERS : ret;
275 }
276
277
opd_log_ibs_event(unsigned int event,struct transient * trans)278 void opd_log_ibs_event(unsigned int event,
279 struct transient * trans)
280 {
281 ibs_derived_event_stats++;
282 trans->event = event;
283 sfile_log_sample_count(trans, 1);
284 }
285
286
opd_log_ibs_count(unsigned int event,struct transient * trans,unsigned int count)287 void opd_log_ibs_count(unsigned int event,
288 struct transient * trans,
289 unsigned int count)
290 {
291 ibs_derived_event_stats++;
292 trans->event = event;
293 sfile_log_sample_count(trans, count);
294 }
295
296
get_ibs_vci_key(unsigned int event)297 static unsigned long get_ibs_vci_key(unsigned int event)
298 {
299 unsigned long key = ibs_event_to_counter(event);
300 if (key == ~0UL || key < OP_MAX_COUNTERS)
301 return ~0UL;
302
303 key = key - OP_MAX_COUNTERS;
304
305 return key;
306 }
307
308
ibs_parse_and_set_events(char * str)309 static int ibs_parse_and_set_events(char * str)
310 {
311 char * tmp, * ptr, * tok1, * tok2 = NULL;
312 int is_done = 0;
313 struct op_event * event = NULL;
314 op_cpu cpu_type = CPU_NO_GOOD;
315 unsigned long key;
316
317 if (!str)
318 return -1;
319
320 cpu_type = op_get_cpu_type();
321 op_events(cpu_type);
322
323 tmp = op_xstrndup(str, strlen(str));
324 ptr = tmp;
325
326 while (is_done != 1
327 && (tok1 = strtok_r(ptr, ",", &tok2)) != NULL) {
328
329 if ((ptr = strstr(tok1, ":")) != NULL) {
330 *ptr = '\0';
331 is_done = 1;
332 }
333
334 // Resove event number
335 event = find_event_by_name(tok1, 0, 0);
336 if (!event)
337 return -1;
338
339 // Grouping
340 if (IS_IBS_FETCH(event->val)) {
341 ibs_fetch_selected_flag |= 1 << IBS_FETCH_OFFSET(event->val);
342 ibs_fetch_selected_size++;
343 } else if (IS_IBS_OP(event->val)) {
344 ibs_op_selected_flag |= 1 << IBS_OP_OFFSET(event->val);
345 ibs_op_selected_size++;
346 } else if (IS_IBS_OP_LS(event->val)) {
347 ibs_op_ls_selected_flag |= 1 << IBS_OP_LS_OFFSET(event->val);
348 ibs_op_ls_selected_size++;
349 } else if (IS_IBS_OP_NB(event->val)) {
350 ibs_op_nb_selected_flag |= 1 << IBS_OP_NB_OFFSET(event->val);
351 ibs_op_nb_selected_size++;
352 } else {
353 return -1;
354 }
355
356 key = get_ibs_vci_key(event->val);
357 if (key == ~0UL)
358 return -1;
359
360 ibs_vci_map[key] = ibs_selected_size;
361
362 /* Initialize part of ibs_vc */
363 ibs_vc[ibs_selected_size].name = tok1;
364 ibs_vc[ibs_selected_size].value = event->val;
365 ibs_vc[ibs_selected_size].counter = ibs_selected_size + OP_MAX_COUNTERS;
366 ibs_vc[ibs_selected_size].kernel = 1;
367 ibs_vc[ibs_selected_size].user = 1;
368
369 ibs_selected_size++;
370
371 ptr = NULL;
372 }
373
374 return 0;
375 }
376
377
ibs_parse_counts(char * str,unsigned long int * count)378 static int ibs_parse_counts(char * str, unsigned long int * count)
379 {
380 char * tmp, * tok1, * tok2 = NULL, *end = NULL;
381 if (!str)
382 return -1;
383
384 tmp = op_xstrndup(str, strlen(str));
385 tok1 = strtok_r(tmp, ":", &tok2);
386 *count = strtoul(tok1, &end, 10);
387 if ((end && *end) || *count == 0
388 || errno == EINVAL || errno == ERANGE) {
389 fprintf(stderr,"Invalid count (%s)\n", str);
390 return -1;
391 }
392
393 return 0;
394 }
395
396
ibs_parse_and_set_um_fetch(char const * str)397 static int ibs_parse_and_set_um_fetch(char const * str)
398 {
399 if (!str)
400 return -1;
401 return 0;
402 }
403
404
405
ibs_parse_and_set_um_op(char const * str,unsigned long int * ibs_op_um)406 static int ibs_parse_and_set_um_op(char const * str, unsigned long int * ibs_op_um)
407 {
408 char * end = NULL;
409 if (!str)
410 return -1;
411
412 *ibs_op_um = strtoul(str, &end, 16);
413 if ((end && *end) || errno == EINVAL || errno == ERANGE) {
414 fprintf(stderr,"Invalid unitmaks (%s)\n", str);
415 return -1;
416 }
417 return 0;
418 }
419
420
ibs_init(char const * argv)421 static int ibs_init(char const * argv)
422 {
423 char * tmp, * ptr, * tok1, * tok2 = NULL;
424 unsigned int i = 0;
425 unsigned long int ibs_fetch_count = 0;
426 unsigned long int ibs_op_count = 0;
427 unsigned long int ibs_op_um = 0;
428
429 if (!argv)
430 return -1;
431
432 if (empty_line(argv) != 0)
433 return -1;
434
435 tmp = op_xstrndup(argv, strlen(argv));
436 ptr = (char *) skip_ws(tmp);
437
438 // "fetch:event1,event2,....:count:um|op:event1,event2,.....:count:um"
439 tok1 = strtok_r(ptr, "|", &tok2);
440
441 while (tok1 != NULL) {
442
443 if (!strncmp("fetch:", tok1, strlen("fetch:"))) {
444 // Get to event section
445 tok1 = tok1 + strlen("fetch:");
446 if (ibs_parse_and_set_events(tok1) == -1)
447 return -1;
448
449 // Get to count section
450 while (tok1) {
451 if (*tok1 == '\0')
452 return -1;
453 if (*tok1 != ':') {
454 tok1++;
455 } else {
456 tok1++;
457 break;
458 }
459 }
460
461 if (ibs_parse_counts(tok1, &ibs_fetch_count) == -1)
462 return -1;
463
464 // Get to um section
465 while (tok1) {
466 if (*tok1 == '\0')
467 return -1;
468 if (*tok1 != ':') {
469 tok1++;
470 } else {
471 tok1++;
472 break;
473 }
474 }
475
476 if (ibs_parse_and_set_um_fetch(tok1) == -1)
477 return -1;
478
479 } else if (!strncmp("op:", tok1, strlen("op:"))) {
480 // Get to event section
481 tok1 = tok1 + strlen("op:");
482 if (ibs_parse_and_set_events(tok1) == -1)
483 return -1;
484
485 // Get to count section
486 while (tok1) {
487 if (*tok1 == '\0')
488 return -1;
489 if (*tok1 != ':') {
490 tok1++;
491 } else {
492 tok1++;
493 break;
494 }
495 }
496
497 if (ibs_parse_counts(tok1, &ibs_op_count) == -1)
498 return -1;
499
500 // Get to um section
501 while (tok1) {
502 if (*tok1 == '\0')
503 return -1;
504 if (*tok1 != ':') {
505 tok1++;
506 } else {
507 tok1++;
508 break;
509 }
510 }
511
512 if (ibs_parse_and_set_um_op(tok1, &ibs_op_um))
513 return -1;
514
515 } else
516 return -1;
517
518 tok1 = strtok_r(NULL, "|", &tok2);
519 }
520
521 /* Initialize ibs_vc */
522 for (i = 0 ; i < ibs_selected_size ; i++)
523 {
524 if (IS_IBS_FETCH(ibs_vc[i].value)) {
525 ibs_vc[i].count = ibs_fetch_count;
526 ibs_vc[i].um = 0;
527 } else {
528 ibs_vc[i].count = ibs_op_count;
529 ibs_vc[i].um = ibs_op_um;
530 }
531 }
532
533 // Allow no event
534 no_event_ok = 1;
535 return 0;
536 }
537
538
ibs_print_stats()539 static int ibs_print_stats()
540 {
541 printf("Nr. IBS Fetch samples : %lu (%lu entries)\n", ibs_fetch_sample_stats, (ibs_fetch_sample_stats * 7));
542 printf("Nr. IBS Fetch incompletes : %lu\n", ibs_fetch_incomplete_stats);
543 printf("Nr. IBS Op samples : %lu (%lu entries)\n", ibs_op_sample_stats, (ibs_op_sample_stats * 13));
544 printf("Nr. IBS Op incompletes : %lu\n", ibs_op_incomplete_stats);
545 printf("Nr. IBS derived events : %lu\n", ibs_derived_event_stats);
546 return 0;
547 }
548
549
ibs_sfile_create(struct sfile * sf)550 static int ibs_sfile_create(struct sfile * sf)
551 {
552 unsigned int i;
553 sf->ext_files = xmalloc(ibs_selected_size * sizeof(odb_t));
554 for (i = 0 ; i < ibs_selected_size ; ++i)
555 odb_init(&sf->ext_files[i]);
556
557 return 0;
558 }
559
560
ibs_sfile_dup(struct sfile * to,struct sfile * from)561 static int ibs_sfile_dup (struct sfile * to, struct sfile * from)
562 {
563 unsigned int i;
564 if (from->ext_files != NULL) {
565 to->ext_files = xmalloc(ibs_selected_size * sizeof(odb_t));
566 for (i = 0 ; i < ibs_selected_size ; ++i)
567 odb_init(&to->ext_files[i]);
568 } else {
569 to->ext_files = NULL;
570 }
571 return 0;
572 }
573
ibs_sfile_close(struct sfile * sf)574 static int ibs_sfile_close(struct sfile * sf)
575 {
576 unsigned int i;
577 if (sf->ext_files != NULL) {
578 for (i = 0; i < ibs_selected_size ; ++i)
579 odb_close(&sf->ext_files[i]);
580
581 free(sf->ext_files);
582 sf->ext_files= NULL;
583 }
584 return 0;
585 }
586
ibs_sfile_sync(struct sfile * sf)587 static int ibs_sfile_sync(struct sfile * sf)
588 {
589 unsigned int i;
590 if (sf->ext_files != NULL) {
591 for (i = 0; i < ibs_selected_size ; ++i)
592 odb_sync(&sf->ext_files[i]);
593 }
594 return 0;
595 }
596
ibs_sfile_get(struct transient const * trans,int is_cg)597 static odb_t * ibs_sfile_get(struct transient const * trans, int is_cg)
598 {
599 struct sfile * sf = trans->current;
600 struct sfile * last = trans->last;
601 struct cg_entry * cg;
602 struct list_head * pos;
603 unsigned long hash;
604 odb_t * file;
605 unsigned long counter, ibs_vci, key;
606
607 /* Note: "trans->event" for IBS is not the same as traditional
608 * events. Here, it has the actual event (0xfxxx), while the
609 * traditional event has the event index.
610 */
611 key = get_ibs_vci_key(trans->event);
612 if (key == ~0UL) {
613 fprintf(stderr, "%s: Invalid IBS event %lu\n", __func__, trans->event);
614 abort();
615 }
616 ibs_vci = ibs_vci_map[key];
617 counter = ibs_vci + OP_MAX_COUNTERS;
618
619 /* Creating IBS sfile if it not already exists */
620 if (sf->ext_files == NULL)
621 ibs_sfile_create(sf);
622
623 file = &(sf->ext_files[ibs_vci]);
624 if (!is_cg)
625 goto open;
626
627 hash = last->hashval & (CG_HASH_SIZE - 1);
628
629 /* Need to look for the right 'to'. Since we're looking for
630 * 'last', we use its hash.
631 */
632 list_for_each(pos, &sf->cg_hash[hash]) {
633 cg = list_entry(pos, struct cg_entry, hash);
634 if (sfile_equal(last, &cg->to)) {
635 file = &(cg->to.ext_files[ibs_vci]);
636 goto open;
637 }
638 }
639
640 cg = xmalloc(sizeof(struct cg_entry));
641 sfile_dup(&cg->to, last);
642 list_add(&cg->hash, &sf->cg_hash[hash]);
643 file = &(cg->to.ext_files[ibs_vci]);
644
645 open:
646 if (!odb_open_count(file))
647 opd_open_sample_file(file, last, sf, counter, is_cg);
648
649 /* Error is logged by opd_open_sample_file */
650 if (!odb_open_count(file))
651 return NULL;
652
653 return file;
654 }
655
656
657 /** Filled opd_event structure with IBS derived event information
658 * from the given counter value.
659 */
ibs_sfile_find_counter_event(unsigned long counter)660 static struct opd_event * ibs_sfile_find_counter_event(unsigned long counter)
661 {
662 unsigned long ibs_vci;
663
664 if (counter >= OP_MAX_COUNTERS + OP_MAX_IBS_COUNTERS
665 || counter < OP_MAX_COUNTERS) {
666 fprintf(stderr,"Error: find_ibs_counter_event : "
667 "invalid counter value %lu.\n", counter);
668 abort();
669 }
670
671 ibs_vci = counter - OP_MAX_COUNTERS;
672 return &ibs_vc[ibs_vci];
673 }
674
675
676 struct opd_ext_sfile_handlers ibs_sfile_handlers =
677 {
678 .create = &ibs_sfile_create,
679 .dup = &ibs_sfile_dup,
680 .close = &ibs_sfile_close,
681 .sync = &ibs_sfile_sync,
682 .get = &ibs_sfile_get,
683 .find_counter_event = &ibs_sfile_find_counter_event
684 };
685
686
687 struct opd_ext_handlers ibs_handlers =
688 {
689 .ext_init = &ibs_init,
690 .ext_print_stats = &ibs_print_stats,
691 .ext_sfile = &ibs_sfile_handlers
692 };
693