Skip to content

Commit 2d8cb0b

Browse files
committed
Merge branch 'tools-ynl-fix-enum-as-flags-in-the-generic-cli'
Jakub Kicinski says: ==================== tools: ynl: fix enum-as-flags in the generic CLI The CLI needs to use proper classes when looking at Enum definitions rather than interpreting the YAML spec ad-hoc, because we have more than on format of the definition supported. ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents 649c15c + c311aaa commit 2d8cb0b

File tree

4 files changed

+126
-96
lines changed

4 files changed

+126
-96
lines changed

tools/net/ynl/lib/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
22

3-
from .nlspec import SpecAttr, SpecAttrSet, SpecFamily, SpecOperation
3+
from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \
4+
SpecFamily, SpecOperation
45
from .ynl import YnlFamily
56

6-
__all__ = ["SpecAttr", "SpecAttrSet", "SpecFamily", "SpecOperation",
7-
"YnlFamily"]
7+
__all__ = ["SpecAttr", "SpecAttrSet", "SpecEnumEntry", "SpecEnumSet",
8+
"SpecFamily", "SpecOperation", "YnlFamily"]

tools/net/ynl/lib/nlspec.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,94 @@ def resolve(self):
5757
pass
5858

5959

60+
class SpecEnumEntry(SpecElement):
61+
""" Entry within an enum declared in the Netlink spec.
62+
63+
Attributes:
64+
doc documentation string
65+
enum_set back reference to the enum
66+
value numerical value of this enum (use accessors in most situations!)
67+
68+
Methods:
69+
raw_value raw value, i.e. the id in the enum, unlike user value which is a mask for flags
70+
user_value user value, same as raw value for enums, for flags it's the mask
71+
"""
72+
def __init__(self, enum_set, yaml, prev, value_start):
73+
if isinstance(yaml, str):
74+
yaml = {'name': yaml}
75+
super().__init__(enum_set.family, yaml)
76+
77+
self.doc = yaml.get('doc', '')
78+
self.enum_set = enum_set
79+
80+
if 'value' in yaml:
81+
self.value = yaml['value']
82+
elif prev:
83+
self.value = prev.value + 1
84+
else:
85+
self.value = value_start
86+
87+
def has_doc(self):
88+
return bool(self.doc)
89+
90+
def raw_value(self):
91+
return self.value
92+
93+
def user_value(self):
94+
if self.enum_set['type'] == 'flags':
95+
return 1 << self.value
96+
else:
97+
return self.value
98+
99+
100+
class SpecEnumSet(SpecElement):
101+
""" Enum type
102+
103+
Represents an enumeration (list of numerical constants)
104+
as declared in the "definitions" section of the spec.
105+
106+
Attributes:
107+
type enum or flags
108+
entries entries by name
109+
entries_by_val entries by value
110+
Methods:
111+
get_mask for flags compute the mask of all defined values
112+
"""
113+
def __init__(self, family, yaml):
114+
super().__init__(family, yaml)
115+
116+
self.type = yaml['type']
117+
118+
prev_entry = None
119+
value_start = self.yaml.get('value-start', 0)
120+
self.entries = dict()
121+
self.entries_by_val = dict()
122+
for entry in self.yaml['entries']:
123+
e = self.new_entry(entry, prev_entry, value_start)
124+
self.entries[e.name] = e
125+
self.entries_by_val[e.raw_value()] = e
126+
prev_entry = e
127+
128+
def new_entry(self, entry, prev_entry, value_start):
129+
return SpecEnumEntry(self, entry, prev_entry, value_start)
130+
131+
def has_doc(self):
132+
if 'doc' in self.yaml:
133+
return True
134+
for entry in self.entries.values():
135+
if entry.has_doc():
136+
return True
137+
return False
138+
139+
def get_mask(self):
140+
mask = 0
141+
idx = self.yaml.get('value-start', 0)
142+
for _ in self.entries.values():
143+
mask |= 1 << idx
144+
idx += 1
145+
return mask
146+
147+
60148
class SpecAttr(SpecElement):
61149
""" Single Netlink atttribute type
62150
@@ -193,6 +281,7 @@ class SpecFamily(SpecElement):
193281
msgs dict of all messages (index by name)
194282
msgs_by_value dict of all messages (indexed by name)
195283
ops dict of all valid requests / responses
284+
consts dict of all constants/enums
196285
"""
197286
def __init__(self, spec_path, schema_path=None):
198287
with open(spec_path, "r") as stream:
@@ -222,6 +311,7 @@ def __init__(self, spec_path, schema_path=None):
222311
self.req_by_value = collections.OrderedDict()
223312
self.rsp_by_value = collections.OrderedDict()
224313
self.ops = collections.OrderedDict()
314+
self.consts = collections.OrderedDict()
225315

