[SRU][R][PATCH 0/1] linux: dtbs_install fails on Resolute builders due to uutils install(1) EEXIST race under parallel make (LP: #2166356)

Edoardo Canepa edoardo.canepa at canonical.com
Thu Sep 3 13:55:17 UTC 2026


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

SRU Justification

[Impact]

Kernel source package builds for Resolute (26.04) fail intermittently
during dtbs_install on Launchpad builders. The failure surfaces as:

  INSTALL .../device-tree/nuvoton/ma35d1-iot-512m.dtb
  INSTALL .../device-tree/nuvoton/ma35d1-som-256m.dtb
  INSTALL .../device-tree/nuvoton/nuvoton-npcm845-evb.dtb
  install: cannot create directory '.../device-tree/nuvoton'
  make[4]: *** [scripts/Makefile.dtbinst:23:
      .../nuvoton/ma35d1-iot-512m.dtb] Error 1

The root cause is a race in the rust-coreutils implementation of
install(1), which provides /usr/bin/install on Resolute. In uutils'
`install -D`, parent-directory creation is done by stat_at() followed
by mkdir_at() without retry: two concurrent invocations sharing a
parent both observe NotFound before either issues mkdir_at(), the
loser then receives EEXIST from the kernel and uutils bubbles it up
as a hard error instead of treating an existing directory as success.

scripts/Makefile.dtbinst hard-codes literal `install -D -m 0644`,
one invocation per .dtb file:

  quiet_cmd_dtb_install = INSTALL $@
        cmd_dtb_install = install -D -m 0644 $< $@

Under `make -j`, every .dtb inside a given vendor directory (e.g.
device-tree/apple/, device-tree/nuvoton/) races on that directory's
creation, and the build fails non-deterministically.

Impact:
  - Series: Resolute (26.04) only.
  - Arches: any that enable do_dtbs=true (arm64, armhf); observed
    on arm64 Launchpad builders for linux 7.0.0-37.37.
  - Severity: hard build failure, blocks source package uploads.

[Fix]

Serialise the dtbs_install sub-make by forcing `-j1` for that single
target in debian/rules.d/2-binary-arch.mk. All other targets in the
package build ($(bldimg), modules, modules_install, headers_install)
retain full parallelism via $(conc_level).

A tree-side fix is not viable in the SRU timeframe:

  - scripts/Makefile.dtbinst hard-codes the literal `install`; there
    is no $(INSTALL) variable defined anywhere in the tree to hook.
  - Resolute ships no GNU coreutils install(1) that we could point
    to via an override even if the recipe were parametric.
  - Fixing uutils is out of scope; the upstream fix (uutils/coreutils
    PR #12429) is open but unmerged, and will need to land in
    rust-coreutils before Resolute can benefit.

The workaround is intentionally minimal and self-documenting: an
inline comment references the uutils issues so it can be reverted
cleanly once #12429 lands in Resolute.

[Test Case]

Reproducer (without the patch):

  1. On a Resolute build environment with rust-coreutils providing
     /usr/bin/install (default), obtain the linux source package
     and run:
         dpkg-buildpackage -B -aarm64 -j$(nproc)
  2. Repeat 5-10 times. dtbs_install fails intermittently with
     "install: cannot create directory '.../device-tree/<vendor>'"
     and a non-zero exit from make[4].

Verification (with the patch):

  1. Same environment, same command. Run 10 consecutive builds.
     None fail in dtbs_install.
  2. Confirm the resulting linux-image-* package contains the full
     set of .dtb files under
         usr/lib/firmware/<abi>-<flavour>/device-tree/
     matching the pre-patch layout (diff of installed file lists).
  3. Wall-clock delta for dtbs_install between $(conc_level) and -j1
     is under 10s on a Launchpad builder (I/O-bound, short target).

Additionally, a successful Launchpad rebuild of the affected source
(linux 7.0.0-37.37) with the patch applied is the definitive test.

[Where problems could occur]

The change is one line: swap $(conc_level) for -j1 on the single
`$(kmake) ... dtbs_install` invocation in
debian/rules.d/2-binary-arch.mk. Scope of risk:

  - Build time: dtbs_install runs serially. In practice this is a
    few seconds on arm64/armhf; the target is I/O-bound.
  - No other build stage is touched; all other sub-makes still run
    at $(conc_level).
  - No change to compiler flags, config, ABI, module layout,
    installed artefacts, or package contents.
  - No effect on arches with do_dtbs != true (e.g. amd64), where
    the guarded block is not entered at all.
  - No runtime effect on end users; the change is confined to
    package build.

If regressions do occur, they would manifest at build time (not on
target systems) and can be reverted trivially by restoring
$(conc_level).

[Other Info]

Upstream uutils references:
  - uutils/coreutils#12355 — install -D loses some files to EEXIST
  - uutils/coreutils#12429 — install: fix EEXIST race losing files
                             in parallel install -D (open PR)

This is a SAUCE patch (packaging-only, no upstream analogue) tagged
appropriately in the subject. It should be dropped when uutils
PR #12429 is merged and lands in Resolute's rust-coreutils; the
revert is a one-line change to restore $(conc_level).



More information about the kernel-team mailing list