Commit | Line | Data |
---|---|---|
bcea3f96 | 1 | // SPDX-License-Identifier: GPL-2.0 |
8ab83f56 SD |
2 | /* |
3 | * Common code for probe-based Dynamic events. | |
4 | * | |
8ab83f56 SD |
5 | * This code was copied from kernel/trace/trace_kprobe.c written by |
6 | * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | |
7 | * | |
8 | * Updates to make this generic: | |
9 | * Copyright (C) IBM Corporation, 2010-2011 | |
10 | * Author: Srikar Dronamraju | |
11 | */ | |
72576341 | 12 | #define pr_fmt(fmt) "trace_probe: " fmt |
8ab83f56 SD |
13 | |
14 | #include "trace_probe.h" | |
15 | ||
16 | const char *reserved_field_names[] = { | |
17 | "common_type", | |
18 | "common_flags", | |
19 | "common_preempt_count", | |
20 | "common_pid", | |
21 | "common_tgid", | |
22 | FIELD_STRING_IP, | |
23 | FIELD_STRING_RETIP, | |
24 | FIELD_STRING_FUNC, | |
25 | }; | |
26 | ||
8ab83f56 | 27 | /* Printing in basic type function template */ |
17ce3dc7 | 28 | #define DEFINE_BASIC_PRINT_TYPE_FUNC(tname, type, fmt) \ |
56de7630 | 29 | int PRINT_TYPE_FUNC_NAME(tname)(struct trace_seq *s, void *data, void *ent)\ |
8ab83f56 | 30 | { \ |
56de7630 | 31 | trace_seq_printf(s, fmt, *(type *)data); \ |
d2b0191a | 32 | return !trace_seq_has_overflowed(s); \ |
8ab83f56 | 33 | } \ |
7bfbc63e | 34 | const char PRINT_TYPE_FMT_NAME(tname)[] = fmt; |
8ab83f56 | 35 | |
bdca79c2 MH |
36 | DEFINE_BASIC_PRINT_TYPE_FUNC(u8, u8, "%u") |
37 | DEFINE_BASIC_PRINT_TYPE_FUNC(u16, u16, "%u") | |
38 | DEFINE_BASIC_PRINT_TYPE_FUNC(u32, u32, "%u") | |
39 | DEFINE_BASIC_PRINT_TYPE_FUNC(u64, u64, "%Lu") | |
17ce3dc7 MH |
40 | DEFINE_BASIC_PRINT_TYPE_FUNC(s8, s8, "%d") |
41 | DEFINE_BASIC_PRINT_TYPE_FUNC(s16, s16, "%d") | |
42 | DEFINE_BASIC_PRINT_TYPE_FUNC(s32, s32, "%d") | |
43 | DEFINE_BASIC_PRINT_TYPE_FUNC(s64, s64, "%Ld") | |
44 | DEFINE_BASIC_PRINT_TYPE_FUNC(x8, u8, "0x%x") | |
45 | DEFINE_BASIC_PRINT_TYPE_FUNC(x16, u16, "0x%x") | |
46 | DEFINE_BASIC_PRINT_TYPE_FUNC(x32, u32, "0x%x") | |
47 | DEFINE_BASIC_PRINT_TYPE_FUNC(x64, u64, "0x%Lx") | |
8ab83f56 | 48 | |
8ab83f56 | 49 | /* Print type function for string type */ |
56de7630 | 50 | int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, void *data, void *ent) |
8ab83f56 SD |
51 | { |
52 | int len = *(u32 *)data >> 16; | |
53 | ||
54 | if (!len) | |
56de7630 | 55 | trace_seq_puts(s, "(fault)"); |
8ab83f56 | 56 | else |
56de7630 | 57 | trace_seq_printf(s, "\"%s\"", |
d2b0191a SRRH |
58 | (const char *)get_loc_data(data, ent)); |
59 | return !trace_seq_has_overflowed(s); | |
8ab83f56 SD |
60 | } |
61 | ||
b26c74e1 | 62 | const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; |
8ab83f56 | 63 | |
f451bc89 MH |
64 | /* Fetch type information table */ |
65 | static const struct fetch_type probe_fetch_types[] = { | |
66 | /* Special types */ | |
67 | __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, | |
68 | "__data_loc char[]"), | |
69 | /* Basic types */ | |
70 | ASSIGN_FETCH_TYPE(u8, u8, 0), | |
71 | ASSIGN_FETCH_TYPE(u16, u16, 0), | |
72 | ASSIGN_FETCH_TYPE(u32, u32, 0), | |
73 | ASSIGN_FETCH_TYPE(u64, u64, 0), | |
74 | ASSIGN_FETCH_TYPE(s8, u8, 1), | |
75 | ASSIGN_FETCH_TYPE(s16, u16, 1), | |
76 | ASSIGN_FETCH_TYPE(s32, u32, 1), | |
77 | ASSIGN_FETCH_TYPE(s64, u64, 1), | |
78 | ASSIGN_FETCH_TYPE_ALIAS(x8, u8, u8, 0), | |
79 | ASSIGN_FETCH_TYPE_ALIAS(x16, u16, u16, 0), | |
80 | ASSIGN_FETCH_TYPE_ALIAS(x32, u32, u32, 0), | |
81 | ASSIGN_FETCH_TYPE_ALIAS(x64, u64, u64, 0), | |
82 | ||
83 | ASSIGN_FETCH_TYPE_END | |
84 | }; | |
85 | ||
86 | static const struct fetch_type *find_fetch_type(const char *type) | |
8ab83f56 SD |
87 | { |
88 | int i; | |
89 | ||
90 | if (!type) | |
91 | type = DEFAULT_FETCH_TYPE_STR; | |
92 | ||
93 | /* Special case: bitfield */ | |
94 | if (*type == 'b') { | |
95 | unsigned long bs; | |
96 | ||
97 | type = strchr(type, '/'); | |
98 | if (!type) | |
99 | goto fail; | |
100 | ||
101 | type++; | |
bcd83ea6 | 102 | if (kstrtoul(type, 0, &bs)) |
8ab83f56 SD |
103 | goto fail; |
104 | ||
105 | switch (bs) { | |
106 | case 8: | |
f451bc89 | 107 | return find_fetch_type("u8"); |
8ab83f56 | 108 | case 16: |
f451bc89 | 109 | return find_fetch_type("u16"); |
8ab83f56 | 110 | case 32: |
f451bc89 | 111 | return find_fetch_type("u32"); |
8ab83f56 | 112 | case 64: |
f451bc89 | 113 | return find_fetch_type("u64"); |
8ab83f56 SD |
114 | default: |
115 | goto fail; | |
116 | } | |
117 | } | |
118 | ||
f451bc89 MH |
119 | for (i = 0; probe_fetch_types[i].name; i++) { |
120 | if (strcmp(type, probe_fetch_types[i].name) == 0) | |
121 | return &probe_fetch_types[i]; | |
34fee3a1 | 122 | } |
8ab83f56 SD |
123 | |
124 | fail: | |
125 | return NULL; | |
126 | } | |
127 | ||
8ab83f56 | 128 | /* Split symbol and offset. */ |
c5d343b6 | 129 | int traceprobe_split_symbol_offset(char *symbol, long *offset) |
8ab83f56 SD |
130 | { |
131 | char *tmp; | |
132 | int ret; | |
133 | ||
134 | if (!offset) | |
135 | return -EINVAL; | |
136 | ||
c5d343b6 | 137 | tmp = strpbrk(symbol, "+-"); |
8ab83f56 | 138 | if (tmp) { |
c5d343b6 | 139 | ret = kstrtol(tmp, 0, offset); |
8ab83f56 SD |
140 | if (ret) |
141 | return ret; | |
8ab83f56 SD |
142 | *tmp = '\0'; |
143 | } else | |
144 | *offset = 0; | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) | |
150 | ||
151 | static int parse_probe_vars(char *arg, const struct fetch_type *t, | |
53305928 | 152 | struct fetch_insn *code, bool is_return, |
b079d374 | 153 | bool is_kprobe) |
8ab83f56 SD |
154 | { |
155 | int ret = 0; | |
156 | unsigned long param; | |
157 | ||
158 | if (strcmp(arg, "retval") == 0) { | |
159 | if (is_return) | |
53305928 | 160 | code->op = FETCH_OP_RETVAL; |
8ab83f56 SD |
161 | else |
162 | ret = -EINVAL; | |
163 | } else if (strncmp(arg, "stack", 5) == 0) { | |
164 | if (arg[5] == '\0') { | |
53305928 | 165 | code->op = FETCH_OP_STACKP; |
8ab83f56 | 166 | } else if (isdigit(arg[5])) { |
bcd83ea6 | 167 | ret = kstrtoul(arg + 5, 10, ¶m); |
b079d374 | 168 | if (ret || (is_kprobe && param > PARAM_MAX_STACK)) |
8ab83f56 SD |
169 | ret = -EINVAL; |
170 | else { | |
53305928 MH |
171 | code->op = FETCH_OP_STACK; |
172 | code->param = (unsigned int)param; | |
8ab83f56 SD |
173 | } |
174 | } else | |
175 | ret = -EINVAL; | |
35abb67d | 176 | } else if (strcmp(arg, "comm") == 0) { |
53305928 | 177 | code->op = FETCH_OP_COMM; |
8ab83f56 SD |
178 | } else |
179 | ret = -EINVAL; | |
180 | ||
181 | return ret; | |
182 | } | |
183 | ||
184 | /* Recursive argument parser */ | |
53305928 MH |
185 | static int |
186 | parse_probe_arg(char *arg, const struct fetch_type *type, | |
187 | struct fetch_insn **pcode, struct fetch_insn *end, | |
f451bc89 | 188 | bool is_return, bool is_kprobe) |
8ab83f56 | 189 | { |
53305928 | 190 | struct fetch_insn *code = *pcode; |
8ab83f56 SD |
191 | unsigned long param; |
192 | long offset; | |
193 | char *tmp; | |
34fee3a1 | 194 | int ret = 0; |
8ab83f56 | 195 | |
8ab83f56 SD |
196 | switch (arg[0]) { |
197 | case '$': | |
53305928 MH |
198 | ret = parse_probe_vars(arg + 1, type, code, |
199 | is_return, is_kprobe); | |
8ab83f56 SD |
200 | break; |
201 | ||
202 | case '%': /* named register */ | |
203 | ret = regs_query_register_offset(arg + 1); | |
204 | if (ret >= 0) { | |
53305928 MH |
205 | code->op = FETCH_OP_REG; |
206 | code->param = (unsigned int)ret; | |
8ab83f56 SD |
207 | ret = 0; |
208 | } | |
209 | break; | |
210 | ||
b7e0bf34 | 211 | case '@': /* memory, file-offset or symbol */ |
8ab83f56 | 212 | if (isdigit(arg[1])) { |
bcd83ea6 | 213 | ret = kstrtoul(arg + 1, 0, ¶m); |
8ab83f56 SD |
214 | if (ret) |
215 | break; | |
53305928 MH |
216 | /* load address */ |
217 | code->op = FETCH_OP_IMM; | |
218 | code->immediate = param; | |
b7e0bf34 NK |
219 | } else if (arg[1] == '+') { |
220 | /* kprobes don't support file offsets */ | |
221 | if (is_kprobe) | |
222 | return -EINVAL; | |
223 | ||
224 | ret = kstrtol(arg + 2, 0, &offset); | |
225 | if (ret) | |
226 | break; | |
227 | ||
53305928 MH |
228 | code->op = FETCH_OP_FOFFS; |
229 | code->immediate = (unsigned long)offset; // imm64? | |
8ab83f56 | 230 | } else { |
b079d374 NK |
231 | /* uprobes don't support symbols */ |
232 | if (!is_kprobe) | |
233 | return -EINVAL; | |
234 | ||
8ab83f56 SD |
235 | ret = traceprobe_split_symbol_offset(arg + 1, &offset); |
236 | if (ret) | |
237 | break; | |
238 | ||
53305928 MH |
239 | code->op = FETCH_OP_IMM; |
240 | code->immediate = | |
241 | (unsigned long)kallsyms_lookup_name(arg + 1); | |
242 | if (!code->immediate) | |
243 | return -ENOENT; | |
244 | code->immediate += offset; | |
8ab83f56 | 245 | } |
53305928 MH |
246 | /* These are fetching from memory */ |
247 | if (++code == end) | |
248 | return -E2BIG; | |
249 | *pcode = code; | |
250 | code->op = FETCH_OP_DEREF; | |
251 | code->offset = offset; | |
8ab83f56 SD |
252 | break; |
253 | ||
254 | case '+': /* deref memory */ | |
bcd83ea6 | 255 | arg++; /* Skip '+', because kstrtol() rejects it. */ |
8ab83f56 SD |
256 | case '-': |
257 | tmp = strchr(arg, '('); | |
258 | if (!tmp) | |
53305928 | 259 | return -EINVAL; |
8ab83f56 SD |
260 | |
261 | *tmp = '\0'; | |
bcd83ea6 | 262 | ret = kstrtol(arg, 0, &offset); |
8ab83f56 SD |
263 | if (ret) |
264 | break; | |
265 | ||
266 | arg = tmp + 1; | |
267 | tmp = strrchr(arg, ')'); | |
268 | ||
269 | if (tmp) { | |
f451bc89 | 270 | const struct fetch_type *t2 = find_fetch_type(NULL); |
8ab83f56 | 271 | |
8ab83f56 | 272 | *tmp = '\0'; |
53305928 | 273 | ret = parse_probe_arg(arg, t2, &code, end, is_return, |
f451bc89 | 274 | is_kprobe); |
8ab83f56 | 275 | if (ret) |
53305928 MH |
276 | break; |
277 | if (code->op == FETCH_OP_COMM) | |
278 | return -EINVAL; | |
279 | if (++code == end) | |
280 | return -E2BIG; | |
281 | *pcode = code; | |
282 | ||
283 | code->op = FETCH_OP_DEREF; | |
284 | code->offset = offset; | |
8ab83f56 SD |
285 | } |
286 | break; | |
287 | } | |
53305928 MH |
288 | if (!ret && code->op == FETCH_OP_NOP) { |
289 | /* Parsed, but do not find fetch method */ | |
8ab83f56 SD |
290 | ret = -EINVAL; |
291 | } | |
8ab83f56 SD |
292 | return ret; |
293 | } | |
294 | ||
295 | #define BYTES_TO_BITS(nb) ((BITS_PER_LONG * (nb)) / sizeof(long)) | |
296 | ||
297 | /* Bitfield type needs to be parsed into a fetch function */ | |
298 | static int __parse_bitfield_probe_arg(const char *bf, | |
299 | const struct fetch_type *t, | |
53305928 | 300 | struct fetch_insn **pcode) |
8ab83f56 | 301 | { |
53305928 | 302 | struct fetch_insn *code = *pcode; |
8ab83f56 SD |
303 | unsigned long bw, bo; |
304 | char *tail; | |
305 | ||
306 | if (*bf != 'b') | |
307 | return 0; | |
308 | ||
8ab83f56 SD |
309 | bw = simple_strtoul(bf + 1, &tail, 0); /* Use simple one */ |
310 | ||
311 | if (bw == 0 || *tail != '@') | |
312 | return -EINVAL; | |
313 | ||
314 | bf = tail + 1; | |
315 | bo = simple_strtoul(bf, &tail, 0); | |
316 | ||
317 | if (tail == bf || *tail != '/') | |
318 | return -EINVAL; | |
53305928 MH |
319 | code++; |
320 | if (code->op != FETCH_OP_NOP) | |
321 | return -E2BIG; | |
322 | *pcode = code; | |
8ab83f56 | 323 | |
53305928 MH |
324 | code->op = FETCH_OP_MOD_BF; |
325 | code->lshift = BYTES_TO_BITS(t->size) - (bw + bo); | |
326 | code->rshift = BYTES_TO_BITS(t->size) - bw; | |
327 | code->basesize = t->size; | |
8ab83f56 SD |
328 | |
329 | return (BYTES_TO_BITS(t->size) < (bw + bo)) ? -EINVAL : 0; | |
330 | } | |
331 | ||
332 | /* String length checking wrapper */ | |
333 | int traceprobe_parse_probe_arg(char *arg, ssize_t *size, | |
f451bc89 | 334 | struct probe_arg *parg, bool is_return, bool is_kprobe) |
8ab83f56 | 335 | { |
53305928 | 336 | struct fetch_insn *code, *tmp = NULL; |
8ab83f56 SD |
337 | const char *t; |
338 | int ret; | |
339 | ||
340 | if (strlen(arg) > MAX_ARGSTR_LEN) { | |
341 | pr_info("Argument is too long.: %s\n", arg); | |
342 | return -ENOSPC; | |
343 | } | |
344 | parg->comm = kstrdup(arg, GFP_KERNEL); | |
345 | if (!parg->comm) { | |
346 | pr_info("Failed to allocate memory for command '%s'.\n", arg); | |
347 | return -ENOMEM; | |
348 | } | |
349 | t = strchr(parg->comm, ':'); | |
350 | if (t) { | |
351 | arg[t - parg->comm] = '\0'; | |
352 | t++; | |
353 | } | |
35abb67d OS |
354 | /* |
355 | * The default type of $comm should be "string", and it can't be | |
356 | * dereferenced. | |
357 | */ | |
358 | if (!t && strcmp(arg, "$comm") == 0) | |
359 | t = "string"; | |
f451bc89 | 360 | parg->type = find_fetch_type(t); |
8ab83f56 SD |
361 | if (!parg->type) { |
362 | pr_info("Unsupported type: %s\n", t); | |
363 | return -EINVAL; | |
364 | } | |
365 | parg->offset = *size; | |
366 | *size += parg->type->size; | |
8ab83f56 | 367 | |
53305928 MH |
368 | code = tmp = kzalloc(sizeof(*code) * FETCH_INSN_MAX, GFP_KERNEL); |
369 | if (!code) | |
370 | return -ENOMEM; | |
371 | code[FETCH_INSN_MAX - 1].op = FETCH_OP_END; | |
372 | ||
373 | ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1], | |
f451bc89 | 374 | is_return, is_kprobe); |
53305928 MH |
375 | if (ret) |
376 | goto fail; | |
377 | ||
378 | /* Store operation */ | |
379 | if (!strcmp(parg->type->name, "string")) { | |
380 | if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_IMM && | |
381 | code->op != FETCH_OP_COMM) { | |
382 | pr_info("string only accepts memory or address.\n"); | |
383 | ret = -EINVAL; | |
384 | goto fail; | |
385 | } | |
386 | /* Since IMM or COMM must be the 1st insn, this is safe */ | |
387 | if (code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM) | |
388 | code++; | |
389 | code->op = FETCH_OP_ST_STRING; /* In DEREF case, replace it */ | |
390 | parg->dynamic = true; | |
391 | } else if (code->op == FETCH_OP_DEREF) { | |
392 | code->op = FETCH_OP_ST_MEM; | |
393 | code->size = parg->type->size; | |
394 | } else { | |
395 | code++; | |
396 | if (code->op != FETCH_OP_NOP) { | |
397 | ret = -E2BIG; | |
398 | goto fail; | |
399 | } | |
400 | code->op = FETCH_OP_ST_RAW; | |
401 | code->size = parg->type->size; | |
402 | } | |
403 | /* Modify operation */ | |
404 | if (t != NULL) { | |
405 | ret = __parse_bitfield_probe_arg(t, parg->type, &code); | |
406 | if (ret) | |
407 | goto fail; | |
8ab83f56 | 408 | } |
53305928 MH |
409 | code++; |
410 | code->op = FETCH_OP_END; | |
411 | ||
412 | /* Shrink down the code buffer */ | |
413 | parg->code = kzalloc(sizeof(*code) * (code - tmp + 1), GFP_KERNEL); | |
414 | if (!parg->code) | |
415 | ret = -ENOMEM; | |
416 | else | |
417 | memcpy(parg->code, tmp, sizeof(*code) * (code - tmp + 1)); | |
418 | ||
419 | fail: | |
420 | kfree(tmp); | |
8ab83f56 SD |
421 | |
422 | return ret; | |
423 | } | |
424 | ||
425 | /* Return 1 if name is reserved or already used by another argument */ | |
426 | int traceprobe_conflict_field_name(const char *name, | |
427 | struct probe_arg *args, int narg) | |
428 | { | |
429 | int i; | |
430 | ||
431 | for (i = 0; i < ARRAY_SIZE(reserved_field_names); i++) | |
432 | if (strcmp(reserved_field_names[i], name) == 0) | |
433 | return 1; | |
434 | ||
435 | for (i = 0; i < narg; i++) | |
436 | if (strcmp(args[i].name, name) == 0) | |
437 | return 1; | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
8ab83f56 SD |
442 | void traceprobe_free_probe_arg(struct probe_arg *arg) |
443 | { | |
53305928 | 444 | kfree(arg->code); |
8ab83f56 SD |
445 | kfree(arg->name); |
446 | kfree(arg->comm); | |
447 | } | |
448 | ||
5bf652aa NK |
449 | static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, |
450 | bool is_return) | |
451 | { | |
452 | int i; | |
453 | int pos = 0; | |
454 | ||
455 | const char *fmt, *arg; | |
456 | ||
457 | if (!is_return) { | |
458 | fmt = "(%lx)"; | |
459 | arg = "REC->" FIELD_STRING_IP; | |
460 | } else { | |
461 | fmt = "(%lx <- %lx)"; | |
462 | arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP; | |
463 | } | |
464 | ||
465 | /* When len=0, we just calculate the needed length */ | |
466 | #define LEN_OR_ZERO (len ? len - pos : 0) | |
467 | ||
468 | pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt); | |
469 | ||
470 | for (i = 0; i < tp->nr_args; i++) { | |
471 | pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s", | |
472 | tp->args[i].name, tp->args[i].type->fmt); | |
473 | } | |
474 | ||
475 | pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg); | |
476 | ||
477 | for (i = 0; i < tp->nr_args; i++) { | |
478 | if (strcmp(tp->args[i].type->name, "string") == 0) | |
479 | pos += snprintf(buf + pos, LEN_OR_ZERO, | |
480 | ", __get_str(%s)", | |
481 | tp->args[i].name); | |
482 | else | |
483 | pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s", | |
484 | tp->args[i].name); | |
485 | } | |
486 | ||
487 | #undef LEN_OR_ZERO | |
488 | ||
489 | /* return the length of print_fmt */ | |
490 | return pos; | |
491 | } | |
492 | ||
493 | int set_print_fmt(struct trace_probe *tp, bool is_return) | |
494 | { | |
495 | int len; | |
496 | char *print_fmt; | |
497 | ||
498 | /* First: called with 0 length to calculate the needed length */ | |
499 | len = __set_print_fmt(tp, NULL, 0, is_return); | |
500 | print_fmt = kmalloc(len + 1, GFP_KERNEL); | |
501 | if (!print_fmt) | |
502 | return -ENOMEM; | |
503 | ||
504 | /* Second: actually write the @print_fmt */ | |
505 | __set_print_fmt(tp, print_fmt, len + 1, is_return); | |
506 | tp->call.print_fmt = print_fmt; | |
507 | ||
508 | return 0; | |
509 | } | |
eeb07b06 MH |
510 | |
511 | int traceprobe_define_arg_fields(struct trace_event_call *event_call, | |
512 | size_t offset, struct trace_probe *tp) | |
513 | { | |
514 | int ret, i; | |
515 | ||
516 | /* Set argument names as fields */ | |
517 | for (i = 0; i < tp->nr_args; i++) { | |
518 | struct probe_arg *parg = &tp->args[i]; | |
519 | ||
520 | ret = trace_define_field(event_call, parg->type->fmttype, | |
521 | parg->name, | |
522 | offset + parg->offset, | |
523 | parg->type->size, | |
524 | parg->type->is_signed, | |
525 | FILTER_OTHER); | |
526 | if (ret) | |
527 | return ret; | |
528 | } | |
529 | return 0; | |
530 | } |