226316
last_exception = None
227317
while len(self._resolution_list) > 0:
@@ -242,6 +332,9 @@ def __init__(self, spec_path, schema_path=None):
242332
if len(resolved) == 0:
243333
raise last_exception
244334

335+
def new_enum(self, elem):
336+
return SpecEnumSet(self, elem)
337+
245338
def new_attr_set(self, elem):
246339
return SpecAttrSet(self, elem)
247340

@@ -296,6 +389,12 @@ def _dictify_ops_directional(self):
296389
def resolve(self):
297390
self.resolve_up(super())
298391

392+
for elem in self.yaml['definitions']:
393+
if elem['type'] == 'enum' or elem['type'] == 'flags':
394+
self.consts[elem['name']] = self.new_enum(elem)
395+
else:
396+
self.consts[elem['name']] = elem
397+
299398
for elem in self.yaml['attribute-sets']:
300399
attr_set = self.new_attr_set(elem)
301400
self.attr_sets[elem['name']] = attr_set

tools/net/ynl/lib/ynl.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,6 @@ def __init__(self, def_path, schema=None):
303303
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1)
304304
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_EXT_ACK, 1)
305305

306-
self._types = dict()
307-
308-
for elem in self.yaml.get('definitions', []):
309-
self._types[elem['name']] = elem
310-
311306
self.async_msg_ids = set()
312307
self.async_msg_queue = []
313308

@@ -353,13 +348,13 @@ def _add_attr(self, space, name, value):
353348

354349
def _decode_enum(self, rsp, attr_spec):
355350
raw = rsp[attr_spec['name']]
356-
enum = self._types[attr_spec['enum']]
351+
enum = self.consts[attr_spec['enum']]
357352
i = attr_spec.get('value-start', 0)
358353
if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']:
359354
value = set()
360355
while raw:
361356
if raw & 1:
362-
value.add(enum['entries'][i])
357+
value.add(enum.entries_by_val[i].name)
363358
raw >>= 1
364359
i += 1
365360
else:

tools/net/ynl/ynl-gen-c.py

Lines changed: 21 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import os
77
import yaml
88

9-
from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation
9+
from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry
1010

1111

1212
def c_upper(name):
@@ -567,97 +567,37 @@ def set_inherited(self, new_inherited):
567567
self.inherited = [c_lower(x) for x in sorted(self._inherited)]
568568

569569

570-
class EnumEntry:
570+
class EnumEntry(SpecEnumEntry):
571571
def __init__(self, enum_set, yaml, prev, value_start):
572-
if isinstance(yaml, str):
573-
self.name = yaml
574-
yaml = {}
575-
self.doc = ''
576-
else:
577-
self.name = yaml['name']
578-
self.doc = yaml.get('doc', '')
579-
580-
self.yaml = yaml
581-
self.enum_set = enum_set
582-
self.c_name = c_upper(enum_set.value_pfx + self.name)
583-
584-
if 'value' in yaml:
585-
self.value = yaml['value']
586-
if prev:
587-
self.value_change = (self.value != prev.value + 1)
588-
elif prev:
589-
self.value_change = False
590-
self.value = prev.value + 1
572+
super().__init__(enum_set, yaml, prev, value_start)
573+
574+
if prev:
575+
self.value_change = (self.value != prev.value + 1)
591576
else:
592-
self.value = value_start
593577
self.value_change = (self.value != 0)
594-
595578
self.value_change = self.value_change or self.enum_set['type'] == 'flags'
596579

597-
def __getitem__(self, key):
598-
return self.yaml[key]
599-
600-
def __contains__(self, key):
601-
return key in self.yaml
602-
603-
def has_doc(self):
604-
return bool(self.doc)
580+
# Added by resolve:
581+
self.c_name = None
582+
delattr(self, "c_name")
605583

