[SRU][N/hwe-7.0-lmm][PATCH 1/2] UBUNTU: [Packaging] lmm: Add flavour migration support

Kuan-Ying Lee kuan-ying.lee at canonical.com
Wed Sep 9 08:30:39 UTC 2026


BugLink: https://bugs.launchpad.net/bugs/1786013

When a kernel is retired, the module packages of its flavour need to be
transitioned onto the module packages of the kernel replacing it. Now
that the module packages are built by LMM instead of linux-meta, LMM has
to provide those transitional packages itself.

Add a "migrate <module> <source flavour> <target flavour> <arch>..."
directive to debian/package.config, mirroring the syntax already used by
linux-restricted-modules, and emit an oldlibs transitional package for
every entry.

The transitional packages are only emitted for the signed source package,
as those are the packages users actually have installed. An entry is
skipped, with a note, when the module is not built by this source package,
when the target flavour does not exist, or when the module and the target
flavour have no architecture in common.

Signed-off-by: Kuan-Ying Lee <kuan-ying.lee at canonical.com>
---
 debian/scripts/control_craft.py         | 67 +++++++++++++++++++++++++
 debian/scripts/migration_helper.py      | 54 ++++++++++++++++++++
 debian/scripts/parameterise-ancillaries |  1 +
 3 files changed, 122 insertions(+)
 create mode 100644 debian/scripts/migration_helper.py

diff --git a/debian/scripts/control_craft.py b/debian/scripts/control_craft.py
index fb29d9d33ddd..4ea4a5c158fb 100755
--- a/debian/scripts/control_craft.py
+++ b/debian/scripts/control_craft.py
@@ -6,6 +6,7 @@ import shutil
 
 from dkms_helper import dkms_modules
 from flavour_finder import find_flavours
+from migration_helper import find_migrations
 from variant_helper import find_variants
 
 if sys.version_info >= (3, 9):
