kernel/filtermods.py
Julio Faracco 91a0bd7b67 kernel-6.12.0-78.el10
* Mon Apr 28 2025 Julio Faracco <jfaracco@redhat.com> [6.12.0-78.el10]
- redhat: Remove kernel-rt-kvm package (Juri Lelli) [RHEL-62687]
- nfsd: clear acl_access/acl_default after releasing them (Olga Kornievskaia) [RHEL-81534] {CVE-2025-21796}
- redhat/configs: Remove CONFIG_VDPA_USER unset config on RHEL10 (Cindy Lu) [RHEL-87716]
- scripts/nsdeps: get 'make nsdeps' working again (José Expósito) [RHEL-85957]
- doc: module: revert misconversions for MODULE_IMPORT_NS() (José Expósito) [RHEL-85957]
- module: Convert default symbol namespace to string literal (José Expósito) [RHEL-85957]
- scripts/kernel-doc: Get -export option working again (José Expósito) [RHEL-85957]
- doc: module: Fix documented type of namespace (José Expósito) [RHEL-85957]
- module: Convert symbol namespace to string literal (José Expósito) [RHEL-85957]
- net: Fix null-ptr-deref by sock_lock_init_class_and_name() and rmmod. (Paolo Abeni) [RHEL-87453]
- net/neighbor: add missing policy for NDTPA_QUEUE_LENBYTES (Paolo Abeni) [RHEL-87453]
- netpoll: hold rcu read lock in __netpoll_send_skb() (Paolo Abeni) [RHEL-87453]
- netmem: prevent TX of unreadable skbs (Paolo Abeni) [RHEL-87453] {CVE-2025-21954}
- net: Clear old fragment checksum value in napi_reuse_skb (Paolo Abeni) [RHEL-87453]
- ipvs: Always clear ipvs_property flag in skb_scrub_packet() (Paolo Abeni) [RHEL-87453]
- net: set the minimum for net_hotdata.netdev_budget_usecs (Paolo Abeni) [RHEL-87453]
- flow_dissector: Fix handling of mixed port and port-range keys (Paolo Abeni) [RHEL-87453]
- neighbour: use RCU protection in __neigh_notify() (Paolo Abeni) [RHEL-87453] {CVE-2025-21763}
- net: fib_rules: annotate data-races around rule->[io]ifindex (Paolo Abeni) [RHEL-87453]
- flow_dissector: use RCU protection to fetch dev_net() (Paolo Abeni) [RHEL-87453]
- net: let net.core.dev_weight always be non-zero (Paolo Abeni) [RHEL-87453] {CVE-2025-21806}
- dev: Acquire netdev_rename_lock before restoring dev->name in dev_change_name(). (Paolo Abeni) [RHEL-87453]
- ptr_ring: do not block hard interrupts in ptr_ring_resize_multiple() (Paolo Abeni) [RHEL-87453] {CVE-2024-57994}
- nfsd: validate the nfsd_serv pointer before calling svc_wake_up (Olga Kornievskaia) [RHEL-86641]
- Bluetooth: L2CAP: handle NULL sock pointer in l2cap_sock_alloc (Bastien Nocera) [RHEL-74484] {CVE-2024-58009}
- Bluetooth: btnxpuart: Fix glitches seen in dual A2DP streaming (Bastien Nocera) [RHEL-74484]
- Revert "Bluetooth: hci_core: Fix sleeping function called from invalid context" (Bastien Nocera) [RHEL-74484]
- Bluetooth: SCO: fix sco_conn refcounting on sco_conn_ready (Bastien Nocera) [RHEL-74484]
- Bluetooth: btnxpuart: Fix driver sending truncated data (Bastien Nocera) [RHEL-74484]
- Bluetooth: MGMT: Fix Add Device to responding before completing (Bastien Nocera) [RHEL-74484]
- Bluetooth: hci_sync: Fix not setting Random Address when required (Bastien Nocera) [RHEL-74484]
- Bluetooth: iso: Fix circular lock in iso_conn_big_sync (Bastien Nocera) [RHEL-74484] {CVE-2024-54191}
- Bluetooth: iso: Fix circular lock in iso_listen_bis (Bastien Nocera) [RHEL-74484] {CVE-2024-54460}
- Bluetooth: SCO: Add support for 16 bits transparent voice setting (Bastien Nocera) [RHEL-74484]
- Bluetooth: iso: Fix recursive locking warning (Bastien Nocera) [RHEL-74484]
- Bluetooth: iso: Always release hdev at the end of iso_listen_bis (Bastien Nocera) [RHEL-74484] {CVE-2024-57879}
- Bluetooth: hci_event: Fix using rcu_read_(un)lock while iterating (Bastien Nocera) [RHEL-74484] {CVE-2024-56654}
- Bluetooth: Improve setsockopt() handling of malformed user input (Bastien Nocera) [RHEL-74484]
- Bluetooth: SCO: remove the redundant sco_conn_put (Bastien Nocera) [RHEL-74484]
- Bluetooth: MGMT: Fix possible deadlocks (Bastien Nocera) [RHEL-74484] {CVE-2024-53207}
- Bluetooth: MGMT: Add initial implementation of MGMT_OP_HCI_CMD_SYNC (Bastien Nocera) [RHEL-74484]
- Bluetooth: hci_bcm: Use the devm_clk_get_optional() helper (Bastien Nocera) [RHEL-74484]
- Bluetooth: ISO: Send BIG Create Sync via hci_sync (Bastien Nocera) [RHEL-74484]
- Bluetooth: hci_conn: Remove alloc from critical section (Bastien Nocera) [RHEL-74484]
- Bluetooth: ISO: Use kref to track lifetime of iso_conn (Bastien Nocera) [RHEL-74484]
- Bluetooth: SCO: Use kref to track lifetime of sco_conn (Bastien Nocera) [RHEL-74484]
- Bluetooth: HCI: Add IPC(11) bus type (Bastien Nocera) [RHEL-74484]
- Bluetooth: btusb: Add 3 HWIDs for MT7925 (Bastien Nocera) [RHEL-74484]
- Bluetooth: btusb: Add new VID/PID 0489/e124 for MT7925 (Bastien Nocera) [RHEL-74484]
- Bluetooth: ISO: Update hci_conn_hash_lookup_big for Broadcast slave (Bastien Nocera) [RHEL-74484]
- Bluetooth: ISO: Do not emit LE BIG Create Sync if previous is pending (Bastien Nocera) [RHEL-74484]
- Bluetooth: ISO: Fix matching parent socket for BIS slave (Bastien Nocera) [RHEL-74484]
- Bluetooth: ISO: Do not emit LE PA Create Sync if previous is pending (Bastien Nocera) [RHEL-74484]
- Bluetooth: btrtl: Decrease HCI_OP_RESET timeout from 10 s to 2 s (Bastien Nocera) [RHEL-74484]
- Bluetooth: btbcm: fix missing of_node_put() in btbcm_get_board_name() (Bastien Nocera) [RHEL-74484]
- Bluetooth: btusb: Add new VID/PID 0489/e111 for MT7925 (Bastien Nocera) [RHEL-74484]
- Bluetooth: Set quirks for ATS2851 (Bastien Nocera) [RHEL-74484]
- Bluetooth: Support new quirks for ATS2851 (Bastien Nocera) [RHEL-74484]
- Bluetooth: Add new quirks for ATS2851 (Bastien Nocera) [RHEL-74484]
- Bluetooth: Fix type of len in rfcomm_sock_getsockopt{,_old}() (Bastien Nocera) [RHEL-74484]
- Bluetooth: hci_qca: use devm_clk_get_optional_enabled_with_rate() (Bastien Nocera) [RHEL-74484]
- Bluetooth: btmtksdio: Lookup device node only as fallback (Bastien Nocera) [RHEL-74484]
- Bluetooth: hci_core: Fix not checking skb length on hci_scodata_packet (Bastien Nocera) [RHEL-74484]
- Bluetooth: btnxpuart: Add GPIO support to power save feature (Bastien Nocera) [RHEL-74484]
- Bluetooth: hci_conn: Use disable_delayed_work_sync (Bastien Nocera) [RHEL-74484] {CVE-2024-56591}
- Bluetooth: btusb: Add USB HW IDs for MT7920/MT7925 (Bastien Nocera) [RHEL-74484]
- Bluetooth: btusb: Add RTL8852BE device 0489:e123 to device tables (Bastien Nocera) [RHEL-74484]
- Bluetooth: hci_conn: Reduce hci_conn_drop() calls in two functions (Bastien Nocera) [RHEL-74484]
- Bluetooth: btnxpuart: Rename IW615 to IW610 (Bastien Nocera) [RHEL-74484]
- Bluetooth: btnxpuart: Drop _v0 suffix from FW names (Bastien Nocera) [RHEL-74484]
- Bluetooth: btusb: Add one more ID 0x13d3:0x3623 for Qualcomm WCN785x (Bastien Nocera) [RHEL-74484]
- Bluetooth: btusb: Add one more ID 0x0489:0xe0f3 for Qualcomm WCN785x (Bastien Nocera) [RHEL-74484]
- Bluetooth: btusb: add Foxconn 0xe0fc for Qualcomm WCN785x (Bastien Nocera) [RHEL-74484]
- Bluetooth: add HAS_IOPORT dependencies (Bastien Nocera) [RHEL-74484]
- Bluetooth: RFCOMM: avoid leaving dangling sk pointer in rfcomm_sock_alloc() (Bastien Nocera) [RHEL-74484] {CVE-2024-56604}
- Bluetooth: L2CAP: do not leave dangling sk pointer on error in l2cap_sock_create() (Bastien Nocera) [RHEL-74484] {CVE-2024-56605}
- igb: Fix potential invalid memory access in igb_init_module() (Corinna Vinschen) [RHEL-74378] {CVE-2024-52332}
- RDMA/bnxt_re: Remove unusable nq variable (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix budget handling of notification queue (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Support perf management counters (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Avoid clearing VLAN_ID mask in modify qp path (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix reporting maximum SRQs on P7 chips (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add missing paranthesis in map_qp_id_to_tbl_indx (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix allocation of QP table (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix the page details for the srq created by kernel consumers (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix the statistics for Gen P7 VF (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix issue in the unload path (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add sanity checks on rdev validity (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix an issue in bnxt_re_async_notifier (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix the condition check while programming congestion control (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix buffer overflow in debugfs code (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Congestion control settings using debugfs hook (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Allocate dev_attr information dynamically (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Pass the context for ulp_irq_stop (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add support to handle DCB_CONFIG_CHANGE event (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Query firmware defaults of CC params during probe (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add Async event handling support (Sreekanth Reddy) [RHEL-76566]
- bnxt_en: Add ULP call to notify async events (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix to drop reference to the mmap entry in case of error (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Remove deliver net device event (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix MSN table size for variable wqe mode (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add send queue size check for variable wqe (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix to export port num to ib_query_qp (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix setting mandatory attributes for modify_qp (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Remove unnecessary header file inclusion (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Eliminate need for some forward declarations (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Optimize error handling in bnxt_re_probe (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Remove unnecessary goto in bnxt_re_netdev_event (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Remove extra new line in bnxt_re_netdev_event (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Don't fail destroy QP and cleanup debugfs earlier (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Use the default mode of congestion control (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Support different traffic class (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Cache MSIx info to a local structure (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Refurbish CQ to NQ hash calculation (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Refactor NQ allocation (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fail probe early when not enough MSI-x vectors are reserved (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add set_func_resources support for P5/P7 adapters (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Enhance RoCE SRIOV resource configuration design (Sreekanth Reddy) [RHEL-76566]
- bnxt_en: Add support for RoCE sriov configuration (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add debugfs hook in the driver (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Support raw data query for each resources (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add support for querying HW contexts (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Support driver specific data collection using rdma tool (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Fix access flags for MR and QP modify (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add support for modify_device hook (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add support for CQ rx coalescing (Sreekanth Reddy) [RHEL-76566]
- RDMA/bnxt_re: Add support for optimized modify QP (Sreekanth Reddy) [RHEL-76566]
- tools headers: Update the socket headers with the kernel sources (Paolo Abeni) [RHEL-87244]
- net: ipv6: fix TCP GSO segmentation with NAT (Paolo Abeni) [RHEL-87244]
- net-timestamp: support TCP GSO case for a few missing flags (Paolo Abeni) [RHEL-87244]
- tcp: Defer ts_recent changes until req is owned (Paolo Abeni) [RHEL-87244]
- tcp: devmem: don't write truncated dmabuf CMSGs to userspace (Paolo Abeni) [RHEL-87244]
- tcp: adjust rcvq_space after updating scaling ratio (Paolo Abeni) [RHEL-87244]
- tcp: correct handling of extreme memory squeeze (Paolo Abeni) [RHEL-87244] {CVE-2025-21710}
- tcp_cubic: fix incorrect HyStart round start detection (Paolo Abeni) [RHEL-87244]
- tcp: Annotate data-race around sk->sk_mark in tcp_v4_send_reset (Paolo Abeni) [RHEL-87244]
- udp: Fix memory accounting leak. (Paolo Abeni) [RHEL-87213]
- udp: Fix multiple wraparounds of sk->sk_rmem_alloc. (Paolo Abeni) [RHEL-87213]
- udp: gso: do not drop small packets when PMTU reduces (Paolo Abeni) [RHEL-87213]
- udp: Deal with race between UDP socket address change and rehash (Paolo Abeni) [RHEL-87213] {CVE-2024-57974}
- io_uring/sqpoll: zero sqd->thread on tctx errors (CKI Backport Bot) [RHEL-74652] {CVE-2025-21633}
- selftests: netfilter: add test case for recent mismatch bug (CKI Backport Bot) [RHEL-87160]
- nft_set_pipapo: fix incorrect avx2 match of 5th field octet (CKI Backport Bot) [RHEL-87160]
- sctp: detect and prevent references to a freed transport in sendmsg (Xin Long) [RHEL-84557]
- sctp: add mutual exclusion in proc_sctp_do_udp_port() (Xin Long) [RHEL-84557]
- sctp: Remove unused payload from sctp_idatahdr (Xin Long) [RHEL-84557]
- sctp: Fix undefined behavior in left shift operation (Xin Long) [RHEL-84557]
- sctp: Remove commented out code (Xin Long) [RHEL-84557]
- sctp: sysctl: plpmtud_probe_interval: avoid using current->nsproxy (Xin Long) [RHEL-84557]
- sctp: sysctl: udp_port: avoid using current->nsproxy (Xin Long) [RHEL-84557]
- sctp: sysctl: auth_enable: avoid using current->nsproxy (Xin Long) [RHEL-84557]
- sctp: sysctl: rto_min/max: avoid using current->nsproxy (Xin Long) [RHEL-84557]
- sctp: sysctl: cookie_hmac_alg: avoid using current->nsproxy (Xin Long) [RHEL-84557]
- sctp: Prepare sctp_v4_get_dst() to dscp_t conversion. (Xin Long) [RHEL-84557]
- sctp: Avoid enqueuing addr events redundantly (Xin Long) [RHEL-84557]
- ice: ensure periodic output start time is in the future (Petr Oros) [RHEL-86857]
- ice: fix PHY Clock Recovery availability check (Petr Oros) [RHEL-86857]
- ice: Drop auxbus use for PTP to finalize ice_adapter move (Petr Oros) [RHEL-86857]
- ice: Use ice_adapter for PTP shared data instead of auxdev (Petr Oros) [RHEL-86857]
- ice: Initial support for E825C hardware in ice_adapter (Petr Oros) [RHEL-86857]
- ice: Add ice_get_ctrl_ptp() wrapper to simplify the code (Petr Oros) [RHEL-86857]
- ice: Introduce ice_get_phy_model() wrapper (Petr Oros) [RHEL-86857]
- ice: Enable 1PPS out from CGU for E825C products (Petr Oros) [RHEL-86857]
- ice: Read SDP section from NVM for pin definitions (Petr Oros) [RHEL-86857]
- ice: Disable shared pin on E810 on setfunc (Petr Oros) [RHEL-86857]
- ice: Cache perout/extts requests and check flags (Petr Oros) [RHEL-86857]
- ice: Align E810T GPIO to other products (Petr Oros) [RHEL-86857]
- ice: Add SDPs support for E825C (Petr Oros) [RHEL-86857]
- ice: Implement ice_ptp_pin_desc (Petr Oros) [RHEL-86857]
- ata: libata-sff: Ensure that we cannot write outside the allocated buffer (CKI Backport Bot) [RHEL-81460] {CVE-2025-21738}
- blackhole_dev: convert self-test to KUnit (Hangbin Liu) [RHEL-81699]
- treewide: Switch/rename to timer_delete[_sync]() (Kamal Heib) [RHEL-86552]
- RDMA/cxgb4: Notify rdma stack for IB_EVENT_QP_LAST_WQE_REACHED event (Kamal Heib) [RHEL-86552]
- rdma/cxgb4: Prevent potential integer overflow on 32bit (Kamal Heib) [RHEL-86552]
- ipc/util.c: complete the kernel-doc function descriptions (Rafael Aquini) [RHEL-86254]
- ipc: fix memleak if msg_init_ns failed in create_ipc_ns (Rafael Aquini) [RHEL-86254]
- ipc/msg: replace one-element array with flexible array member (Rafael Aquini) [RHEL-86254]
- do_mq_notify(): saner skb freeing on failures (Rafael Aquini) [RHEL-86254]
- switch netlink_getsockbyfilp() to taking descriptor (Rafael Aquini) [RHEL-86254]
- scsi: core: Clear driver private data when retrying request (Ewan D. Milne) [RHEL-86157]
- scsi: core: Do not retry I/Os during depopulation (Ewan D. Milne) [RHEL-86157]
- scsi: core: Use GFP_NOIO to avoid circular locking dependency (Ewan D. Milne) [RHEL-86157]
- scsi: core: Add passthrough tests for success and no failure definitions (Ewan D. Milne) [RHEL-86157]
- scsi: scsi_debug: Constify sdebug_driver_template (Ewan D. Milne) [RHEL-86157]
- scsi: transport: sas: spi: Fix kernel-doc for exported functions (Ewan D. Milne) [RHEL-86157]
- scsi: scsi_scan: Add kernel-doc for exported function (Ewan D. Milne) [RHEL-86157]
- scsi: scsi_lib: Add kernel-doc for exported functions (Ewan D. Milne) [RHEL-86157]
- scsi: scsi_ioctl: Add kernel-doc for exported functions (Ewan D. Milne) [RHEL-86157]
- scsi: scsi_error: Add kernel-doc for exported functions (Ewan D. Milne) [RHEL-86157]
- scsi: iscsi: Fix redundant response for ISCSI_UEVENT_GET_HOST_STATS request (Ewan D. Milne) [RHEL-86157]
- scsi: scsi_debug: Skip host/bus reset settle delay (Ewan D. Milne) [RHEL-86157]
- scsi: core: Constify 'struct bin_attribute' (Ewan D. Milne) [RHEL-86157]
- scsi: iscsi: Remove unused iscsi_create_session() (Ewan D. Milne) [RHEL-86157]
- scsi: Eliminate scsi_register() and scsi_unregister() usage & docs (Ewan D. Milne) [RHEL-86157]
- scsi: docs: Remove init_this_scsi_driver() (Ewan D. Milne) [RHEL-86157]
- scsi: core: Remove the .slave_configure() method (Ewan D. Milne) [RHEL-86157]
- scsi: Convert SCSI drivers to .sdev_configure() (Ewan D. Milne) [RHEL-86157]
- scsi: Rename .device_configure() into .sdev_configure() (Ewan D. Milne) [RHEL-86157]
- scsi: Rename .slave_alloc() and .slave_destroy() (Ewan D. Milne) [RHEL-86157]
- scsi: scsi_debug: Fix hrtimer support for ndelay (Ewan D. Milne) [RHEL-86157]
- scsi: bsg: Replace zero-length array with flexible array member (Ewan D. Milne) [RHEL-86157]
- scsi: sg: Enable runtime power management (Ewan D. Milne) [RHEL-86157]
- scsi: Switch back to struct platform_driver::remove() (Ewan D. Milne) [RHEL-86157]
- scsi: libfcoe: Include <linux/prandom.h> instead of <linux/random.h> (Ewan D. Milne) [RHEL-86157]
- crypto: ccp - Fix uAPI definitions of PSP errors (Vladis Dronov) [RHEL-85132]
- crypto: ccp - Add support for PCI device 0x1134 (Vladis Dronov) [RHEL-85132]
- crypto: ccp - Fix check for the primary ASP device (Vladis Dronov) [RHEL-85132]
- crypto: ccp: Add external API interface for PSP module initialization (Vladis Dronov) [RHEL-85132]
- crypto: ccp - Use scoped guard for mutex (Vladis Dronov) [RHEL-85132]
- crypto: drivers - Switch back to struct platform_driver::remove() (Vladis Dronov) [RHEL-85132]
- vsock/virtio: discard packets if the transport changes (CKI Backport Bot) [RHEL-77231] {CVE-2025-21669}
- sched/deadline: Use online cpus for validating runtime (Phil Auld) [RHEL-85609]
- sched/rt: Update limit of sched_rt sysctl in documentation (Phil Auld) [RHEL-85609]
- rtla/tests: Test setting default options (Tomas Glozar) [RHEL-73865]
- rtla/tests: Reset osnoise options before check (Tomas Glozar) [RHEL-73865]
- rtla: Always set all tracer options (Tomas Glozar) [RHEL-73865]
- rtla/osnoise: Set OSNOISE_WORKLOAD to true (Tomas Glozar) [RHEL-73865]
- rtla: Unify apply_config between top and hist (Tomas Glozar) [RHEL-73865]
- rtla/osnoise: Unify params struct (Tomas Glozar) [RHEL-73865]
- redhat/kernel.spec.template: Build rtla with BPF sample collection (Tomas Glozar) [RHEL-77357]
- tools/build: Use SYSTEM_BPFTOOL for system bpftool (Tomas Glozar) [RHEL-77357]
- rtla/timerlat: Test BPF mode (Tomas Glozar) [RHEL-77357]
- rtla/timerlat_top: Use BPF to collect samples (Tomas Glozar) [RHEL-77357]
- rtla/timerlat_top: Move divisor to update (Tomas Glozar) [RHEL-77357]
- rtla/timerlat_hist: Use BPF to collect samples (Tomas Glozar) [RHEL-77357]
- rtla/timerlat: Add BPF skeleton to collect samples (Tomas Glozar) [RHEL-77357]
- rtla: Add optional dependency on BPF tooling (Tomas Glozar) [RHEL-77357]
- tools/build: Add bpftool-skeletons feature test (Tomas Glozar) [RHEL-77357]
- rtla/timerlat: Unify params struct (Tomas Glozar) [RHEL-77357]
- trace/osnoise: Add trace events for samples (Tomas Glozar) [RHEL-77357]
- tools/rtla: Add basic test suite (Tomas Glozar) [RHEL-77357]
- mm: page_owner: use new iteration API (Luiz Capitulino) [RHEL-68306]
- mm: page_table_check: use new iteration API (Luiz Capitulino) [RHEL-68306]
- mm: page_ext: add an iteration API for page extensions (Luiz Capitulino) [RHEL-68306]
- cgroup: Remove steal time from usage_usec (Waiman Long) [RHEL-80669]
- nfsd: Set technology preview if inter SSC offload is enabled (Benjamin Coddington) [RHEL-79657]
- tools/rv: Keep user LDFLAGS in build (Tomas Glozar) [RHEL-71546]
- tunnels: Accept PACKET_HOST in skb_tunnel_check_pmtu(). (Guillaume Nault) [RHEL-57735]
- pfifo_tail_enqueue: Drop new packet when sch->limit == 0 (CKI Backport Bot) [RHEL-80057] {CVE-2025-21702}
- selftests: livepatch: handle PRINTK_CALLER in check_result() (Denis Aleksandrov) [RHEL-85304]
- livepatch: convert timeouts to secs_to_jiffies() (Denis Aleksandrov) [RHEL-85304]
- selftests: livepatch: add test cases of stack_order sysfs interface (Denis Aleksandrov) [RHEL-85304]
- livepatch: Add stack_order sysfs attribute (Denis Aleksandrov) [RHEL-85304]
- selftests/livepatch: Replace hardcoded module name with variable in test-callbacks.sh (Denis Aleksandrov) [RHEL-85304]
- net: gso: fix ownership in __udp_gso_segment (Antoine Tenart) [RHEL-82260] {CVE-2025-21926}
Resolves: RHEL-57735, RHEL-62687, RHEL-68306, RHEL-71546, RHEL-73865, RHEL-74378, RHEL-74484, RHEL-74652, RHEL-76566, RHEL-77231, RHEL-77357, RHEL-79657, RHEL-80057, RHEL-80669, RHEL-81460, RHEL-81534, RHEL-81699, RHEL-82260, RHEL-84557, RHEL-85132, RHEL-85304, RHEL-85609, RHEL-85957, RHEL-86157, RHEL-86254, RHEL-86552, RHEL-86641, RHEL-86857, RHEL-87160, RHEL-87213, RHEL-87244, RHEL-87453, RHEL-87716

Signed-off-by: Julio Faracco <jfaracco@redhat.com>
2025-04-28 23:54:41 -03:00

1097 lines
39 KiB
Python
Executable File

#!/usr/bin/env python3
"""
filter kmods into groups for packaging, see filtermods.adoc
"""
import argparse
import os
import re
import subprocess
import sys
import yaml
import unittest
from logging import getLogger, DEBUG, INFO, WARN, ERROR, CRITICAL, NOTSET, FileHandler, StreamHandler, Formatter, Logger
from typing import Optional
log = getLogger('filtermods')
def get_td(filename):
script_dir = os.path.dirname(os.path.realpath(__file__))
return os.path.join(script_dir, 'filtermods-testdata', filename)
def run_command(cmd, cwddir=None):
p = subprocess.Popen(cmd, cwd=cwddir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
out, err = p.communicate()
out_str = out.decode('utf-8')
err_str = err.decode('utf-8')
return p.returncode, out_str, err_str
def safe_run_command(cmd, cwddir=None):
log.info('%s', cmd)
retcode, out, err = run_command(cmd, cwddir)
if retcode != 0:
log.warning('Command failed: %s, ret_code: %d', cmd, retcode)
log.warning(out)
log.warning(err)
raise Exception(err)
log.info(' ^^[OK]')
return retcode, out, err
def setup_logging(log_filename, stdout_log_level):
log_format = '%(asctime)s %(levelname)7.7s %(funcName)20.20s:%(lineno)4s %(message)s'
log = getLogger('filtermods')
log.setLevel(DEBUG)
handler = StreamHandler(sys.stdout)
formatter = Formatter(log_format, '%H:%M:%S')
handler.setFormatter(formatter)
handler.setLevel(stdout_log_level)
log.addHandler(handler)
log.debug('stdout logging on')
if log_filename:
file_handler = FileHandler(log_filename, 'w')
file_handler.setFormatter(formatter)
file_handler.setLevel(DEBUG)
log.addHandler(file_handler)
log.info('file logging on: %s', log_filename)
return log
def canon_modname(kmod_pathname: str) -> str:
name = os.path.basename(kmod_pathname)
if name.endswith('.xz'):
name = name[:-3]
return name
class HierarchyObject:
def __init__(self):
self.depends_on = set()
def get_topo_order(obj_list: list[HierarchyObject], func_get_linked_objs=lambda x: x.depends_on) -> list[HierarchyObject]:
topo_order = []
objs_to_sort = set(obj_list)
objs_sorted = set()
while len(objs_to_sort) > 0:
no_deps = set()
for obj in objs_to_sort:
linked = func_get_linked_objs(obj)
if not linked:
no_deps.add(obj)
else:
all_deps_sorted = True
for dep in linked:
if dep not in objs_sorted:
all_deps_sorted = False
break
if all_deps_sorted:
no_deps.add(obj)
for obj in no_deps:
topo_order.append(obj)
objs_sorted.add(obj)
objs_to_sort.remove(obj)
return topo_order
class KMod(HierarchyObject):
def __init__(self, kmod_pathname: str) -> None:
super(KMod, self).__init__()
self.name: str = canon_modname(kmod_pathname)
self.kmod_pathname: str = kmod_pathname
self.is_dependency_for: set[KMod] = set()
self.assigned_to_pkg: Optional[KModPackage] = None
self.preferred_pkg: Optional[KModPackage] = None
self.rule_specifity: int = 0
self.allowed_list: Optional[set[KModPackage]] = None
self.err = 0
def __str__(self):
depends_on = ''
for kmod in self.depends_on:
depends_on = depends_on + ' ' + kmod.name
return '%s {%s}' % (self.name, depends_on)
class KModList():
def __init__(self) -> None:
self.name_to_kmod_map: dict[str, KMod] = {}
self.topo_order: Optional[list[KMod]] = None
def get(self, kmod_pathname, create_if_missing=False):
kmod_name = canon_modname(kmod_pathname)
if kmod_name in self.name_to_kmod_map:
return self.name_to_kmod_map[kmod_name]
if not create_if_missing:
return None
kmod = KMod(kmod_pathname)
# log.debug('Adding kmod %s (%s) to list', kmod.name, kmod.kmod_pathname)
if kmod.kmod_pathname != kmod_pathname:
raise Exception('Already have %s, but path changed? %s', kmod_name, kmod_pathname)
if not kmod.name:
raise Exception('Each kmod needs a name')
self.name_to_kmod_map[kmod_name] = kmod
return kmod
def process_depmod_line(self, line):
tmp = line.split(':')
if len(tmp) != 2:
raise Exception('Depmod line has unexpected format: %s', line)
kmod_pathname = tmp[0].strip()
dependencies_pathnames = tmp[1].strip()
kmod = self.get(kmod_pathname, create_if_missing=True)
if dependencies_pathnames:
for dep_pathname in dependencies_pathnames.split(' '):
dep_kmod = self.get(dep_pathname, create_if_missing=True)
kmod.depends_on.add(dep_kmod)
dep_kmod.is_dependency_for.add(kmod)
def load_depmod_file(self, filepath):
with open(filepath) as f:
lines = f.readlines()
for line in lines:
if not line or line.startswith('#'):
continue
self.process_depmod_line(line)
log.info('depmod %s loaded, number of kmods: %s', filepath, len(self.name_to_kmod_map))
def dump(self):
for kmod in self.name_to_kmod_map.values():
print(kmod)
def get_topo_order(self):
if self.topo_order is None:
self.topo_order = get_topo_order(self.name_to_kmod_map.values())
# TODO: what if we add something after?
return self.topo_order
def get_alphabetical_order(self):
kmods = list(self.name_to_kmod_map.values())
kmods.sort(key=lambda k: k.kmod_pathname)
return kmods
def load_kmods_from_dir(self, topdir):
ret = []
for root, dirs, files in os.walk(topdir):
for filename in files:
if filename.endswith('.xz'):
filename = filename[:-3]
if filename.endswith('.ko'):
kmod_pathname = os.path.join(root, filename)
ret.append(kmod_pathname)
return ret
def check_depmod_has_all_kmods(self, dirpath):
ret = self.load_kmods_from_dir(dirpath)
for kmod_pathname in ret:
kmod = self.get(kmod_pathname)
if not kmod:
raise Exception('Could not find kmod %s in depmod', kmod_pathname)
log.debug('OK: all (%s) kmods from %s are known', len(ret), dirpath)
class KModPackage(HierarchyObject):
def _get_depends_on(pkg):
return pkg.depends_on
def _get_deps_for(pkg):
return pkg.is_dependency_for
def __init__(self, name: str, depends_on=[]) -> None:
self.name: str = name
self.depends_on: set[KModPackage] = set(depends_on)
self.is_dependency_for: set[KModPackage] = set()
for pkg in self.depends_on:
pkg.is_dependency_for.add(self)
self.all_depends_on_list: list[KModPackage] = self._get_all_linked(KModPackage._get_depends_on)
self.all_depends_on: set[KModPackage] = set(self.all_depends_on_list)
self.all_deps_for: Optional[set[KModPackage]] = None
self.default = False
log.debug('KModPackage created %s, depends_on: %s', name, [pkg.name for pkg in depends_on])
def __repr__(self):
return self.name
def get_all_deps_for(self):
if self.all_deps_for is None:
self.all_deps_for = set(self._get_all_linked(KModPackage._get_deps_for))
return self.all_deps_for
def _get_all_linked(self, func_get_links):
ret = []
explore = func_get_links(self)
while len(explore) > 0:
new_explore = set()
for pkg in explore:
if pkg not in ret:
ret.append(pkg)
for dep in func_get_links(pkg):
new_explore.add(dep)
explore = new_explore
return ret
class KModPackageList(HierarchyObject):
def __init__(self) -> None:
self.name_to_obj: dict[str, KModPackage] = {}
self.kmod_pkg_list: list[KModPackage] = []
self.rules: list[tuple[str, str, str]] = []
def get(self, pkgname):
if pkgname in self.name_to_obj:
return self.name_to_obj[pkgname]
return None
def add_kmod_pkg(self, pkg):
self.name_to_obj[pkg.name] = pkg
self.kmod_pkg_list.append(pkg)
def __iter__(self):
return iter(self.kmod_pkg_list)
def get_kmods_matching_re(kmod_list: KModList, param_re: str) -> list[KMod]:
ret = []
# first subdir can be anything - this is because during build everything
# goes to kernel, but subpackages can move it (e.g. to extra)
param_re = '[^/]+/' + param_re
pattern = re.compile(param_re)
for kmod in kmod_list.get_topo_order():
m = pattern.match(kmod.kmod_pathname)
if m:
ret.append(kmod)
return ret
def walk_kmod_chain(kmod, myfunc):
visited = set()
def visit_kmod(kmod, parent_kmod, func_to_call):
func_to_call(kmod, parent_kmod)
visited.add(kmod)
for dep in kmod.depends_on:
if dep not in visited:
visit_kmod(dep, kmod, func_to_call)
visit_kmod(kmod, None, myfunc)
return visited
# is pkg a parent to any pkg from "alist"
def is_pkg_parent_to_any(pkg: KModPackage, alist: set[KModPackage]) -> bool:
if pkg in alist:
return True
for some_pkg in alist:
if some_pkg in pkg.all_depends_on:
return True
return False
# is pkg a child to any pkg from "alist"
def is_pkg_child_to_any(pkg: KModPackage, alist: set[KModPackage]) -> bool:
if pkg in alist:
return True
for some_pkg in alist:
if pkg in some_pkg.all_depends_on:
return True
return False
def update_allowed(kmod: KMod, visited: set[KMod], update_linked: bool = False) -> int:
num_updated = 0
init = False
to_remove = set()
if kmod in visited:
return num_updated
visited.add(kmod)
# if we have nothing, try to initialise based on parents and children
if kmod.allowed_list is None:
init_allowed_list: set[KModPackage] = set()
# init from children
for kmod_dep in kmod.depends_on:
if kmod_dep.allowed_list:
init_allowed_list.update(kmod_dep.allowed_list)
init = True
if init:
# also add any pkgs that pkgs from list could depend on
deps_for = set()
for pkg in init_allowed_list:
deps_for.update(pkg.get_all_deps_for())
init_allowed_list.update(deps_for)
# init from parents
if not init:
for kmod_par in kmod.is_dependency_for:
if kmod_par.allowed_list:
init_allowed_list.update(kmod_par.allowed_list)
# also add any pkgs that depend on pkgs from list
for pkg in kmod_par.allowed_list:
init_allowed_list.update(pkg.all_depends_on)
init = True
if init:
kmod.allowed_list = init_allowed_list
log.debug('%s: init to %s', kmod.name, [x.name for x in kmod.allowed_list])
kmod_allowed_list = kmod.allowed_list or set()
# log.debug('%s: update to %s', kmod.name, [x.name for x in kmod_allowed_list])
# each allowed is parent to at least one child allowed [for _all_ children]
for pkg in kmod_allowed_list:
for kmod_dep in kmod.depends_on:
if kmod_dep.allowed_list is None or kmod_dep.err:
continue
if not is_pkg_parent_to_any(pkg, kmod_dep.allowed_list):
to_remove.add(pkg)
log.debug('%s: remove %s from allowed, child: %s [%s]',
kmod.name, [pkg.name], kmod_dep.name, [x.name for x in kmod_dep.allowed_list])
# each allowed is child to at least one parent allowed [for _all_ parents]
for pkg in kmod_allowed_list:
for kmod_par in kmod.is_dependency_for:
if kmod_par.allowed_list is None or kmod_par.err:
continue
if not is_pkg_child_to_any(pkg, kmod_par.allowed_list):
to_remove.add(pkg)
log.debug('%s: remove %s from allowed, parent: %s %s',
kmod.name, [pkg.name], kmod_par.name, [x.name for x in kmod_par.allowed_list])
for pkg in to_remove:
kmod_allowed_list.remove(pkg)
num_updated = num_updated + 1
if len(kmod_allowed_list) == 0:
log.error('%s: cleared entire allow list', kmod.name)
kmod.err = 1
if init or to_remove or update_linked:
if to_remove:
log.debug('%s: updated to %s', kmod.name, [x.name for x in kmod_allowed_list])
for kmod_dep in kmod.depends_on:
num_updated = num_updated + update_allowed(kmod_dep, visited)
for kmod_dep in kmod.is_dependency_for:
num_updated = num_updated + update_allowed(kmod_dep, visited)
return num_updated
def apply_initial_labels(pkg_list: KModPackageList, kmod_list: KModList, treat_default_as_wants=False):
log.debug('')
for cur_rule in ['needs', 'wants', 'default']:
for package_name, rule_type, rule in pkg_list.rules:
pkg_obj = pkg_list.get(package_name)
if not pkg_obj:
log.error('no package with name %s', package_name)
if cur_rule != rule_type:
continue
if rule_type == 'default' and treat_default_as_wants:
rule_type = 'wants'
if 'needs' == rule_type:
# kmod_matching is already in topo_order
kmod_matching = get_kmods_matching_re(kmod_list, rule)
for kmod in kmod_matching:
if kmod.assigned_to_pkg and kmod.assigned_to_pkg != pkg_obj:
log.error('%s: can not be required by 2 pkgs %s %s', kmod.name, kmod.assigned_to_pkg, pkg_obj.name)
else:
kmod.assigned_to_pkg = pkg_obj
kmod.allowed_list = set([pkg_obj])
kmod.rule_specifity = len(kmod_matching)
log.debug('%s: needed by %s', kmod.name, [pkg_obj.name])
if 'wants' == rule_type:
# kmod_matching is already in topo_order
kmod_matching = get_kmods_matching_re(kmod_list, rule)
for kmod in kmod_matching:
if kmod.allowed_list is None:
kmod.allowed_list = set(pkg_obj.all_depends_on)
kmod.allowed_list.add(pkg_obj)
kmod.preferred_pkg = pkg_obj
kmod.rule_specifity = len(kmod_matching)
log.debug('%s: wanted by %s, init allowed to %s', kmod.name, [pkg_obj.name], [pkg.name for pkg in kmod.allowed_list])
else:
if kmod.assigned_to_pkg:
log.debug('%s: ignoring wants by %s, already assigned to %s', kmod.name, pkg_obj.name, kmod.assigned_to_pkg.name)
else:
# rule specifity may not be good idea, so just log it
# e.g. .*test.* may not be more specific than arch/x86/.*
log.debug('already have wants for %s %s, new rule: %s', kmod.name, kmod.preferred_pkg, rule)
if 'default' == rule_type:
pkg_obj.default = True
def settle(kmod_list: KModList) -> None:
kmod_topo_order = list(kmod_list.get_topo_order())
for i in range(0, 25):
log.debug('settle start %s', i)
ret = 0
for kmod in kmod_topo_order:
visited: set[KMod] = set()
ret = ret + update_allowed(kmod, visited)
log.debug('settle %s updated nodes: %s', i, ret)
if ret == 0:
break
kmod_topo_order.reverse()
# phase 1 - propagate initial labels
def propagate_labels_1(pkg_list: KModPackageList, kmod_list: KModList):
log.info('')
settle(kmod_list)
def pick_closest_to_preffered(preferred_pkg: KModPackage, allowed_set: set[KModPackage]):
for child in preferred_pkg.all_depends_on_list:
if child in allowed_set:
return child
return None
# phase 2 - if some kmods allow more than one pkg, pick wanted package
def propagate_labels_2(pkg_list: KModPackageList, kmod_list: KModList):
log.info('')
ret = 0
for kmod in kmod_list.get_topo_order():
update_linked = False
if kmod.allowed_list is None and kmod.preferred_pkg:
log.error('%s: has no allowed list but has preferred_pkg %s', kmod.name, kmod.preferred_pkg.name)
kmod.err = 1
if kmod.allowed_list and kmod.preferred_pkg:
chosen_pkg = None
if kmod.preferred_pkg in kmod.allowed_list:
chosen_pkg = kmod.preferred_pkg
else:
chosen_pkg = pick_closest_to_preffered(kmod.preferred_pkg, kmod.allowed_list)
if chosen_pkg is not None:
kmod.allowed_list = set([chosen_pkg])
log.debug('%s: making to prefer %s (preffered is %s), allowed: %s', kmod.name, chosen_pkg.name,
kmod.preferred_pkg.name, [pkg.name for pkg in kmod.allowed_list])
update_linked = True
visited: set[KMod] = set()
ret = ret + update_allowed(kmod, visited, update_linked)
log.debug('updated nodes: %s', ret)
settle(kmod_list)
# Is this the best pick? ¯\_(ツ)_/¯
def pick_topmost_allowed(allowed_set: set[KModPackage]) -> KModPackage:
topmost = next(iter(allowed_set))
for pkg in allowed_set:
if len(pkg.all_depends_on) > len(topmost.all_depends_on):
topmost = pkg
return topmost
# phase 3 - assign everything else that remained
def propagate_labels_3(pkg_list: KModPackageList, kmod_list: KModList):
log.info('')
ret = 0
kmod_topo_order = list(kmod_list.get_topo_order())
# do reverse topo order to cover children faster
kmod_topo_order.reverse()
default_pkg = None
default_name = ''
for pkg_obj in pkg_list:
if pkg_obj.default:
if default_pkg:
log.error('Already have default pkg: %s / %s', default_pkg.name, pkg_obj.name)
else:
default_pkg = pkg_obj
default_name = default_pkg.name
for kmod in kmod_topo_order:
update_linked = False
chosen_pkg = None
if kmod.allowed_list is None:
if default_pkg:
chosen_pkg = default_pkg
else:
log.error('%s not assigned and there is no default', kmod.name)
elif len(kmod.allowed_list) > 1:
if default_pkg:
if default_pkg in kmod.allowed_list:
chosen_pkg = default_pkg
else:
chosen_pkg = pick_closest_to_preffered(default_pkg, kmod.allowed_list)
if chosen_pkg:
log.debug('closest is %s', chosen_pkg.name)
if not chosen_pkg:
# multiple pkgs are allowed, but none is preferred or default
chosen_pkg = pick_topmost_allowed(kmod.allowed_list)
log.debug('topmost is %s', chosen_pkg.name)
if chosen_pkg:
kmod.allowed_list = set([chosen_pkg])
log.debug('%s: making to prefer %s (default: %s)', kmod.name, [chosen_pkg.name], default_name)
update_linked = True
visited: set[KMod] = set()
ret = ret + update_allowed(kmod, visited, update_linked)
log.debug('updated nodes: %s', ret)
settle(kmod_list)
def load_config(config_pathname: str, kmod_list: KModList, variants=[]):
kmod_pkg_list = KModPackageList()
with open(config_pathname, 'r') as file:
yobj = yaml.safe_load(file)
for pkg_dict in yobj['packages']:
pkg_name = pkg_dict['name']
depends_on = pkg_dict.get('depends-on', [])
if_variant_in = pkg_dict.get('if_variant_in')
if if_variant_in is not None:
if not (set(variants) & set(if_variant_in)):
log.debug('Skipping %s for variants %s', pkg_name, variants)
continue
pkg_dep_list = []
for pkg_dep_name in depends_on:
pkg_dep = kmod_pkg_list.get(pkg_dep_name)
pkg_dep_list.append(pkg_dep)
pkg_obj = kmod_pkg_list.get(pkg_name)
if not pkg_obj:
pkg_obj = KModPackage(pkg_name, pkg_dep_list)
kmod_pkg_list.add_kmod_pkg(pkg_obj)
else:
log.error('package %s already exists?', pkg_name)
rules_list = yobj.get('rules', [])
for rule_dict in rules_list:
if_variant_in = rule_dict.get('if_variant_in')
exact_pkg = rule_dict.get('exact_pkg')
for key, value in rule_dict.items():
if key in ['if_variant_in', 'exact_pkg']:
continue
if if_variant_in is not None:
if not (set(variants) & set(if_variant_in)):
continue
rule = key
package_name = value
if not kmod_pkg_list.get(package_name):
raise Exception('Unknown package ' + package_name)
rule_type = 'wants'
if exact_pkg is True:
rule_type = 'needs'
elif key == 'default':
rule_type = 'default'
rule = '.*'
log.debug('found rule: %s', (package_name, rule_type, rule))
kmod_pkg_list.rules.append((package_name, rule_type, rule))
log.info('loaded config, rules: %s', len(kmod_pkg_list.rules))
return kmod_pkg_list
def make_pictures(pkg_list: KModPackageList, kmod_list: KModList, filename: str, print_allowed=True):
f = open(filename + '.dot', 'w')
f.write('digraph {\n')
f.write('node [style=filled fillcolor="#f8f8f8"]\n')
f.write(' subgraph kmods {\n')
f.write(' "Legend" [shape=note label="kmod name\\n{desired package}\\nresulting package(s)"]\n')
for kmod in kmod_list.get_topo_order():
pkg_name = ''
attr = ''
if kmod.assigned_to_pkg:
attr = 'fillcolor="#eddad5" color="#b22800"'
pkg_name = kmod.assigned_to_pkg.name + "!"
if kmod.preferred_pkg:
attr = 'fillcolor="#ddddf5" color="#b268fe"'
pkg_name = kmod.preferred_pkg.name + "?"
allowed = ''
if kmod.allowed_list and print_allowed:
allowed = '=' + ' '.join([pkg.name for pkg in kmod.allowed_list])
f.write(' "%s" [label="%s\\n%s\\n%s" shape=box %s] \n' % (kmod.name, kmod.name, pkg_name, allowed, attr))
for kmod in kmod_list.get_topo_order():
for kmod_dep in kmod.depends_on:
f.write(' "%s" -> "%s";\n' % (kmod.name, kmod_dep.name))
f.write(' }\n')
f.write(' subgraph packages {\n')
for pkg in pkg_list:
desc = ''
if pkg.default:
desc = '/default'
f.write(' "%s" [label="%s\\n%s"] \n' % (pkg.name, pkg.name, desc))
for pkg_dep in pkg.depends_on:
f.write(' "%s" -> "%s";\n' % (pkg.name, pkg_dep.name))
f.write(' }\n')
f.write('}\n')
f.close()
# safe_run_command('dot -Tpng -Gdpi=150 %s.dot > %s.png' % (filename, filename))
safe_run_command('dot -Tsvg %s.dot > %s.svg' % (filename, filename))
def sort_kmods(depmod_pathname: str, config_str: str, variants=[], do_pictures=''):
log.info('%s %s', depmod_pathname, config_str)
kmod_list = KModList()
kmod_list.load_depmod_file(depmod_pathname)
pkg_list = load_config(config_str, kmod_list, variants)
basename = os.path.splitext(config_str)[0]
apply_initial_labels(pkg_list, kmod_list)
if '0' in do_pictures:
make_pictures(pkg_list, kmod_list, basename + "_0", print_allowed=False)
try:
propagate_labels_1(pkg_list, kmod_list)
if '1' in do_pictures:
make_pictures(pkg_list, kmod_list, basename + "_1")
propagate_labels_2(pkg_list, kmod_list)
propagate_labels_3(pkg_list, kmod_list)
finally:
if 'f' in do_pictures:
make_pictures(pkg_list, kmod_list, basename + "_f")
return pkg_list, kmod_list
def abbrev_list_for_report(alist: list[KMod]) -> str:
tmp_str = []
for kmod in alist:
if kmod.allowed_list:
tmp_str.append('%s(%s)' % (kmod.name, ' '.join([x.name for x in kmod.allowed_list])))
ret = ', '.join(tmp_str)
return ret
def print_report(pkg_list: KModPackageList, kmod_list: KModList):
log.info('*'*26 + ' REPORT ' + '*'*26)
kmods_err = 0
kmods_moved = 0
kmods_good = 0
for kmod in kmod_list.get_topo_order():
if not kmod.allowed_list:
log.error('%s: not assigned to any package! Please check the full log for details', kmod.name)
kmods_err = kmods_err + 1
continue
if len(kmod.allowed_list) > 1:
log.error('%s: assigned to more than one package! Please check the full log for details', kmod.name)
kmods_err = kmods_err + 1
continue
if not kmod.preferred_pkg:
# config doesn't care where it ended up
kmods_good = kmods_good + 1
continue
if kmod.preferred_pkg in kmod.allowed_list:
# it ended up where it needs to be
kmods_good = kmods_good + 1
continue
bad_parent_list = []
for kmod_parent in kmod.is_dependency_for:
if not is_pkg_child_to_any(kmod.preferred_pkg, kmod_parent.allowed_list):
bad_parent_list.append(kmod_parent)
bad_child_list = []
for kmod_child in kmod.depends_on:
if not is_pkg_parent_to_any(kmod.preferred_pkg, kmod_child.allowed_list):
bad_child_list.append(kmod_parent)
log.info('%s: wanted by %s but ended up in %s', kmod.name, [kmod.preferred_pkg.name], [pkg.name for pkg in kmod.allowed_list])
if bad_parent_list:
log.info('\thas conflicting parent: %s', abbrev_list_for_report(bad_parent_list))
if bad_child_list:
log.info('\thas conflicting children: %s', abbrev_list_for_report(bad_child_list))
kmods_moved = kmods_moved + 1
log.info('No. of kmod(s) assigned to preferred package: %s', kmods_good)
log.info('No. of kmod(s) moved to a related package: %s', kmods_moved)
log.info('No. of kmod(s) which could not be assigned: %s', kmods_err)
log.info('*'*60)
return kmods_err
def write_modules_lists(path_prefix: str, pkg_list: KModPackageList, kmod_list: KModList):
kmod_list_alphabetical = sorted(kmod_list.get_topo_order(), key=lambda x: x.kmod_pathname)
for pkg in pkg_list:
output_path = os.path.join(path_prefix, pkg.name + '.list')
i = 0
with open(output_path, "w") as file:
for kmod in kmod_list_alphabetical:
if kmod.allowed_list and pkg in kmod.allowed_list:
file.write(kmod.kmod_pathname)
file.write('\n')
i = i + 1
log.info('Module list %s created with %s kmods', output_path, i)
class FiltermodTests(unittest.TestCase):
do_pictures = ''
def setUp(self):
self.pkg_list = None
self.kmod_list = None
def _is_kmod_pkg(self, kmodname, pkgnames):
self.assertIsNotNone(self.pkg_list)
self.assertIsNotNone(self.kmod_list)
if type(pkgnames) is str:
pkgnames = [pkgnames]
expected_pkgs = []
for pkgname in pkgnames:
pkg = self.pkg_list.get(pkgname)
self.assertIsNotNone(pkg)
expected_pkgs.append(pkg)
kmod = self.kmod_list.get(kmodname)
self.assertIsNotNone(kmod)
if expected_pkgs:
self.assertTrue(len(kmod.allowed_list) == 1)
self.assertIn(next(iter(kmod.allowed_list)), expected_pkgs)
else:
self.assertEqual(kmod.allowed_list, set())
def test1a(self):
self.pkg_list, self.kmod_list = sort_kmods(get_td('test1.dep'), get_td('test1.yaml'),
do_pictures=FiltermodTests.do_pictures)
self._is_kmod_pkg('kmod1', 'modules-core')
self._is_kmod_pkg('kmod2', 'modules-core')
self._is_kmod_pkg('kmod3', 'modules')
self._is_kmod_pkg('kmod4', 'modules')
def test1b(self):
self.pkg_list, self.kmod_list = sort_kmods(get_td('test1.dep'), get_td('test1.yaml'),
do_pictures=FiltermodTests.do_pictures,
variants=['rt'])
self.assertIsNotNone(self.pkg_list.get('modules-other'))
self._is_kmod_pkg('kmod1', 'modules-core')
self._is_kmod_pkg('kmod2', 'modules-core')
self._is_kmod_pkg('kmod3', 'modules')
self._is_kmod_pkg('kmod4', 'modules-other')
def test2(self):
self.pkg_list, self.kmod_list = sort_kmods(get_td('test2.dep'), get_td('test2.yaml'),
do_pictures=FiltermodTests.do_pictures)
self._is_kmod_pkg('kmod1', 'modules-extra')
self._is_kmod_pkg('kmod2', 'modules')
self._is_kmod_pkg('kmod3', 'modules-core')
self._is_kmod_pkg('kmod4', 'modules-core')
self._is_kmod_pkg('kmod5', 'modules-core')
self._is_kmod_pkg('kmod6', 'modules-extra')
self._is_kmod_pkg('kmod8', 'modules')
def test3(self):
self.pkg_list, self.kmod_list = sort_kmods(get_td('test3.dep'), get_td('test3.yaml'),
do_pictures=FiltermodTests.do_pictures)
self._is_kmod_pkg('kmod2', ['modules-core', 'modules'])
self._is_kmod_pkg('kmod4', ['modules-core', 'modules-extra'])
self._is_kmod_pkg('kmod5', 'modules-core')
self._is_kmod_pkg('kmod6', 'modules-core')
def test4(self):
self.pkg_list, self.kmod_list = sort_kmods(get_td('test4.dep'), get_td('test4.yaml'),
do_pictures=FiltermodTests.do_pictures)
self._is_kmod_pkg('kmod0', 'modules')
self._is_kmod_pkg('kmod1', 'modules')
self._is_kmod_pkg('kmod2', 'modules')
self._is_kmod_pkg('kmod3', 'modules')
self._is_kmod_pkg('kmod4', 'modules')
self._is_kmod_pkg('kmod5', 'modules')
self._is_kmod_pkg('kmod6', 'modules')
self._is_kmod_pkg('kmod7', 'modules-partner2')
self._is_kmod_pkg('kmod8', 'modules-partner')
self._is_kmod_pkg('kmod9', 'modules-partner')
def _check_preffered_pkg(self, kmodname, pkgname):
kmod = self.kmod_list.get(kmodname)
self.assertIsNotNone(kmod)
self.assertEqual(kmod.preferred_pkg.name, pkgname)
def test5(self):
self.pkg_list, self.kmod_list = sort_kmods(get_td('test5.dep'), get_td('test5.yaml'),
do_pictures=FiltermodTests.do_pictures)
self._check_preffered_pkg('kmod2', 'modules')
self._check_preffered_pkg('kmod3', 'modules-partner')
self._check_preffered_pkg('kmod4', 'modules-partner')
def test6(self):
self.pkg_list, self.kmod_list = sort_kmods(get_td('test6.dep'), get_td('test6.yaml'),
do_pictures=FiltermodTests.do_pictures)
self._is_kmod_pkg('kmod2', 'modules-core')
self._is_kmod_pkg('kmod3', 'modules')
self._is_kmod_pkg('kmod4', 'modules')
self._is_kmod_pkg('kmod1', [])
def test7(self):
self.pkg_list, self.kmod_list = sort_kmods(get_td('test7.dep'), get_td('test7.yaml'),
do_pictures=FiltermodTests.do_pictures)
self._is_kmod_pkg('kmod1', 'modules-core')
self._is_kmod_pkg('kmod2', 'modules-core')
self._is_kmod_pkg('kmod3', 'modules-other')
self._is_kmod_pkg('kmod4', 'modules')
def do_rpm_mapping_test(config_pathname, kmod_rpms):
kmod_dict = {}
def get_kmods_matching_re(pkgname, param_re):
matched = []
param_re = '^kernel/' + param_re
pattern = re.compile(param_re)
for kmod_pathname, kmod_rec in kmod_dict.items():
m = pattern.match(kmod_pathname)
if m:
matched.append(kmod_pathname)
return matched
for kmod_rpm in kmod_rpms.split():
filename = os.path.basename(kmod_rpm)
m = re.match(r'.*-modules-([^-]+)', filename)
if not m:
raise Exception('Unrecognized rpm ' + kmod_rpm + ', expected a kernel-modules* rpm')
pkgname = 'modules-' + m.group(1)
m = re.match(r'modules-([0-9.]+)', pkgname)
if m:
pkgname = 'modules'
tmpdir = os.path.join('tmp.filtermods', filename, pkgname)
if not os.path.exists(tmpdir):
log.info('creating tmp dir %s', tmpdir)
os.makedirs(tmpdir)
safe_run_command('rpm2cpio %s | cpio -id' % (os.path.abspath(kmod_rpm)), cwddir=tmpdir)
else:
log.info('using cached content of tmp dir: %s', tmpdir)
for path, subdirs, files in os.walk(tmpdir):
for name in files:
ret = re.match(r'.*/'+pkgname+'/lib/modules/[^/]+/[^/]+/(.*)', os.path.join(path, name))
if not ret:
continue
kmod_pathname = 'kernel/' + ret.group(1)
if not kmod_pathname.endswith('.xz') and not kmod_pathname.endswith('.ko'):
continue
if kmod_pathname in kmod_dict:
if pkgname not in kmod_dict[kmod_pathname]['target_pkgs']:
kmod_dict[kmod_pathname]['target_pkgs'].append(pkgname)
else:
kmod_dict[kmod_pathname] = {}
kmod_dict[kmod_pathname]['target_pkgs'] = [pkgname]
kmod_dict[kmod_pathname]['pkg'] = None
kmod_dict[kmod_pathname]['matched'] = False
kmod_pkg_list = load_config(config_pathname, None)
for package_name, rule_type, rule in kmod_pkg_list.rules:
kmod_names = get_kmods_matching_re(package_name, rule)
for kmod_pathname in kmod_names:
kmod_rec = kmod_dict[kmod_pathname]
if not kmod_rec['matched']:
kmod_rec['matched'] = True
kmod_rec['pkg'] = package_name
for kmod_pathname, kmod_rec in kmod_dict.items():
if kmod_rec['pkg'] not in kmod_rec['target_pkgs']:
log.warning('kmod %s wanted by config in %s, in tree it is: %s', kmod_pathname, [kmod_rec['pkg']], kmod_rec['target_pkgs'])
elif len(kmod_rec['target_pkgs']) > 1:
# if set(kmod_rec['target_pkgs']) != set(['modules', 'modules-core']):
log.warning('kmod %s multiple matches in tree: %s/%s', kmod_pathname, [kmod_rec['pkg']], kmod_rec['target_pkgs'])
def cmd_sort(options):
do_pictures = ''
if options.graphviz:
do_pictures = '0f'
pkg_list, kmod_list = sort_kmods(options.depmod, options.config,
options.variants, do_pictures)
ret = print_report(pkg_list, kmod_list)
if options.output:
write_modules_lists(options.output, pkg_list, kmod_list)
return ret
def cmd_print_rule_map(options):
kmod_list = KModList()
kmod_list.load_depmod_file(options.depmod)
pkg_list = load_config(options.config, kmod_list, options.variants)
apply_initial_labels(pkg_list, kmod_list, treat_default_as_wants=True)
for kmod in kmod_list.get_alphabetical_order():
print('%-20s %s' % (kmod.preferred_pkg, kmod.kmod_pathname))
def cmd_selftest(options):
if options.graphviz:
FiltermodTests.do_pictures = '0f'
for arg in ['selftest', '-g', '--graphviz']:
if arg in sys.argv:
sys.argv.remove(arg)
unittest.main()
sys.exit(0)
def cmd_cmp2rpm(options):
do_rpm_mapping_test(options.config, options.kmod_rpms)
def main():
global log
parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', dest='verbose',
help='be more verbose', action='count', default=4)
parser.add_argument('-q', '--quiet', dest='quiet',
help='be more quiet', action='count', default=0)
parser.add_argument('-l', '--log-filename', dest='log_filename',
help='log filename', default='filtermods.log')
subparsers = parser.add_subparsers(dest='cmd')
def add_graphviz_arg(p):
p.add_argument('-g', '--graphviz', dest='graphviz',
help='generate graphviz visualizations',
action='store_true', default=False)
def add_config_arg(p):
p.add_argument('-c', '--config', dest='config', required=True,
help='path to yaml config with rules')
def add_depmod_arg(p):
p.add_argument('-d', '--depmod', dest='depmod', required=True,
help='path to modules.dep file')
def add_output_arg(p):
p.add_argument('-o', '--output', dest='output', default=None,
help='output $module_name.list files to directory specified by this parameter')
def add_variants_arg(p):
p.add_argument('-r', '--variants', dest='variants', action='append', default=[],
help='variants to enable in config')
def add_kmod_rpms_arg(p):
p.add_argument('-k', '--kmod-rpms', dest='kmod_rpms', required=True,
help='compare content of specified rpm(s) against yaml config rules')
parser_sort = subparsers.add_parser('sort', help='assign kmods specified by modules.dep using rules from yaml config')
add_config_arg(parser_sort)
add_depmod_arg(parser_sort)
add_output_arg(parser_sort)
add_variants_arg(parser_sort)
add_graphviz_arg(parser_sort)
parser_rule_map = subparsers.add_parser('rulemap', help='print how yaml config maps to kmods')
add_config_arg(parser_rule_map)
add_depmod_arg(parser_rule_map)
add_variants_arg(parser_rule_map)
parser_test = subparsers.add_parser('selftest', help='runs a self-test')
add_graphviz_arg(parser_test)
parser_cmp2rpm = subparsers.add_parser('cmp2rpm', help='compare ruleset against RPM(s)')
add_config_arg(parser_cmp2rpm)
add_kmod_rpms_arg(parser_cmp2rpm)
options = parser.parse_args()
if options.cmd == "selftest":
options.verbose = options.verbose - 2
options.verbose = max(options.verbose - options.quiet, 0)
levels = [NOTSET, CRITICAL, ERROR, WARN, INFO, DEBUG]
stdout_log_level = levels[min(options.verbose, len(levels) - 1)]
log = setup_logging(options.log_filename, stdout_log_level)
ret = 0
if options.cmd == "sort":
ret = cmd_sort(options)
elif options.cmd == "rulemap":
cmd_print_rule_map(options)
elif options.cmd == "selftest":
cmd_selftest(options)
elif options.cmd == "cmp2rpm":
cmd_cmp2rpm(options)
else:
parser.print_help()
return ret
if __name__ == '__main__':
# import profile
# profile.run('main()', sort=1)
sys.exit(main())