606-
# raw value, i.e. the id in the enum, unlike user value which is a mask for flags
607-
def raw_value(self):
608-
return self.value
584+
def resolve(self):
585+
self.resolve_up(super())
609586

610-
# user value, same as raw value for enums, for flags it's the mask
611-
def user_value(self):
612-
if self.enum_set['type'] == 'flags':
613-
return 1 << self.value
614-
else:
615-
return self.value
587+
self.c_name = c_upper(self.enum_set.value_pfx + self.name)
616588

617589

618-
class EnumSet:
590+
class EnumSet(SpecEnumSet):
619591
def __init__(self, family, yaml):
620-
self.yaml = yaml
621-
self.family = family
622-
623592
self.render_name = c_lower(family.name + '-' + yaml['name'])
624593
self.enum_name = 'enum ' + self.render_name
625594

626595
self.value_pfx = yaml.get('name-prefix', f"{family.name}-{yaml['name']}-")
627596

628-
self.type = yaml['type']
629-
630-
prev_entry = None
631-
value_start = self.yaml.get('value-start', 0)
632-
self.entries = {}
633-
self.entry_list = []
634-
for entry in self.yaml['entries']:
635-
e = EnumEntry(self, entry, prev_entry, value_start)
636-
self.entries[e.name] = e
637-
self.entry_list.append(e)
638-
prev_entry = e
639-
640-
def __getitem__(self, key):
641-
return self.yaml[key]
642-
643-
def __contains__(self, key):
644-
return key in self.yaml
645-
646-
def has_doc(self):
647-
if 'doc' in self.yaml:
648-
return True
649-
for entry in self.entry_list:
650-
if entry.has_doc():
651-
return True
652-
return False
597+
super().__init__(family, yaml)
653598

654-
def get_mask(self):
655-
mask = 0
656-
idx = self.yaml.get('value-start', 0)
657-
for _ in self.entry_list:
658-
mask |= 1 << idx
659-
idx += 1
660-
return mask
599+
def new_entry(self, entry, prev_entry, value_start):
600+
return EnumEntry(self, entry, prev_entry, value_start)
661601

662602

663603
class AttrSet(SpecAttrSet):
@@ -792,8 +732,6 @@ def resolve(self):
792732

793733
self.mcgrps = self.yaml.get('mcast-groups', {'list': []})
794734

795-
self.consts = dict()
796-
797735
self.hooks = dict()
798736
for when in ['pre', 'post']:
799737
self.hooks[when] = dict()
@@ -820,6 +758,9 @@ def resolve(self):
820758
if self.kernel_policy == 'global':
821759
self._load_global_policy()
822760

761+
def new_enum(self, elem):
762+
return EnumSet(self, elem)
763+
823764
def new_attr_set(self, elem):
824765
return AttrSet(self, elem)
825766

@@ -837,12 +778,6 @@ def _mock_up_events(self):
837778
}
838779

839780
def _dictify(self):
840-
for elem in self.yaml['definitions']:
841-
if elem['type'] == 'enum' or elem['type'] == 'flags':
842-
self.consts[elem['name']] = EnumSet(self, elem)
843-
else:
844-
self.consts[elem['name']] = elem
845-
846781
ntf = []
847782
for msg in self.msgs.values():
848783
if 'notify' in msg:
@@ -1980,15 +1915,15 @@ def render_uapi(family, cw):
19801915
if 'doc' in enum:
19811916
doc = ' - ' + enum['doc']
19821917
cw.write_doc_line(enum.enum_name + doc)
1983-
for entry in enum.entry_list:
1918+
for entry in enum.entries.values():
19841919
if entry.has_doc():
19851920
doc = '@' + entry.c_name + ': ' + entry['doc']
19861921
cw.write_doc_line(doc)
19871922
cw.p(' */')
19881923

19891924
uapi_enum_start(family, cw, const, 'name')
19901925
name_pfx = const.get('name-prefix', f"{family.name}-{const['name']}-")
1991-
for entry in enum.entry_list:
1926+
for entry in enum.entries.values():
19921927
suffix = ','
19931928
if entry.value_change:
19941929
suffix = f" = {entry.user_value()}" + suffix

0 commit comments

Comments
 (0)