Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'tools-ynl-gen-dust-off-the-user-space-code'

Jakub Kicinski says:

====================
tools: ynl-gen: dust off the user space code

Every now and then I wish I finished the user space part of
the netlink specs, Python scripts kind of stole the show but
C is useful for selftests and stuff which needs to be fast.
Recently someone asked me how to access devlink and ethtool
from C++ which pushed me over the edge.

Fix things which bit rotted and finish notification handling.
This series contains code gen changes only. I'll follow up
with the fixed component, samples and docs as soon as it's
merged.
====================

Link: https://lore.kernel.org/r/20230602023548.463441-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+199 -54
+199 -54
tools/net/ynl/ynl-gen-c.py
··· 170 170 for line in lines: 171 171 ri.cw.p(line) 172 172 ri.cw.block_end() 173 + return True 173 174 174 175 def _setter_lines(self, ri, member, presence): 175 176 raise Exception(f"Setter not implemented for class type {self.type}") ··· 198 197 def presence_type(self): 199 198 return '' 200 199 200 + def arg_member(self, ri): 201 + return [] 202 + 203 + def _attr_get(self, ri, var): 204 + return ['return MNL_CB_ERROR;'], None, None 205 + 201 206 def _attr_typol(self): 202 207 return '.type = YNL_PT_REJECT, ' 203 208 ··· 215 208 def presence_type(self): 216 209 return '' 217 210 211 + def arg_member(self, ri): 212 + return [] 213 + 218 214 def _attr_typol(self): 219 - return '.type = YNL_PT_REJECT, ' 215 + return '.type = YNL_PT_IGNORE, ' 216 + 217 + def attr_get(self, ri, var, first): 218 + pass 220 219 221 220 def attr_policy(self, cw): 222 221 pass ··· 424 411 f"{self.enum_name}, &{var}->{self.c_name})") 425 412 426 413 def _attr_get(self, ri, var): 427 - get_lines = [f"{self.nested_render_name}_parse(&parg, attr);"] 414 + get_lines = [f"if ({self.nested_render_name}_parse(&parg, attr))", 415 + "return MNL_CB_ERROR;"] 428 416 init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;", 429 417 f"parg.data = &{var}->{self.c_name};"] 430 418 return get_lines, init_lines, None ··· 826 812 inherit = set() 827 813 nested = spec['nested-attributes'] 828 814 if nested not in self.root_sets: 829 - self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit) 815 + if nested not in self.pure_nested_structs: 816 + self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit) 830 817 if attr in rs_members['request']: 831 818 self.pure_nested_structs[nested].request = True 832 819 if attr in rs_members['reply']: ··· 887 872 self.hooks[when][op_mode]['set'].add(name) 888 873 self.hooks[when][op_mode]['list'].append(name) 889 874 875 + def has_notifications(self): 876 + for op in self.ops.values(): 877 + if 'notify' in op or 'event' in op: 878 + return True 879 + return False 880 + 890 881 891 882 class RenderInfo: 892 883 def __init__(self, cw, family, ku_space, op, op_name, op_mode, attr_set=None): ··· 904 883 self.op_mode = op_mode 905 884 906 885 # 'do' and 'dump' response parsing is identical 907 - if op_mode != 'do' and 'dump' in op and 'do' in op and 'reply' in op['do'] and \ 908 - op["do"]["reply"] == op["dump"]["reply"]: 909 - self.type_consistent = True 910 - else: 911 - self.type_consistent = op_mode == 'event' 886 + self.type_consistent = True 887 + if op_mode != 'do' and 'dump' in op and 'do' in op: 888 + if ('reply' in op['do']) != ('reply' in op["dump"]): 889 + self.type_consistent = False 890 + elif 'reply' in op['do'] and op["do"]["reply"] != op["dump"]["reply"]: 891 + self.type_consistent = False 912 892 913 893 self.attr_set = attr_set 914 894 if not self.attr_set: ··· 1174 1152 cw.nl() 1175 1153 1176 1154 1155 + def put_op_name_fwd(family, cw): 1156 + cw.write_func_prot('const char *', f'{family.name}_op_str', ['int op'], suffix=';') 1157 + 1158 + 1159 + def put_op_name(family, cw): 1160 + map_name = f'{family.name}_op_strmap' 1161 + cw.block_start(line=f"static const char * const {map_name}[] =") 1162 + for op_name, op in family.msgs.items(): 1163 + cw.p(f'[{op.enum_name}] = "{op_name}",') 1164 + cw.block_end(line=';') 1165 + cw.nl() 1166 + 1167 + cw.write_func_prot('const char *', f'{family.name}_op_str', ['int op']) 1168 + cw.block_start() 1169 + cw.p(f'if (op < 0 || op >= (int)MNL_ARRAY_SIZE({map_name}))') 1170 + cw.p('return NULL;') 1171 + cw.p(f'return {map_name}[op];') 1172 + cw.block_end() 1173 + cw.nl() 1174 + 1175 + 1176 + def put_enum_to_str_fwd(family, cw, enum): 1177 + args = [f'enum {enum.render_name} value'] 1178 + if 'enum-name' in enum and not enum['enum-name']: 1179 + args = ['int value'] 1180 + cw.write_func_prot('const char *', f'{enum.render_name}_str', args, suffix=';') 1181 + 1182 + 1183 + def put_enum_to_str(family, cw, enum): 1184 + map_name = f'{enum.render_name}_strmap' 1185 + cw.block_start(line=f"static const char * const {map_name}[] =") 1186 + for entry in enum.entries.values(): 1187 + cw.p(f'[{entry.value}] = "{entry.name}",') 1188 + cw.block_end(line=';') 1189 + cw.nl() 1190 + 1191 + args = [f'enum {enum.render_name} value'] 1192 + if 'enum-name' in enum and not enum['enum-name']: 1193 + args = ['int value'] 1194 + cw.write_func_prot('const char *', f'{enum.render_name}_str', args) 1195 + cw.block_start() 1196 + if enum.type == 'flags': 1197 + cw.p('value = ffs(value) - 1;') 1198 + cw.p(f'if (value < 0 || value >= (int)MNL_ARRAY_SIZE({map_name}))') 1199 + cw.p('return NULL;') 1200 + cw.p(f'return {map_name}[value];') 1201 + cw.block_end() 1202 + cw.nl() 1203 + 1204 + 1177 1205 def put_req_nested(ri, struct): 1178 1206 func_args = ['struct nlmsghdr *nlh', 1179 1207 'unsigned int attr_type', ··· 1283 1211 1284 1212 first = True 1285 1213 for _, arg in struct.member_list(): 1286 - arg.attr_get(ri, 'dst', first=first) 1287 - first = False 1214 + good = arg.attr_get(ri, 'dst', first=first) 1215 + # First may be 'unused' or 'pad', ignore those 1216 + first &= not good 1288 1217 1289 1218 ri.cw.block_end() 1290 1219 ri.cw.nl() ··· 1377 1304 ret_err = '-1' 1378 1305 direction = "request" 1379 1306 local_vars = ['struct nlmsghdr *nlh;', 1380 - 'int len, err;'] 1307 + 'int err;'] 1381 1308 1382 1309 if 'reply' in ri.op[ri.op_mode]: 1383 1310 ret_ok = 'rsp' 1384 1311 ret_err = 'NULL' 1385 1312 local_vars += [f'{type_name(ri, rdir(direction))} *rsp;', 1386 - 'struct ynl_parse_arg yarg = { .ys = ys, };'] 1313 + 'struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };'] 1387 1314 1388 1315 print_prototype(ri, direction, terminate=False) 1389 1316 ri.cw.block_start() ··· 1393 1320 1394 1321 ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;") 1395 1322 if 'reply' in ri.op[ri.op_mode]: 1396 - ri.cw.p(f"yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;") 1323 + ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;") 1397 1324 ri.cw.nl() 1398 1325 for _, attr in ri.struct["request"].member_list(): 1399 1326 attr.attr_put(ri, "req") 1400 1327 ri.cw.nl() 1401 1328 1402 - ri.cw.p('err = mnl_socket_sendto(ys->sock, nlh, nlh->nlmsg_len);') 1403 - ri.cw.p('if (err < 0)') 1404 - ri.cw.p(f"return {ret_err};") 1405 - ri.cw.nl() 1406 - ri.cw.p('len = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);') 1407 - ri.cw.p('if (len < 0)') 1408 - ri.cw.p(f"return {ret_err};") 1409 - ri.cw.nl() 1410 - 1329 + parse_arg = "NULL" 1411 1330 if 'reply' in ri.op[ri.op_mode]: 1412 1331 ri.cw.p('rsp = calloc(1, sizeof(*rsp));') 1413 - ri.cw.p('yarg.data = rsp;') 1332 + ri.cw.p('yrs.yarg.data = rsp;') 1333 + ri.cw.p(f"yrs.cb = {op_prefix(ri, 'reply')}_parse;") 1334 + if ri.op.value is not None: 1335 + ri.cw.p(f'yrs.rsp_cmd = {ri.op.enum_name};') 1336 + else: 1337 + ri.cw.p(f'yrs.rsp_cmd = {ri.op.rsp_value};') 1414 1338 ri.cw.nl() 1415 - ri.cw.p(f"err = {ri.nl.parse_cb_run(op_prefix(ri, 'reply') + '_parse', '&yarg', False)};") 1416 - ri.cw.p('if (err < 0)') 1339 + parse_arg = '&yrs' 1340 + ri.cw.p(f"err = ynl_exec(ys, nlh, {parse_arg});") 1341 + ri.cw.p('if (err < 0)') 1342 + if 'reply' in ri.op[ri.op_mode]: 1417 1343 ri.cw.p('goto err_free;') 1418 - ri.cw.nl() 1419 - 1420 - ri.cw.p('err = ynl_recv_ack(ys, err);') 1421 - ri.cw.p('if (err)') 1422 - ri.cw.p('goto err_free;') 1344 + else: 1345 + ri.cw.p('return -1;') 1423 1346 ri.cw.nl() 1347 + 1424 1348 ri.cw.p(f"return {ret_ok};") 1425 1349 ri.cw.nl() 1426 - ri.cw.p('err_free:') 1427 1350 1428 1351 if 'reply' in ri.op[ri.op_mode]: 1352 + ri.cw.p('err_free:') 1429 1353 ri.cw.p(f"{call_free(ri, rdir(direction), 'rsp')}") 1430 - ri.cw.p(f"return {ret_err};") 1354 + ri.cw.p(f"return {ret_err};") 1355 + 1431 1356 ri.cw.block_end() 1432 1357 1433 1358 ··· 1435 1364 ri.cw.block_start() 1436 1365 local_vars = ['struct ynl_dump_state yds = {};', 1437 1366 'struct nlmsghdr *nlh;', 1438 - 'int len, err;'] 1367 + 'int err;'] 1439 1368 1440 1369 for var in local_vars: 1441 1370 ri.cw.p(f'{var}') ··· 1444 1373 ri.cw.p('yds.ys = ys;') 1445 1374 ri.cw.p(f"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});") 1446 1375 ri.cw.p(f"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;") 1376 + if ri.op.value is not None: 1377 + ri.cw.p(f'yds.rsp_cmd = {ri.op.enum_name};') 1378 + else: 1379 + ri.cw.p(f'yds.rsp_cmd = {ri.op.rsp_value};') 1447 1380 ri.cw.p(f"yds.rsp_policy = &{ri.struct['reply'].render_name}_nest;") 1448 1381 ri.cw.nl() 1449 1382 ri.cw.p(f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);") ··· 1459 1384 attr.attr_put(ri, "req") 1460 1385 ri.cw.nl() 1461 1386 1462 - ri.cw.p('err = mnl_socket_sendto(ys->sock, nlh, nlh->nlmsg_len);') 1463 - ri.cw.p('if (err < 0)') 1464 - ri.cw.p('return NULL;') 1465 - ri.cw.nl() 1466 - 1467 - ri.cw.block_start(line='do') 1468 - ri.cw.p('len = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);') 1469 - ri.cw.p('if (len < 0)') 1470 - ri.cw.p('goto free_list;') 1471 - ri.cw.nl() 1472 - ri.cw.p(f"err = {ri.nl.parse_cb_run('ynl_dump_trampoline', '&yds', False, indent=2)};") 1387 + ri.cw.p('err = ynl_exec_dump(ys, nlh, &yds);') 1473 1388 ri.cw.p('if (err < 0)') 1474 1389 ri.cw.p('goto free_list;') 1475 - ri.cw.block_end(line='while (err > 0);') 1476 1390 ri.cw.nl() 1477 1391 1478 1392 ri.cw.p('return yds.first;') ··· 1480 1416 if direction: 1481 1417 return direction_to_suffix[direction][1:] 1482 1418 return 'obj' 1419 + 1420 + 1421 + def print_alloc_wrapper(ri, direction): 1422 + name = op_prefix(ri, direction) 1423 + ri.cw.write_func_prot(f'static inline struct {name} *', f"{name}_alloc", [f"void"]) 1424 + ri.cw.block_start() 1425 + ri.cw.p(f'return calloc(1, sizeof(struct {name}));') 1426 + ri.cw.block_end() 1483 1427 1484 1428 1485 1429 def print_free_prototype(ri, direction, suffix=';'): ··· 1537 1465 1538 1466 def print_type_helpers(ri, direction, deref=False): 1539 1467 print_free_prototype(ri, direction) 1468 + ri.cw.nl() 1540 1469 1541 1470 if ri.ku_space == 'user' and direction == 'request': 1542 1471 for _, attr in ri.struct[direction].member_list(): ··· 1546 1473 1547 1474 1548 1475 def print_req_type_helpers(ri): 1476 + print_alloc_wrapper(ri, "request") 1549 1477 print_type_helpers(ri, "request") 1550 1478 1551 1479 ··· 1570 1496 print_type(ri, "request") 1571 1497 1572 1498 1499 + def print_req_free(ri): 1500 + if 'request' not in ri.op[ri.op_mode]: 1501 + return 1502 + _free_type(ri, 'request', ri.struct['request']) 1503 + 1504 + 1573 1505 def print_rsp_type(ri): 1574 1506 if (ri.op_mode == 'do' or ri.op_mode == 'dump') and 'reply' in ri.op[ri.op_mode]: 1575 1507 direction = 'reply' ··· 1593 1513 elif ri.op_mode == 'notify' or ri.op_mode == 'event': 1594 1514 ri.cw.p('__u16 family;') 1595 1515 ri.cw.p('__u8 cmd;') 1516 + ri.cw.p('struct ynl_ntf_base_type *next;') 1596 1517 ri.cw.p(f"void (*free)({type_name(ri, 'reply')} *ntf);") 1597 1518 ri.cw.p(f"{type_name(ri, 'reply', deref=True)} obj __attribute__ ((aligned (8)));") 1598 1519 ri.cw.block_end(line=';') ··· 1645 1564 ri.cw.block_start() 1646 1565 ri.cw.p(f"{sub_type} *next = rsp;") 1647 1566 ri.cw.nl() 1648 - ri.cw.block_start(line='while (next)') 1567 + ri.cw.block_start(line='while ((void *)next != YNL_LIST_END)') 1649 1568 _free_type_members_iter(ri, ri.struct['reply']) 1650 1569 ri.cw.p('rsp = next;') 1651 1570 ri.cw.p('next = rsp->next;') ··· 2116 2035 cw.p(f'#endif /* {hdr_prot} */') 2117 2036 2118 2037 2038 + def _render_user_ntf_entry(ri, op): 2039 + ri.cw.block_start(line=f"[{op.enum_name}] = ") 2040 + ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),") 2041 + ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,") 2042 + ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,") 2043 + ri.cw.p(f".free\t\t= (void *){op_prefix(ri, 'notify')}_free,") 2044 + ri.cw.block_end(line=',') 2045 + 2046 + 2047 + def render_user_family(family, cw, prototype): 2048 + symbol = f'const struct ynl_family ynl_{family.c_name}_family' 2049 + if prototype: 2050 + cw.p(f'extern {symbol};') 2051 + return 2052 + 2053 + ntf = family.has_notifications() 2054 + if ntf: 2055 + cw.block_start(line=f"static const struct ynl_ntf_info {family['name']}_ntf_info[] = ") 2056 + for ntf_op in sorted(family.all_notify.keys()): 2057 + op = family.ops[ntf_op] 2058 + ri = RenderInfo(cw, family, "user", op, ntf_op, "notify") 2059 + for ntf in op['notify']['cmds']: 2060 + _render_user_ntf_entry(ri, ntf) 2061 + for op_name, op in family.ops.items(): 2062 + if 'event' not in op: 2063 + continue 2064 + ri = RenderInfo(cw, family, "user", op, op_name, "event") 2065 + _render_user_ntf_entry(ri, op) 2066 + cw.block_end(line=";") 2067 + cw.nl() 2068 + 2069 + cw.block_start(f'{symbol} = ') 2070 + cw.p(f'.name\t\t= "{family.name}",') 2071 + if ntf: 2072 + cw.p(f".ntf_info\t= {family['name']}_ntf_info,") 2073 + cw.p(f".ntf_info_size\t= MNL_ARRAY_SIZE({family['name']}_ntf_info),") 2074 + cw.block_end(line=';') 2075 + 2076 + 2119 2077 def find_kernel_root(full_path): 2120 2078 sub_path = '' 2121 2079 while True: ··· 2223 2103 cw.nl() 2224 2104 headers = ['uapi/' + parsed.uapi_header] 2225 2105 else: 2106 + cw.p('#include <stdlib.h>') 2107 + if args.header: 2108 + cw.p('#include <string.h>') 2109 + cw.p('#include <linux/types.h>') 2110 + else: 2111 + cw.p(f'#include "{parsed.name}-user.h"') 2112 + cw.p('#include "ynl.h"') 2226 2113 headers = [parsed.uapi_header] 2227 2114 for definition in parsed['definitions']: 2228 2115 if 'header' in definition: ··· 2250 2123 cw.p(f'#include "{one}"') 2251 2124 else: 2252 2125 cw.p('struct ynl_sock;') 2126 + cw.nl() 2127 + render_user_family(parsed, cw, True) 2253 2128 cw.nl() 2254 2129 2255 2130 if args.mode == "kernel": ··· 2313 2184 print_kernel_family_struct_src(parsed, cw) 2314 2185 2315 2186 if args.mode == "user": 2316 - has_ntf = False 2317 2187 if args.header: 2188 + cw.p('/* Enums */') 2189 + put_op_name_fwd(parsed, cw) 2190 + 2191 + for name, const in parsed.consts.items(): 2192 + if isinstance(const, EnumSet): 2193 + put_enum_to_str_fwd(parsed, cw, const) 2194 + cw.nl() 2195 + 2318 2196 cw.p('/* Common nested types */') 2319 2197 for attr_set, struct in sorted(parsed.pure_nested_structs.items()): 2320 2198 ri = RenderInfo(cw, parsed, args.mode, "", "", "", attr_set) ··· 2357 2221 if 'notify' in op: 2358 2222 cw.p(f"/* {op.enum_name} - notify */") 2359 2223 ri = RenderInfo(cw, parsed, args.mode, op, op_name, 'notify') 2360 - has_ntf = True 2361 2224 if not ri.type_consistent: 2362 - raise Exception('Only notifications with consistent types supported') 2225 + raise Exception(f'Only notifications with consistent types supported ({op.name})') 2363 2226 print_wrapped_type(ri) 2364 2227 2365 2228 if 'event' in op: ··· 2368 2233 cw.nl() 2369 2234 print_wrapped_type(ri) 2370 2235 2371 - if has_ntf: 2236 + if parsed.has_notifications(): 2372 2237 cw.p('/* --------------- Common notification parsing --------------- */') 2373 2238 print_ntf_parse_prototype(parsed, cw) 2374 2239 cw.nl() 2375 2240 else: 2241 + cw.p('/* Enums */') 2242 + put_op_name(parsed, cw) 2243 + 2244 + for name, const in parsed.consts.items(): 2245 + if isinstance(const, EnumSet): 2246 + put_enum_to_str(parsed, cw, const) 2247 + cw.nl() 2248 + 2376 2249 cw.p('/* Policies */') 2377 2250 for name, _ in parsed.attr_sets.items(): 2378 2251 struct = Struct(parsed, name) ··· 2406 2263 if 'do' in op and 'event' not in op: 2407 2264 cw.p(f"/* {op.enum_name} - do */") 2408 2265 ri = RenderInfo(cw, parsed, args.mode, op, op_name, "do") 2266 + print_req_free(ri) 2409 2267 print_rsp_free(ri) 2410 2268 parse_rsp_msg(ri) 2411 2269 print_req(ri) ··· 2424 2280 if 'notify' in op: 2425 2281 cw.p(f"/* {op.enum_name} - notify */") 2426 2282 ri = RenderInfo(cw, parsed, args.mode, op, op_name, 'notify') 2427 - has_ntf = True 2428 2283 if not ri.type_consistent: 2429 - raise Exception('Only notifications with consistent types supported') 2284 + raise Exception(f'Only notifications with consistent types supported ({op.name})') 2430 2285 print_ntf_type_free(ri) 2431 2286 2432 2287 if 'event' in op: 2433 2288 cw.p(f"/* {op.enum_name} - event */") 2434 - has_ntf = True 2435 2289 2436 2290 ri = RenderInfo(cw, parsed, args.mode, op, op_name, "do") 2437 2291 parse_rsp_msg(ri) ··· 2437 2295 ri = RenderInfo(cw, parsed, args.mode, op, op_name, "event") 2438 2296 print_ntf_type_free(ri) 2439 2297 2440 - if has_ntf: 2298 + if parsed.has_notifications(): 2441 2299 cw.p('/* --------------- Common notification parsing --------------- */') 2442 2300 print_ntf_type_parse(parsed, cw, args.mode) 2301 + 2302 + cw.nl() 2303 + render_user_family(parsed, cw, False) 2443 2304 2444 2305 if args.header: 2445 2306 cw.p(f'#endif /* {hdr_prot} */')