def css_next_descendant_pre(pos: Object, root: Object) -> Object: """ Get the next pre-order descendant (or ``NULL`` if there is none) of the given css root starting from the given position (``NULL`` to initiate traversal). :param pos: ``struct cgroup_subsys_state *`` :param root: ``struct cgroup_subsys_state *`` :return: ``struct cgroup_subsys_state *`` """ # If first iteration, visit root. if not pos: return root # Visit the first child if exists. null = NULL(pos.prog_, "struct cgroup_subsys_state *") next_ = css_next_child(null, pos) if next_: return next_ # No child, visit my or the closest ancestor's next sibling. while pos != root: next_ = css_next_child(pos, pos.parent) if next_: return next_ pos = pos.parent return NULL(root.prog_, "struct cgroup_subsys_state *")
def test_rb_next(self): for i in range(self.num_entries - 1): self.assertEqual(rb_next(self.node(i)), self.node(i + 1)) self.assertEqual( rb_next(self.node(self.num_entries - 1)), NULL(self.prog, "struct rb_node *"), )
def find_user(prog, uid): """ .. c:function:: struct user_struct *find_user(kuid_t uid) Return the user structure with the given UID, which may be a ``kuid_t`` or an integer. """ try: uidhashentry = prog.cache["uidhashentry"] except KeyError: uidhash_table = prog["uidhash_table"] uidhash_sz = len(uidhash_table) uidhash_bits = uidhash_sz.bit_length() - 1 uidhash_mask = uidhash_sz - 1 def uidhashentry(uid): hash = ((uid >> uidhash_bits) + uid) & uidhash_mask return uidhash_table + hash prog.cache["uidhashentry"] = uidhashentry uid = _kuid_val(uid) for user in hlist_for_each_entry( "struct user_struct", uidhashentry(uid), "uidhash_node" ): if user.uid.val == uid: return user return NULL(prog, "struct user_struct *")
def rb_next(node): """ .. c:function:: struct rb_node *rb_next(struct rb_node *node) Return the next node (in sort order) after a red-black node, or a ``NULL`` object if the node is the last node in the tree or is empty. """ node = node.read_() if RB_EMPTY_NODE(node): return NULL(node.prog_, node.type_) next = node.rb_right.read_() if next: node = next while True: next = node.rb_left.read_() if not next: return node node = next parent = rb_parent(node).read_() while parent and node == parent.rb_right: node = parent parent = rb_parent(node).read_() return parent
def find_pid(prog_or_ns, nr): """ .. c:function:: struct pid *find_pid(struct pid_namespace *ns, int nr) Return the ``struct pid *`` for the given PID number in the given namespace. If given a :class:`Program` instead, the initial PID namespace is used. """ if isinstance(prog_or_ns, Program): prog = prog_or_ns ns = prog_or_ns['init_pid_ns'].address_of_() else: prog = prog_or_ns.prog_ ns = prog_or_ns if hasattr(ns, 'idr'): return cast('struct pid *', idr_find(ns.idr, nr)) else: # We could implement pid_hashfn() and only search that bucket, but it's # different for 32-bit and 64-bit systems, and it has changed at least # once, in v4.7. Searching the whole hash table is slower but # foolproof. pid_hash = prog['pid_hash'] for i in range(1 << prog['pidhash_shift'].value_()): for upid in hlist_for_each_entry('struct upid', pid_hash[i].address_of_(), 'pid_chain'): if upid.nr == nr and upid.ns == ns: return container_of(upid, 'struct pid', f'numbers[{ns.level.value_()}]') return NULL(prog, 'struct pid *')
def rb_find( type: Union[str, Type], root: Object, member: str, key: KeyType, cmp: Callable[[KeyType, Object], int], ) -> Object: """ Find an entry in a red-black tree given a key and a comparator function. Note that this function does not have an analogue in the Linux kernel source code, as tree searches are all open-coded. :param type: Entry type. :param root: ``struct rb_root *`` :param member: Name of ``struct rb_node`` member in entry type. :param key: Key to find. :param cmp: Callback taking key and entry that returns < 0 if the key is less than the entry, > 0 if the key is greater than the entry, and 0 if the key matches the entry. :return: ``type *`` found entry, or ``NULL`` if not found. """ prog = root.prog_ type = prog.type(type) node = root.rb_node.read_() while node: entry = container_of(node, type, member) ret = cmp(key, entry) if ret < 0: node = node.rb_left.read_() elif ret > 0: node = node.rb_right.read_() else: return entry return NULL(prog, prog.pointer_type(type))
def rb_prev(node: Object) -> Object: """ Return the previous node (in sort order) before a red-black node, or ``NULL`` if the node is the first node in the tree or is empty. :param node: ``struct rb_node *`` :return: ``struct rb_node *`` """ node = node.read_() if RB_EMPTY_NODE(node): return NULL(node.prog_, node.type_) next = node.rb_left.read_() if next: node = next while True: next = node.rb_right.read_() if not next: return node node = next parent = rb_parent(node).read_() while parent and node == parent.rb_left: node = parent parent = rb_parent(node).read_() return parent
def css_next_child(pos: Object, parent: Object) -> Object: """ Get the next child (or ``NULL`` if there is none) of the given parent starting from the given position (``NULL`` to initiate traversal). :param pos: ``struct cgroup_subsys_state *`` :param parent: ``struct cgroup_subsys_state *`` :return: ``struct cgroup_subsys_state *`` """ if not pos: next_ = container_of( parent.children.next, "struct cgroup_subsys_state", "sibling" ) elif not (pos.flags & pos.prog_["CSS_RELEASED"]): next_ = container_of(pos.sibling.next, "struct cgroup_subsys_state", "sibling") else: serial_nr = pos.serial_nr.value_() # Read once and cache. for next_ in list_for_each_entry( "struct cgroup_subsys_state", parent.children.address_of_(), "sibling" ): if next_.serial_nr > serial_nr: break if next_.sibling.address_of_() != parent.children.address_of_(): return next_ return NULL(next_.prog_, "struct cgroup_subsys_state *")
def find_user(prog: Program, uid: Union[Object, IntegerLike]) -> Object: """ Return the user structure with the given UID. :param uid: ``kuid_t`` object or integer. :return: ``struct user_state *`` """ try: uidhashentry = prog.cache["uidhashentry"] except KeyError: uidhash_table = prog["uidhash_table"] uidhash_sz = len(uidhash_table) uidhash_bits = uidhash_sz.bit_length() - 1 uidhash_mask = uidhash_sz - 1 def uidhashentry(uid: int) -> Object: hash = ((uid >> uidhash_bits) + uid) & uidhash_mask return uidhash_table + hash prog.cache["uidhashentry"] = uidhashentry uid = _kuid_val(uid) for user in hlist_for_each_entry("struct user_struct", uidhashentry(uid), "uidhash_node"): if user.uid.val == uid: return user return NULL(prog, "struct user_struct *")
def _css_for_each_impl(next_fn, css): pos = NULL(css.prog_, "struct cgroup_subsys_state *") while True: pos = next_fn(pos, css) if not pos: break if pos.flags & pos.prog_["CSS_ONLINE"]: yield pos
def pid_task(pid, pid_type): """ .. c:function:: struct task_struct *pid_task(struct pid *pid, enum pid_type pid_type) Return the ``struct task_struct *`` containing the given ``struct pid *`` of the given type. """ if not pid: return NULL(pid.prog_, 'struct task_struct *') first = pid.tasks[0].first if not first: return NULL(pid.prog_, 'struct task_struct *') try: return container_of(first, 'struct task_struct', f'pid_links[{int(pid_type)}]') except LookupError: return container_of(first, 'struct task_struct', f'pids[{int(pid_type)}].node')
def _css_for_each_impl(next_fn: Callable[[Object, Object], Object], css: Object) -> Iterator[Object]: pos = NULL(css.prog_, "struct cgroup_subsys_state *") while True: pos = next_fn(pos, css) if not pos: break if pos.flags & pos.prog_["CSS_ONLINE"]: yield pos
def cgroup_parent(cgrp): """ .. c:function:: struct cgroup *cgroup_parent(struct cgroup *cgrp) Return the parent cgroup of the given cgroup if it exists, ``NULL`` otherwise. """ parent_css = cgrp.self.parent if parent_css: return container_of(parent_css, "struct cgroup", "self") return NULL(cgrp.prog_, "struct cgroup *")
def cgroup_parent(cgrp: Object) -> Object: """ Return the parent cgroup of the given cgroup if it exists, ``NULL`` otherwise. :param cgrp: ``struct cgroup *`` :return: ``struct cgroup *`` """ parent_css = cgrp.self.parent if parent_css: return container_of(parent_css, "struct cgroup", "self") return NULL(cgrp.prog_, "struct cgroup *")
def list_first_entry_or_null(head, type, member): """ .. c:function:: type *list_first_entry_or_null(struct list_head *head, type, member) Return the first entry in a list or ``NULL`` if the list is empty. See also :func:`list_first_entry()`. """ head = head.read_() pos = head.next.read_() if pos == head: return NULL(head.prog_, head.prog_.pointer_type(type)) else: return container_of(pos, type, member)
def test_rb_find(self): def cmp(key, obj): value = obj.value.value_() return key - value for i in range(self.num_entries): self.assertEqual( rb_find("struct drgn_test_rb_entry", self.root, "node", i, cmp), self.entry(i), ) self.assertEqual( rb_find("struct drgn_test_rb_entry", self.root, "node", self.num_entries, cmp), NULL(self.prog, "struct drgn_test_rb_entry *"), )
def test_list_first_entry_or_null(self): self.assertEqual( list_first_entry_or_null(self.empty, "struct drgn_test_list_entry", "node"), NULL(self.prog, "struct drgn_test_list_entry *"), ) self.assertEqual( list_first_entry_or_null(self.full, "struct drgn_test_list_entry", "node"), self.entry(0), ) self.assertEqual( list_first_entry_or_null(self.singular, "struct drgn_test_list_entry", "node"), self.singular_entry, )
def list_first_entry_or_null(head: Object, type: Union[str, Type], member: str) -> Object: """ Return the first entry in a list or ``NULL`` if the list is empty. See also :func:`list_first_entry()`. :param head: ``struct list_head *`` :param type: Entry type. :param member: Name of list node member in entry type. :return: ``type *`` """ head = head.read_() pos = head.next.read_() if pos == head: return NULL(head.prog_, head.prog_.pointer_type(head.prog_.type(type))) else: return container_of(pos, type, member)
def qdisc_lookup(dev: Object, major: IntegerLike) -> Object: """ Get a Qdisc from a device and a major handle number. It is worth noting that conventionally handles are hexadecimal, e.g. ``10:`` in a ``tc`` command means major handle 0x10. :param dev: ``struct net_device *`` :param major: Qdisc major handle number. :return: ``struct Qdisc *`` (``NULL`` if not found) """ major = operator.index(major) << 16 roots = [dev.qdisc] if dev.ingress_queue: roots.append(dev.ingress_queue.qdisc_sleeping) # Since Linux kernel commit 59cc1f61f09c ("net: sched: convert qdisc linked # list to hashtable") (in v4.7), a device's child Qdiscs are maintained in # a hashtable in its struct net_device. Before that, they are maintained in # a linked list in their root Qdisc. use_hashtable = dev.prog_.type("struct net_device").has_member( "qdisc_hash") for root in roots: if root.handle == major: return root if use_hashtable: for head in root.dev_queue.dev.qdisc_hash: for qdisc in hlist_for_each_entry("struct Qdisc", head.address_of_(), "hash"): if qdisc.handle == major: return qdisc else: for qdisc in list_for_each_entry("struct Qdisc", root.list.address_of_(), "list"): if qdisc.handle == major: return qdisc return NULL(dev.prog_, "struct Qdisc *")
def netdev_get_by_name(prog_or_net: Union[Program, Object], name: Union[str, bytes]) -> Object: """ Get the network device with the given interface name. :param prog_or_net: ``struct net *`` containing the device, or :class:`Program` to use the initial network namespace. :param name: Network interface name. :return: ``struct net_device *`` (``NULL`` if not found) """ if isinstance(prog_or_net, Program): prog_or_net = prog_or_net["init_net"] if isinstance(name, str): name = name.encode() # Since Linux kernel commit ff92741270bf ("net: introduce name_node struct # to be used in hashlist") (in v5.5), the device name hash table contains # struct netdev_name_node entries. Before that, it contained the struct # net_device directly. try: entry_type = prog_or_net.prog_.type("struct netdev_name_node") member = "hlist" entry_is_name_node = True except LookupError: entry_type = prog_or_net.prog_.type("struct net_device") member = "name_hlist" entry_is_name_node = False for i in range(_NETDEV_HASHENTRIES): head = prog_or_net.dev_name_head[i] for entry in hlist_for_each_entry(entry_type, head, member): if entry.name.string_() == name: if entry_is_name_node: return entry.dev else: return entry return NULL(prog_or_net.prog_, "struct net_device *")
def netdev_get_by_index(prog_or_net: Union[Program, Object], ifindex: IntegerLike) -> Object: """ Get the network device with the given interface index number. :param prog_or_net: ``struct net *`` containing the device, or :class:`Program` to use the initial network namespace. :param ifindex: Network interface index number. :return: ``struct net_device *`` (``NULL`` if not found) """ if isinstance(prog_or_net, Program): prog_or_net = prog_or_net["init_net"] if isinstance(ifindex, Object): ifindex = ifindex.read_() head = prog_or_net.dev_index_head[operator.index(ifindex) & (_NETDEV_HASHENTRIES - 1)] for netdev in hlist_for_each_entry("struct net_device", head, "index_hlist"): if netdev.ifindex == ifindex: return netdev return NULL(prog_or_net.prog_, "struct net_device *")
def test_rb_prev(self): for i in range(1, self.num_entries): self.assertEqual(rb_prev(self.node(i)), self.node(i - 1)) self.assertEqual(rb_prev(self.node(0)), NULL(self.prog, "struct rb_node *"))
def test_cgroup_parent(self): self.assertEqual(cgroup_parent(self.child_cgroup), self.parent_cgroup) self.assertEqual(cgroup_parent(self.parent_cgroup), self.root_cgroup) self.assertEqual(cgroup_parent(self.root_cgroup), NULL(self.prog, "struct cgroup *"))