@@ -53,6 +54,16 @@ Description: Extra drivers for @DKMS_OLD_NAME at -@KERNEL_FLAVOUR@@KERNEL_VARIANT@
  Transitional package for upgrades of @DKMS_OLD_NAME@ to [@ARCHITECTURE@]
 """
 
+default_migration_package_template = """
+Package: linux-modules- at DKMS_NAME@- at SOURCE_FLAVOUR@
+Architecture: @ARCHITECTURE@
+Section: oldlibs
+Multi-Arch: no
+Depends: linux-modules- at DKMS_NAME@- at TARGET_FLAVOUR@
+Description: Extra drivers for @DKMS_NAME@ for the @SOURCE_FLAVOUR@ flavour (transitional package)
+ Transitional package for upgrades. This package can be safely removed.
+"""
+
 
 def create_new_variant_package(dkms_name:str, archs:str, kernel_flavour:str, kernel_abi:str, unsigned_prefix:str):
     control_variants=""
@@ -146,6 +157,60 @@ def create_transitional_packages(full_template: str, modules, kernel_arch: str,
     return full_template
 
 
+def create_migration_packages(full_template: str, modules,
+                              kernel_flavours: List[str]):
+    """Build the transitional packages that migrate the module packages of a
+    retired kernel (e.g. linux-modules-ipu6-oem-24.04d) onto the module
+    packages built by this source (e.g. linux-modules-ipu6-generic-hwe-24.04).
+
+    Driven by the "migrate <module> <source> <target> <arch>..." lines of
+    debian/package.config.
+    """
+    variants = find_variants()
+    # The target of a migration is a <flavour><variant> pair, which is what
+    # create_new_variant_package() names its packages after.
+    target_flavours = {}
+    for kernel_flavour in kernel_flavours:
+        for variant in variants:
+            target_flavours[kernel_flavour.flavour + variant] = kernel_flavour
+
+    dkms_by_name = {}
+    for item in modules.items:
+        dkms_by_name[item.modulename] = item
+
+    for migration in find_migrations():
+        item = dkms_by_name.get(migration.module)
+        if item is None:
+            print(f"Skipping migration of {migration.module} for "
+                  f"{migration.source}: not built by this source package")
+            continue
+        kernel_flavour = target_flavours.get(migration.target)
+        if kernel_flavour is None:
+            print(f"Skipping migration of {migration.module} for "
+                  f"{migration.source}: no such target {migration.target}")
+            continue
+        if kernel_flavour.flavour in item.skip_flavours:
+            print(f"Skipping migration of {migration.module} for "
+                  f"{migration.source}: skipped for {kernel_flavour.flavour}")
+            continue
+        # The transitional package can only exist on architectures where both
+        # the DKMS module and the target flavour are built.
+        archs = intersect_archs(migration.archs, item.arch)
+        archs = intersect_archs(archs.split(), kernel_flavour.archs)
+        if archs == "":
+            print(f"Skipping migration of {migration.module} for "
+                  f"{migration.source}: no architectures in common")
+            continue
+        template = default_migration_package_template
+        template = re.sub("@DKMS_NAME@", migration.module, template)
+        template = re.sub("@SOURCE_FLAVOUR@", migration.source, template)
+        template = re.sub("@TARGET_FLAVOUR@", migration.target, template)
+        template = re.sub("@ARCHITECTURE@", archs, template)
+        template += "\n"
+        full_template += template
+    return full_template
+
+
 def create_temporary_stub_file(pkgs_dkms_modules: str, is_signed: bool):
     control_file = ""
     if is_signed:
@@ -173,4 +238,6 @@ if is_signed:
     pkgs_dkms_modules = create_transitional_packages(pkgs_dkms_modules,
                                             modules, arg_deb_host_arch,
                                             arg_kernel_abi, flavours)
+    pkgs_dkms_modules = create_migration_packages(pkgs_dkms_modules,
+                                            modules, flavours)
 create_temporary_stub_file(pkgs_dkms_modules, is_signed)
diff --git a/debian/scripts/migration_helper.py b/debian/scripts/migration_helper.py
new file mode 100644
index 000000000000..66953bfaa76a
--- /dev/null
+++ b/debian/scripts/migration_helper.py
@@ -0,0 +1,54 @@
+#! /usr/bin/python3 -B
+
+import sys
+
+if sys.version_info >= (3, 9):
+    List = list
+else:
+    from typing import List
+
+
+class migration_item:
+    module: str = ""
+    source: str = ""
+    target: str = ""
+    archs = []
+
+    def __init__(self, package_config_line: List[str]):
+        self.module = package_config_line[1]
+        self.source = package_config_line[2]
+        self.target = package_config_line[3]
+        self.archs = []
+        for item in package_config_line[4:]:
+            self.archs.append(item.replace("\n", ""))
+
+
+class migrations:
+    items: migration_item = []
+
+    def __init__(self):
+        self.items = []
+        with open("debian/package.config", "r") as fp_dpc:
+            for line in fp_dpc:
+                # Split on runs of whitespace so that the entries may be
+                # column aligned, as they are in the LRM package.config.
+                parms = line.split()
+                if not parms or parms[0] != "migrate":
+                    continue
+                if len(parms) < 5:
+                    raise ValueError(
+                        "debian/package.config: malformed migrate line: "
+                        + line.strip()
+                    )
+                self.items.append(migration_item(parms))
+
+
+def find_migrations():
+    migs = migrations()
+    return migs.items
+
+
+#===============================  DEBUG  =======================================
+if __name__ == "__main__":
+    for m in find_migrations():
+        print(f"{m.module}: {m.source} -> {m.target} [{' '.join(m.archs)}]")
diff --git a/debian/scripts/parameterise-ancillaries b/debian/scripts/parameterise-ancillaries
index 9d0b1343c2a0..dc2d08b9eac9 100755
--- a/debian/scripts/parameterise-ancillaries
+++ b/debian/scripts/parameterise-ancillaries
@@ -52,6 +52,7 @@ def build_ancillary(family, package):
         os.path.join("debian", "scripts", "flavour_finder.py"),
         os.path.join("debian", "scripts", "auto_install.py"),
         os.path.join("debian", "scripts", "control_craft.py"),
+        os.path.join("debian", "scripts", "migration_helper.py"),
         os.path.join("debian", "scripts", "variant_helper.py"),
         os.path.join("debian", "templates", "postinst.in"),
         os.path.join("debian", "templates", "postrm.in"),
-- 
2.43.0




More information about the kernel-team mailing list