poky: subtree update:7231c10430..0ac99625bf

Alban Bedel (1):
      systemd: Fix systemd when used with busybox less

Alejandro Hernandez Samaniego (3):
      poky-tiny: Reduce busybox size by 13%
      poky-tiny: Enable size optimization by default
      python3: Update manifest

Alexander Kamensky (1):
      kexec: arm64: disabled check if kaslr-seed dtb property was wiped

Alexander Kanavin (128):
      systemd-boot: upgrade 246.2 -> 246.6
      glib-2.0: upgrade 2.64.5 -> 2.66.1
      cmake: update 3.18.2 -> 3.18.4
      python3-pygobject: upgrade 3.36.1 -> 3.38.0
      libdazzle: upgrade 3.36.0 -> 3.38.0
      gobject-introspection: upgrade 1.64.1 -> 1.66.1
      json-glib: upgrade 1.4.4 -> 1.6.0
      ovmf: update edk2-stable202005 -> edk2-stable202008
      gnu-config: update to latest revision
      file: enable all built-in compression checkers
      rpm: update 4.15.1 -> 4.16.0
      elfutils: update 0.180 -> 0.181
      ghostscript: update 9.52 -> 9.53.3
      ltp: update 20200515 -> 20200930
      gsettings-desktop-schemas: update 3.36.1 -> 3.38.0
      libsecret: update 0.20.3 -> 0.20.4
      mesa: update 20.1.8 -> 20.2.1
      xf86-video-vesa: update 2.4.0 -> 2.5.0
      lttng-modules: update 2.12.2 -> 2.12.3
      webkitgtk: update 2.28.4 -> 2.30.1
      dos2unix: update 7.4.1 -> 7.4.2
      gnutls: update 3.16.4 -> 3.16.5
      libcap: update 2.43 -> 2.44
      vte: update 0.60.3 -> 0.62.1
      libhandy: upgrade 0.0.13 -> 1.0.0
      libportal: add a recipe
      epiphany: upgrade 3.36.4 -> 3.38.1
      gtk-doc: upgrade 1.32 -> 1.33.0
      rpm: adjust MIPS64 N32 support
      apt: remove host contamination with gtest
      opkg-utils: correct priority matching in update-alternatives
      libxml2: add a patch to fix python 3.9 support
      python: update 3.8.5 -> 3.9.0
      glib-2.0: update 2.66.1 -> 2.66.2
      json-glib: fix reproducibility
      spirv-tools: correctly set PV
      spirv-tools: upgrade 2019.5 -> 2020.5
      glslang: fix upstream version check
      glslang: upgrade 8.13.3559 -> 8.13.3743
      glslang: bump to a newer commit
      shaderc: upgrade 2019.0 -> 2020.3
      vulkan: update 1.2.135 -> 1.2.154
      vulkan-samples: replace vulkan-demos
      piglit: upgrade to latest revision
      acpica: upgrade 20200717 -> 20200925
      adwaita-icon-theme: upgrade 3.36.1 -> 3.38.0
      at-spi2-atk: upgrade 2.34.2 -> 2.38.0
      at-spi2-core: upgrade 2.36.1 -> 2.38.0
      bison: upgrade 3.7.2 -> 3.7.3
      createrepo-c: upgrade 0.16.0 -> 0.16.1
      curl: upgrade 7.72.0 -> 7.73.0
      debianutils: upgrade 4.11.1 -> 4.11.2
      dhcpcd: upgrade 9.2.0 -> 9.3.1
      dmidecode: upgrade 3.2 -> 3.3
      dnf: upgrade 4.2.23 -> 4.4.0
      ethtool: upgrade 5.8 -> 5.9
      expat: upgrade 2.2.9 -> 2.2.10
      gcr: upgrade 3.36.0 -> 3.38.0
      glib-networking: upgrade 2.64.3 -> 2.66.0
      gtk+3: upgrade 3.24.22 -> 3.24.23
      help2man: upgrade 1.47.15 -> 1.47.16
      i2c-tools: upgrade 4.1 -> 4.2
      iw: upgrade 5.8 -> 5.9
      kmscube: upgrade to latest revision
      less: upgrade 562 -> 563
      libdnf: upgrade 0.48.0 -> 0.54.2
      libgudev: upgrade 233 -> 234
      libinput: upgrade 1.16.1 -> 1.16.2
      libuv: upgrade 1.39.0 -> 1.40.0
      libva: upgrade 2.8.0 -> 2.9.0
      libva-utils: update 2.8.0 -> 2.9.1
      libwpe: upgrade 1.7.1 -> 1.8.0
      libxkbcommon: upgrade 0.10.0 -> 1.0.1
      openssh: upgrade 8.3p1 -> 8.4p1
      openssl: upgrade 1.1.1g -> 1.1.1h
      strace: upgrade 5.8 -> 5.9
      sudo: upgrade 1.9.3 -> 1.9.3p1
      vala: upgrade 0.48.9 -> 0.50.1
      wpebackend-fdo: upgrade 1.7.1 -> 1.8.0
      xkeyboard-config: upgrade 2.30 -> 2.31
      u-boot: upgrade 2020.07 -> 2020.10
      usbutils: upgrade 012 -> 013
      nfs-utils: upgrade 2.5.1 -> 2.5.2
      dropbear: upgrade 2020.80 -> 2020.81
      btrfs-tools: upgrade 5.7 -> 5.9
      git: upgrade 2.28.0 -> 2.29.2
      go: upgrade 1.15.2 -> 1.15.3
      mtools: upgrade 4.0.24 -> 4.0.25
      python3-numpy: upgrade 1.19.1 -> 1.19.3
      python3-git: upgrade 3.1.7 -> 3.1.11
      python3-pyelftools: upgrade 0.26 -> 0.27
      python3-pygments: upgrade 2.6.1 -> 2.7.2
      python3-setuptools: upgrade 49.6.0 -> 50.3.2
      asciidoc: upgrade 9.0.2 -> 9.0.4
      iptables: upgrade 1.8.5 -> 1.8.6
      libsolv: upgrade 0.7.14 -> 0.7.16
      stress-ng: upgrade 0.11.21 -> 0.11.23
      libhandy: upgrade 1.0.0 -> 1.0.1
      freetype: upgrade 2.10.2 -> 2.10.4
      linux-firmware: upgrade 20200817 -> 20201022
      alsa: upgrade 1.2.3 -> 1.2.4
      gstreamer1.0: upgrade 1.18.0 -> 1.18.1
      x264: upgrade to latest revision
      rt-tests/hwlatdetect: upgrade 1.8 -> 1.9
      webkitgtk: upgrade 2.30.1 -> 2.30.2
      diffoscope: upgrade 160 -> 161
      enchant2: upgrade 2.2.9 -> 2.2.12
      libassuan: upgrade 2.5.3 -> 2.5.4
      libcap-ng: upgrade 0.7.11 -> 0.8
      libevdev: upgrade 1.9.1 -> 1.10.0
      libgcrypt: upgrade 1.8.6 -> 1.8.7
      libmpc: upgrade 1.2.0 -> 1.2.1
      libsoup-2.4: upgrade 2.70.0 -> 2.72.0
      numactl: upgrade 2.0.13 -> 2.0.14
      kea: use odd-even version scheme for updates
      mesa: fix a build race
      clutter-gst-3.0: do not call out to host gstreamer plugin scanner
      conf-notes.txt: mention more important images than just sato
      weston-init: correctly start under systemd
      weston-init: fall back to fbdev under x32
      wayland-utils: introduce a recipe
      poky/conf-notes.txt: mention more important images than just sato
      python3: split python target configuration into own class
      python3-pycairo: use python3targetconfig
      distutils3-base.bbclass: use python3targetconfig
      meta: drop _PYTHON_SYSCONFIGDATA_NAME hacks
      gpgme: use python3targetconfig
      bitbake: lib/bb/fetch2/__init__.py: drop _PYTHON_SYSCONFIGDATA_NAME unsetting

Alexander Vickberg (1):
      socat: make building with OpenSSL support optional

Alistair (1):
      weston-init: Fix incorrect idle-time setting

Andrej Valek (1):
      autotools: CONFIG_SHELL defaults

Andrey Zhizhikin (1):
      insane: add GitLab /archive/ tests

Anibal Limon (1):
      recipes-graphics: libxkbcommon disable build of libxkbregistry

Anuj Mittal (2):
      glib-2.0: RDEPEND on dbusmock only when GI_DATA_ENABLED is True
      distutils-common-base: fix LINKSHARED expansion

Bruce Ashfield (17):
      kernel: provide module.lds for out of tree builds in v5.10+
      linux-yocto/5.8: update to v5.8.15
      linux-yocto/5.4: update to v5.4.71
      linux-yocto/5.8: update to v5.8.16
      linux-yocto/5.4: update to v5.4.72
      linux-yocto/5.8: update to v5.8.17
      linux-yocto/5.4: update to v5.4.73
      linux-yocto-dev: move to v5.10-rc
      linux-yocto/5.4: config cleanup / warnings
      linux-yocto/5.8: config cleanup / warnings
      linux-yocto/5.8: update to v5.8.18
      linux-yocto/5.4: update to v5.4.75
      kernel: relocate copy of module.lds to module compilation task
      linux-yocto/5.4: perf: Alias SYS_futex with SYS_futex_time64 on 32-bit arches with 64bit time_t
      linux-yocto/5.8: perf: Alias SYS_futex with SYS_futex_time64 on 32-bit arches with 64bit time_t
      linux-yocto/5.8: ext4/tipc warning fixups
      linux-yocto/5.4: update to v5.4.78

Chaitanya Vadrevu (1):
      isoimage-isohybrid.py: Support adding files/dirs

Changqing Li (2):
      timezone: upgrade to 2020d
      vulkan-samples: fix do_compile failure

Chee Yang Lee (2):
      bluez5: update to 5.55
      ruby: update to 2.7.2

Chris Laplante (4):
      bitbake: main: extract creation of argument parser into function so it can be utilized externally, e.g. by unit tests
      bitbake: bb.ui: delete __init__.py to make bb.ui a namespace package
      bitbake: cookerdata: tweak to avoid mutable default argument
      cases/bbtests.py: ensure PACKAGE_CLASSES is set to RPM for bbtests.BitbakeTests.test_force_task_1

Dan Callaghan (1):
      gdb: add PACKAGECONFIG for xz (lzma) compression support

Denys Dmytriyenko (1):
      grep: upgrade 3.4 -> 3.5

Denys Zagorui (1):
      binutils: reproducibility: reuse debug-prefix-map for stabs

Federico Pellegrin (1):
      openssl: Add c_rehash to misc package and add perl runtime dependency

Fedor Ross (2):
      sysvinit: remove bashism to be compatible with dash
      eudev: remove bashism to be compatible with dash

Fredrik Gustafsson (1):
      package management: Allow dynamic loading of PM

Gratian Crisan (1):
      kernel-module-split.bbclass: identify kernel modconf files as configuration files

He Zhe (1):
      lttng-modules: Backport a patch to fix btrfs build failure

Hombourger, Cedric (1):
      bitbake: fetch2: use relative symlinks for anything pulled from PREMIRRORS

Hongxu Jia (1):
      bitbake: Revert "bb.ui: delete __init__.py to make bb.ui a namespace package"

INC@Cisco) (1):
      kernel-devsrc: improve reproducibility for arm64

Jason Wessel (2):
      base-files/profile: Add universal resize function
      systemd-serialgetty: Switch to TERM=linux

Jose Quaresma (31):
      spirv-tools: import from meta-oe to OE core
      spirv-tools: enable native build and install more header files
      glslang: add receipe
      shaderc: add receipe
      spirv-tools: fix identation and cleanup install append
      maintainers.inc: Add Jose Quaresma
      gstreamer1.0: Fix reproducibility issue around libcap
      gstreamer1.0: upgrade to version 1.18.0
      gstreamer1.0-plugins-base: upgrade to version 1.18.0
      gstreamer1.0-plugins-base: add new meson option as PACKAGECONFIG
      gstreamer1.0-plugins-good: upgrade to version 1.18.0
      gstreamer1.0-plugins-good: disable new meson options
      gstreamer1.0-plugins-good: add new meson option as PACKAGECONFIG
      gstreamer1.0-plugins-bad: upgrade to version 1.18.0
      gstreamer1.0-plugins-bad: disable new meson options
      gstreamer1.0-plugins-bad: add new meson options as PACKAGECONFIG
      gstreamer1.0-plugins-ugly: upgrade to version 1.18.0
      gstreamer1.0-python: upgrade to version 1.18.0
      gstreamer1.0-python: install append is not need any more
      gstreamer1.0-rtsp-server: upgrade to version 1.18.0
      gstreamer1.0-vaapi: upgrade to version 1.18.0
      gst-examples: upgrade to version 1.18.0
      gstreamer1.0-omx: upgrade to version 1.18.0
      gstreamer1.0-libav: upgrade to version 1.18.0
      gst-devtools: add version 1.18.0 (gst-validate -> gst-devtools)
      orc: Upgrade 0.4.31 -> 0.4.32
      gstreamer1.0-plugins-good: on wayland qt5 needs qtwayland
      gstreamer1.0-libav: add comercial license flags as ffmpeg needs this
      gstreamer1.0-plugins-bad: add srt package config knob
      ffmpeg: add srt package config knob
      gstreamer1.0-plugins-good: add package config knob for the Raspberry Pi

Joseph Reynolds (1):
      add new extrausers command passwd-expire

Joshua Watt (8):
      documentation: Add Pipenv support
      systemd: Re-enable chvt as non-root user without polkit
      python3-pycryptodomex: upgrade 3.9.8 -> 3.9.9
      weston-init: Stop running weston as root
      python3-pycryptodome: upgrade 3.9.8 -> 3.9.9
      bitbake: bitbake: hashserve: Add async client
      bitbake: bitbake: hashserve: Add support for readonly upstream
      bitbake: bitbake: cache: Remove bad keys() function

Kai Kang (1):
      sudo: fix multilib conflict

Khasim Mohammed (1):
      grub: add grub-nativesdk

Khem Raj (34):
      webkitgtk: Disable gold linker and JIT on riscv
      init-ifupdown: Define interfaces file for riscv emulators
      init-ifupdown: Merge all interface files for differnet qemus
      musl: Update to latest master
      qemuboot.bbclass: Fix a typo
      musl: Add .file directive in crt assembly files
      musl: Update to latest
      rpm: Fix error.h handing properly on musl
      gdb: Update to 10.x release
      numactl: Link with libatomic on rv64/rv32
      gstreamer: Fix build on 32bit arches with 64bit time_t
      rt-tests: Enable only for x86/ppc64 architectures
      lto: Add global LTO distro policy file
      python3: Enable lto if its in DISTRO_FEATURES
      lto.inc: Add -ffat-lto-objects and -fuse-linker-plugin
      lto: Introduce LTOEXTRA variable
      libaio: Disable LTO
      weston: Fix linking with LTO
      lto.inc: Disable LTO for xserver-xorg
      gcc: Do no parameterize LTO configuration flags
      puzzles: Check for excessive constant arguments
      lto.inc: Disable LTO for perf
      gcc: Handle duplicate names for variables
      musl: Update to latest master
      lrzsz: Use Cross AR during compile
      gawk: Avoid using host ar during cross compile
      lto.inc: Disable LTO for webkit
      python-numpy: Add support for riscv32
      arch-riscv: Enable qemu-usermode on rv32
      python3targetconfig.bbclass: Make py3 dep and tasks only for target recipes
      go: Update to 1.15.5
      binutils: Fix linker errors on chromium/ffmpeg on aarch64
      python3-numpy: Upgrade to 1.19.4
      python3-numpy: Add ptest

Konrad Weihmann (3):
      oeqa/core/context: expose results as variable
      oeqa/core/context: initialize _run_end_time
      testimage: print results for interrupted runs

Lee Chee Yang (5):
      bitbake: BBHandler: prompt error when task name contain expression
      libproxy: fix CVE-2020-26154
      python3: fix CVE-2020-27619
      python3: whitelist CVE-2020-15523
      qemu: fix CVE-2020-24352

Loic Domaigne (1):
      roofs_*.bbclass: fix missing vardeps for do_rootfs

Luca Boccassi (1):
      dbus: split -common and -tools out of main package

Mark Jonas (4):
      libsdl2: Fix directfb syntax error
      libsdl2: Fix directfb SDL_RenderFillRect
      libbsd: Remove BSD-4-Clause from main package
      libsdl2: Add directfb to PACKAGECONFIG rdepends

Martin Jansa (5):
      tune-arm9tdmi.inc: include arm9tdmi in PACKAGE_ARCHS
      gnutls: explicitly set --with-librt-prefix
      webkitgtk: fix opengl PACKAGECONFIG
      webkitgtk: fix build with x11 enabled
      weston: add pam to REQUIRED_DISTRO_FEATURES

Matt Madison (1):
      layer.conf: fix syntax error in PATH setting

Max Krummenacher (1):
      linux-firmware: rdepend on license for all nvidia packages

Maxime Roussin-BĂ©langer (3):
      meta: fix some unresponsive homepages and bugtracker links
      bitbake: cache: remove unused variables.
      bitbake: monitordisk: remove unused function parameter

Mert Kirpici (2):
      bitbake: fetch2: add zstd support to unpack
      bitbake: doc/conf.py: add missing import sys

Mingli Yu (2):
      bitbake.conf: Exclude ${CCACHE_DIR} from pseudo database
      update_udev_hwdb: clean hwdb.bin

Nathan Rossi (4):
      vim: add nativesdk to BBCLASSEXTEND
      rsync: add nativesdk to BBCLASSEXTEND
      diffstat: add nativesdk to BBCLASSEXTEND
      cml1.bbclass: Handle ncurses-native being available via pkg-config

Nicolas Dechesne (17):
      conf: update for release 3.2
      poky.yaml: remove unused variables
      poky.yaml: updates for 3.2
      sphinx: releases: add link to 3.1.3
      what-i-wish-id-known: replace labels with references to section title
      sdk-manual: replace labels with references to section title
      ref-manual: replace labels with references to section title
      dev-manual: replace labels with references to section title
      kernel-dev: replace labels with references to section title
      test-manual: remove unused labels
      bsp-guide: remove unused labels
      kernel-dev: remove unused labels
      profile-manual: remove unused labels
      sdk-manual: remove unused labels
      toaster-manual: remove unused labels
      Makefile: enable parallel build
      bitbake: docs: Makefile: enable parallel build

Norbert Kaminski (1):
      grub: Add support for RISC-V

Paul Barker (11):
      conf.py: Improve TOC and Outline depth in PDF output
      conf.py: Add oe_git directive
      documentation/README: Refer to top-level README for contributions
      dev-manual-common-tasks: Fix refs to testing branches
      dev-manual-common-tasks: Update & move patchwork reference
      dev-manual-common-tasks: Tidy up patch submission process
      dev-manual-common-tasks: Describe git-send-email accurately
      dev-manual-common-tasks: Describe how to handle patch feedback
      dev-manual-common-tasks: Describe how to propose changes to stable branches
      dev-manual-common-tasks: Re-order patch submission instructions
      poky.yaml: Define DISTRO_NAME_NO_CAP_LTS

Paul Eggleton (10):
      ref-manual: add reference anchors for each QA check
      ref-manual: fix for features_check class change
      ref-manual: QA check updates
      ref-manual: add PSEUDO_IGNORE_PATHS
      ref-manual: add IMAGE_VERSION_SUFFIX variable
      ref-manual: add IMAGE_NAME_SUFFIX variable
      ref-manual: add migration section for 3.2
      ref-manual: add IMAGE_LINK_NAME
      ref-manual: add migration info for image-artifact-names
      ref-manual: add migration info about MLPREFIX changes

Peter Bergin (2):
      rt-tests: backport patch that enable build for all archs
      Revert "rt-tests: Enable only for x86/ppc64 architectures"

Purushottam choudhary (1):
      systemd: selinux hook handling to enumerate nexthop

Randy MacLeod (1):
      libsdl2: Disable video-rpi

Randy Witt (4):
      numactl: Add the recipe for numactl
      numactl: Remove COMPATIBLE_HOST restrictions
      numactl: Skip the ptests when numa is not supported
      rt-tests: Update recipes to use 1.8

Ricardo Salveti (1):
      dosfstools: add mkfs.vfat to ALTERNATIVE

Richard Leitner (4):
      deb: replace deprecated apt force-yes argument
      xcb-proto: update to 1.14.1
      deb: export INTERCEPT_DIR for remove actions
      weston-init: introduce WESTON_GROUP

Richard Purdie (21):
      ref-manual/faq: Add entry for why binaries are changed in images
      dev-manual: Add a note about prelink changing prebuild binaries
      sstatesig: Log timestamps for hashequiv in reprodubile builds for do_package
      netbase: Add whitespace to purge bogus hash equivalence from autobuilder
      scripts/buildhistory_analysis: Avoid tracebacks from file comparision code
      maintainers: Add myself as numactl maintainer to avoid QA errors
      bitbake: bitbake: Post release version bump
      poky.conf: Post release version bump
      libxcb: Fix install file owner/group
      bitbake: siggen: Remove broken optimisation
      bitbake: fetch2/git: Document that we won't support passwords in git urls
      sstatesig: Remove workaround for bitbake taskhash bug
      ptest-runner: Fix license as it contains 'or later' clause
      libdnf: Fix license as it contains 'or later' clause
      alsa-utils: Fix license to GPLv2 only
      overview-manual-concepts: Fix the compiler bootstrap process
      bitbake: Add missing documentation Makefile
      oeqa/commands: Fix compatibility with python 3.9
      fs-perms: Ensure /usr/src/debug/ file modes are correct
      e2fsprogs: Fix a ptest permissions determinism issue
      uninative: Don't use single sstate for pseudo-native

Robert P. J. Day (3):
      ref-manual/ref-variables: "PACKAGE_FEEDS_ARCHS" -> "PACKAGE_FEED_ARCHS"
      README: "yocto-project-qs" -> "brief-yoctoprojectqs"
      adt-manual: delete obsolete ADT manual, and related content

Ross Burton (13):
      rpm: use libgcrypt instead of OpenSSL for cryptography
      syslinux: add link to upstream discussion in patch
      json-glib: use PACKAGECONFIG for tests
      json-glib: update patch status
      libical: backport a patch to fix build with ICU 68.1
      webkitgtk: fix build with ICU 68.1
      cve-check: show real PN/PV
      python3: add CVE-2007-4559 to whitelist
      sqlite3: add CVE-2015-3717 to whitelist
      gstreamer1.0-rtsp-server: set CVE_PRODUCT
      gstreamer1.0-plugins-base: set CVE_PRODUCT
      bitbake: providers: selected version not available should be a warning
      cve-update-db-native: handle all-wildcard versions

Saul Wold (1):
      classes/buildhistory: record LICENSE

Sinan Kaya (2):
      volatile-binds: add /srv to mount and install
      kernel-uboot: allow compression option to be configurable

Stacy Gaikovaia (1):
      valgrind: helgrind: Intercept libc functions

Steve Sakoman (3):
      netbase: update SRC_URI to reflect new file name
      openssh: whitelist CVE-2014-9278
      cups: whitelist CVE-2018-6553

Tim Orling (22):
      python3-atomicwrites: move from meta-python
      python3-attrs: move from meta-python
      python3-iniconfig: move from meta-python
      python3-more-itertools: move from meta-python
      python3-pathlib2: move from meta-python
      python3-toml: move from meta-python
      python3-py: move from meta-python
      python3-setuptools-scm: move from meta-python
      python3-packaging: move from meta-python
      python3-wcwidth: move from meta-python
      python3-zipp: move from meta-python
      python3-importlib-metadata: move from meta-python
      python3-pluggy: move from meta-python
      python3-pytest: move from meta-python
      maintainers.inc: add self for new pytest packages
      python3-more-itertools: upgrade 8.5.0 -> 8.6.0
      python3-importlib-metadata: upgrade 2.0.0 to 3.1.0
      python3-pytest: RDEPENDS on python3-toml
      python3-hypothesis: move from meta-python
      python3-sortedcontainers: move from meta-python
      maintainers.inc: add self for new python recipes
      python3-hypothesis: upgrade 5.41.3 -> 5.41.4

Tom Hochstein (1):
      mesa: Add xcb-fixes to loader when using x11 and dri3

Vyacheslav Yurkov (1):
      license_image.bbclass: use canonical name for license files

Wonmin Jung (1):
      kernel: Set proper LD in KERNEL_KCONFIG_COMMAND

Yann Dirson (6):
      systemtap: split examples and python scripts out of main package
      systemtap: remove extra dependencies
      systemtap: clarify the relation between exporter and python3-probes feature
      systemtap: fix install when python3-probes is disabled in PACKAGECONFIG
      systemtap: split runtime material in its own package
      systemtap: avoid RDEPENDS on python3-core when not using python3

Yann E. MORIN (2):
      common-licenses: add bzip2-1.0.4
      recipes-core/busybox: fixup licensing information

Yi Zhao (5):
      resolvconf: do not install dhclient hooks
      connman: set service to conflict with systemd-networkd
      pulseaudio: unify volatiles file name
      dhcpcd: install dhcpcd to /sbin rather than /usr/sbin
      dhcpcd: upgrade 9.3.1 -> 9.3.2

Yongxin Liu (2):
      grub: fix several CVEs in grub 2.04
      grub: clean up CVE patches

zangrc (18):
      python3-pycairo: upgrade 1.19.1 -> 1.20.0
      iproute2: upgrade 5.8.0 -> 5.9.0
      icu: upgrade 67.1 -> 68.1
      libdnf: upgrade 0.54.2 -> 0.55.0
      libinput: upgrade 1.16.2 -> 1.16.3
      enchant2: upgrade 2.2.12 -> 2.2.13
      libdrm: upgrade 2.4.102 -> 2.4.103
      gmp: upgrade 6.2.0 -> 6.2.1
      gpgme: upgrade 1.14.0 -> 1.15.0
      libunwind: upgrade 1.4.0 -> 1.5.0
      msmtp: upgrade 1.8.12 -> 1.8.13
      gtk-doc: upgrade 1.33.0 -> 1.33.1
      hdparm: upgrade 9.58 -> 9.60
      libcap-ng: upgrade 0.8 -> 0.8.1
      libjpeg-turbo: upgrade 2.0.5 -> 2.0.6
      libxkbcommon: upgrade 1.0.1 -> 1.0.3
      pulseaudio: upgrade 13.0 -> 14.0
      wireless-regdb: upgrade 2020.04.29 -> 2020.11.20

Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I22fa6c7160be5ff2105113cc63acc25f8977ae4e
diff --git a/poky/bitbake/lib/hashserv/__init__.py b/poky/bitbake/lib/hashserv/__init__.py
index f95e8f4..55f4841 100644
--- a/poky/bitbake/lib/hashserv/__init__.py
+++ b/poky/bitbake/lib/hashserv/__init__.py
@@ -3,6 +3,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 #
 
+import asyncio
 from contextlib import closing
 import re
 import sqlite3
@@ -21,6 +22,24 @@
 # is necessary
 DEFAULT_MAX_CHUNK = 32 * 1024
 
+TABLE_DEFINITION = (
+    ("method", "TEXT NOT NULL"),
+    ("outhash", "TEXT NOT NULL"),
+    ("taskhash", "TEXT NOT NULL"),
+    ("unihash", "TEXT NOT NULL"),
+    ("created", "DATETIME"),
+
+    # Optional fields
+    ("owner", "TEXT"),
+    ("PN", "TEXT"),
+    ("PV", "TEXT"),
+    ("PR", "TEXT"),
+    ("task", "TEXT"),
+    ("outhash_siginfo", "TEXT"),
+)
+
+TABLE_COLUMNS = tuple(name for name, _ in TABLE_DEFINITION)
+
 def setup_database(database, sync=True):
     db = sqlite3.connect(database)
     db.row_factory = sqlite3.Row
@@ -29,23 +48,10 @@
         cursor.execute('''
             CREATE TABLE IF NOT EXISTS tasks_v2 (
                 id INTEGER PRIMARY KEY AUTOINCREMENT,
-                method TEXT NOT NULL,
-                outhash TEXT NOT NULL,
-                taskhash TEXT NOT NULL,
-                unihash TEXT NOT NULL,
-                created DATETIME,
-
-                -- Optional fields
-                owner TEXT,
-                PN TEXT,
-                PV TEXT,
-                PR TEXT,
-                task TEXT,
-                outhash_siginfo TEXT,
-
+                %s
                 UNIQUE(method, outhash, taskhash)
                 )
-            ''')
+            ''' % " ".join("%s %s," % (name, typ) for name, typ in TABLE_DEFINITION))
         cursor.execute('PRAGMA journal_mode = WAL')
         cursor.execute('PRAGMA synchronous = %s' % ('NORMAL' if sync else 'OFF'))
 
@@ -88,10 +94,10 @@
         yield "\n"
 
 
-def create_server(addr, dbname, *, sync=True):
+def create_server(addr, dbname, *, sync=True, upstream=None):
     from . import server
     db = setup_database(dbname, sync=sync)
-    s = server.Server(db)
+    s = server.Server(db, upstream=upstream)
 
     (typ, a) = parse_address(addr)
     if typ == ADDR_TYPE_UNIX:
@@ -113,3 +119,15 @@
         c.connect_tcp(*a)
 
     return c
+
+async def create_async_client(addr):
+    from . import client
+    c = client.AsyncClient()
+
+    (typ, a) = parse_address(addr)
+    if typ == ADDR_TYPE_UNIX:
+        await c.connect_unix(*a)
+    else:
+        await c.connect_tcp(*a)
+
+    return c
diff --git a/poky/bitbake/lib/hashserv/client.py b/poky/bitbake/lib/hashserv/client.py
index a29af83..ae5875d 100644
--- a/poky/bitbake/lib/hashserv/client.py
+++ b/poky/bitbake/lib/hashserv/client.py
@@ -3,189 +3,216 @@
 # SPDX-License-Identifier: GPL-2.0-only
 #
 
+import asyncio
 import json
 import logging
 import socket
 import os
-from . import chunkify, DEFAULT_MAX_CHUNK
+from . import chunkify, DEFAULT_MAX_CHUNK, create_async_client
 
 
-logger = logging.getLogger('hashserv.client')
+logger = logging.getLogger("hashserv.client")
 
 
 class HashConnectionError(Exception):
     pass
 
 
-class Client(object):
+class AsyncClient(object):
     MODE_NORMAL = 0
     MODE_GET_STREAM = 1
 
     def __init__(self):
-        self._socket = None
         self.reader = None
         self.writer = None
         self.mode = self.MODE_NORMAL
         self.max_chunk = DEFAULT_MAX_CHUNK
 
-    def connect_tcp(self, address, port):
-        def connect_sock():
-            s = socket.create_connection((address, port))
-
-            s.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
-            s.setsockopt(socket.SOL_TCP, socket.TCP_QUICKACK, 1)
-            s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
-            return s
+    async def connect_tcp(self, address, port):
+        async def connect_sock():
+            return await asyncio.open_connection(address, port)
 
         self._connect_sock = connect_sock
 
-    def connect_unix(self, path):
-        def connect_sock():
-            s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            # AF_UNIX has path length issues so chdir here to workaround
-            cwd = os.getcwd()
-            try:
-                os.chdir(os.path.dirname(path))
-                s.connect(os.path.basename(path))
-            finally:
-                os.chdir(cwd)
-            return s
+    async def connect_unix(self, path):
+        async def connect_sock():
+            return await asyncio.open_unix_connection(path)
 
         self._connect_sock = connect_sock
 
-    def connect(self):
-        if self._socket is None:
-            self._socket = self._connect_sock()
+    async def _connect(self):
+        if self.reader is None or self.writer is None:
+            (self.reader, self.writer) = await self._connect_sock()
 
-            self.reader = self._socket.makefile('r', encoding='utf-8')
-            self.writer = self._socket.makefile('w', encoding='utf-8')
+            self.writer.write("OEHASHEQUIV 1.1\n\n".encode("utf-8"))
+            await self.writer.drain()
 
-            self.writer.write('OEHASHEQUIV 1.1\n\n')
-            self.writer.flush()
-
-            # Restore mode if the socket is being re-created
             cur_mode = self.mode
             self.mode = self.MODE_NORMAL
-            self._set_mode(cur_mode)
+            await self._set_mode(cur_mode)
 
-        return self._socket
+    async def close(self):
+        self.reader = None
 
-    def close(self):
-        if self._socket is not None:
-            self._socket.close()
-            self._socket = None
-            self.reader = None
+        if self.writer is not None:
+            self.writer.close()
             self.writer = None
 
-    def _send_wrapper(self, proc):
+    async def _send_wrapper(self, proc):
         count = 0
         while True:
             try:
-                self.connect()
-                return proc()
-            except (OSError, HashConnectionError, json.JSONDecodeError, UnicodeDecodeError) as e:
-                logger.warning('Error talking to server: %s' % e)
+                await self._connect()
+                return await proc()
+            except (
+                OSError,
+                HashConnectionError,
+                json.JSONDecodeError,
+                UnicodeDecodeError,
+            ) as e:
+                logger.warning("Error talking to server: %s" % e)
                 if count >= 3:
                     if not isinstance(e, HashConnectionError):
                         raise HashConnectionError(str(e))
                     raise e
-                self.close()
+                await self.close()
                 count += 1
 
-    def send_message(self, msg):
-        def get_line():
-            line = self.reader.readline()
+    async def send_message(self, msg):
+        async def get_line():
+            line = await self.reader.readline()
             if not line:
-                raise HashConnectionError('Connection closed')
+                raise HashConnectionError("Connection closed")
 
-            if not line.endswith('\n'):
-                raise HashConnectionError('Bad message %r' % message)
+            line = line.decode("utf-8")
+
+            if not line.endswith("\n"):
+                raise HashConnectionError("Bad message %r" % message)
 
             return line
 
-        def proc():
+        async def proc():
             for c in chunkify(json.dumps(msg), self.max_chunk):
-                self.writer.write(c)
-            self.writer.flush()
+                self.writer.write(c.encode("utf-8"))
+            await self.writer.drain()
 
-            l = get_line()
+            l = await get_line()
 
             m = json.loads(l)
-            if 'chunk-stream' in m:
+            if "chunk-stream" in m:
                 lines = []
                 while True:
-                    l = get_line().rstrip('\n')
+                    l = (await get_line()).rstrip("\n")
                     if not l:
                         break
                     lines.append(l)
 
-                m = json.loads(''.join(lines))
+                m = json.loads("".join(lines))
 
             return m
 
-        return self._send_wrapper(proc)
+        return await self._send_wrapper(proc)
 
-    def send_stream(self, msg):
-        def proc():
-            self.writer.write("%s\n" % msg)
-            self.writer.flush()
-            l = self.reader.readline()
+    async def send_stream(self, msg):
+        async def proc():
+            self.writer.write(("%s\n" % msg).encode("utf-8"))
+            await self.writer.drain()
+            l = await self.reader.readline()
             if not l:
-                raise HashConnectionError('Connection closed')
-            return l.rstrip()
+                raise HashConnectionError("Connection closed")
+            return l.decode("utf-8").rstrip()
 
-        return self._send_wrapper(proc)
+        return await self._send_wrapper(proc)
 
-    def _set_mode(self, new_mode):
+    async def _set_mode(self, new_mode):
         if new_mode == self.MODE_NORMAL and self.mode == self.MODE_GET_STREAM:
-            r = self.send_stream('END')
-            if r != 'ok':
-                raise HashConnectionError('Bad response from server %r' % r)
+            r = await self.send_stream("END")
+            if r != "ok":
+                raise HashConnectionError("Bad response from server %r" % r)
         elif new_mode == self.MODE_GET_STREAM and self.mode == self.MODE_NORMAL:
-            r = self.send_message({'get-stream': None})
-            if r != 'ok':
-                raise HashConnectionError('Bad response from server %r' % r)
+            r = await self.send_message({"get-stream": None})
+            if r != "ok":
+                raise HashConnectionError("Bad response from server %r" % r)
         elif new_mode != self.mode:
-            raise Exception('Undefined mode transition %r -> %r' % (self.mode, new_mode))
+            raise Exception(
+                "Undefined mode transition %r -> %r" % (self.mode, new_mode)
+            )
 
         self.mode = new_mode
 
-    def get_unihash(self, method, taskhash):
-        self._set_mode(self.MODE_GET_STREAM)
-        r = self.send_stream('%s %s' % (method, taskhash))
+    async def get_unihash(self, method, taskhash):
+        await self._set_mode(self.MODE_GET_STREAM)
+        r = await self.send_stream("%s %s" % (method, taskhash))
         if not r:
             return None
         return r
 
-    def report_unihash(self, taskhash, method, outhash, unihash, extra={}):
-        self._set_mode(self.MODE_NORMAL)
+    async def report_unihash(self, taskhash, method, outhash, unihash, extra={}):
+        await self._set_mode(self.MODE_NORMAL)
         m = extra.copy()
-        m['taskhash'] = taskhash
-        m['method'] = method
-        m['outhash'] = outhash
-        m['unihash'] = unihash
-        return self.send_message({'report': m})
+        m["taskhash"] = taskhash
+        m["method"] = method
+        m["outhash"] = outhash
+        m["unihash"] = unihash
+        return await self.send_message({"report": m})
 
-    def report_unihash_equiv(self, taskhash, method, unihash, extra={}):
-        self._set_mode(self.MODE_NORMAL)
+    async def report_unihash_equiv(self, taskhash, method, unihash, extra={}):
+        await self._set_mode(self.MODE_NORMAL)
         m = extra.copy()
-        m['taskhash'] = taskhash
-        m['method'] = method
-        m['unihash'] = unihash
-        return self.send_message({'report-equiv': m})
+        m["taskhash"] = taskhash
+        m["method"] = method
+        m["unihash"] = unihash
+        return await self.send_message({"report-equiv": m})
 
-    def get_taskhash(self, method, taskhash, all_properties=False):
-        self._set_mode(self.MODE_NORMAL)
-        return self.send_message({'get': {
-            'taskhash': taskhash,
-            'method': method,
-            'all': all_properties
-        }})
+    async def get_taskhash(self, method, taskhash, all_properties=False):
+        await self._set_mode(self.MODE_NORMAL)
+        return await self.send_message(
+            {"get": {"taskhash": taskhash, "method": method, "all": all_properties}}
+        )
 
-    def get_stats(self):
-        self._set_mode(self.MODE_NORMAL)
-        return self.send_message({'get-stats': None})
+    async def get_stats(self):
+        await self._set_mode(self.MODE_NORMAL)
+        return await self.send_message({"get-stats": None})
 
-    def reset_stats(self):
-        self._set_mode(self.MODE_NORMAL)
-        return self.send_message({'reset-stats': None})
+    async def reset_stats(self):
+        await self._set_mode(self.MODE_NORMAL)
+        return await self.send_message({"reset-stats": None})
+
+    async def backfill_wait(self):
+        await self._set_mode(self.MODE_NORMAL)
+        return (await self.send_message({"backfill-wait": None}))["tasks"]
+
+
+class Client(object):
+    def __init__(self):
+        self.client = AsyncClient()
+        self.loop = asyncio.new_event_loop()
+
+        for call in (
+            "connect_tcp",
+            "connect_unix",
+            "close",
+            "get_unihash",
+            "report_unihash",
+            "report_unihash_equiv",
+            "get_taskhash",
+            "get_stats",
+            "reset_stats",
+            "backfill_wait",
+        ):
+            downcall = getattr(self.client, call)
+            setattr(self, call, self._get_downcall_wrapper(downcall))
+
+    def _get_downcall_wrapper(self, downcall):
+        def wrapper(*args, **kwargs):
+            return self.loop.run_until_complete(downcall(*args, **kwargs))
+
+        return wrapper
+
+    @property
+    def max_chunk(self):
+        return self.client.max_chunk
+
+    @max_chunk.setter
+    def max_chunk(self, value):
+        self.client.max_chunk = value
diff --git a/poky/bitbake/lib/hashserv/server.py b/poky/bitbake/lib/hashserv/server.py
index 8105071..3ff4c51 100644
--- a/poky/bitbake/lib/hashserv/server.py
+++ b/poky/bitbake/lib/hashserv/server.py
@@ -3,7 +3,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 #
 
-from contextlib import closing
+from contextlib import closing, contextmanager
 from datetime import datetime
 import asyncio
 import json
@@ -12,8 +12,9 @@
 import os
 import signal
 import socket
+import sys
 import time
-from . import chunkify, DEFAULT_MAX_CHUNK
+from . import chunkify, DEFAULT_MAX_CHUNK, create_async_client, TABLE_COLUMNS
 
 logger = logging.getLogger('hashserv.server')
 
@@ -111,16 +112,40 @@
 class ClientError(Exception):
     pass
 
+def insert_task(cursor, data, ignore=False):
+    keys = sorted(data.keys())
+    query = '''INSERT%s INTO tasks_v2 (%s) VALUES (%s)''' % (
+        " OR IGNORE" if ignore else "",
+        ', '.join(keys),
+        ', '.join(':' + k for k in keys))
+    cursor.execute(query, data)
+
+async def copy_from_upstream(client, db, method, taskhash):
+    d = await client.get_taskhash(method, taskhash, True)
+    if d is not None:
+        # Filter out unknown columns
+        d = {k: v for k, v in d.items() if k in TABLE_COLUMNS}
+        keys = sorted(d.keys())
+
+
+        with closing(db.cursor()) as cursor:
+            insert_task(cursor, d)
+            db.commit()
+
+    return d
+
 class ServerClient(object):
     FAST_QUERY = 'SELECT taskhash, method, unihash FROM tasks_v2 WHERE method=:method AND taskhash=:taskhash ORDER BY created ASC LIMIT 1'
     ALL_QUERY =  'SELECT *                         FROM tasks_v2 WHERE method=:method AND taskhash=:taskhash ORDER BY created ASC LIMIT 1'
 
-    def __init__(self, reader, writer, db, request_stats):
+    def __init__(self, reader, writer, db, request_stats, backfill_queue, upstream):
         self.reader = reader
         self.writer = writer
         self.db = db
         self.request_stats = request_stats
         self.max_chunk = DEFAULT_MAX_CHUNK
+        self.backfill_queue = backfill_queue
+        self.upstream = upstream
 
         self.handlers = {
             'get': self.handle_get,
@@ -130,10 +155,18 @@
             'get-stats': self.handle_get_stats,
             'reset-stats': self.handle_reset_stats,
             'chunk-stream': self.handle_chunk,
+            'backfill-wait': self.handle_backfill_wait,
         }
 
     async def process_requests(self):
+        if self.upstream is not None:
+            self.upstream_client = await create_async_client(self.upstream)
+        else:
+            self.upstream_client = None
+
         try:
+
+
             self.addr = self.writer.get_extra_info('peername')
             logger.debug('Client %r connected' % (self.addr,))
 
@@ -171,6 +204,9 @@
         except ClientError as e:
             logger.error(str(e))
         finally:
+            if self.upstream_client is not None:
+                await self.upstream_client.close()
+
             self.writer.close()
 
     async def dispatch_message(self, msg):
@@ -239,15 +275,19 @@
         if row is not None:
             logger.debug('Found equivalent task %s -> %s', (row['taskhash'], row['unihash']))
             d = {k: row[k] for k in row.keys()}
-
-            self.write_message(d)
+        elif self.upstream_client is not None:
+            d = await copy_from_upstream(self.upstream_client, self.db, method, taskhash)
         else:
-            self.write_message(None)
+            d = None
+
+        self.write_message(d)
 
     async def handle_get_stream(self, request):
         self.write_message('ok')
 
         while True:
+            upstream = None
+
             l = await self.reader.readline()
             if not l:
                 return
@@ -272,6 +312,12 @@
                 if row is not None:
                     msg = ('%s\n' % row['unihash']).encode('utf-8')
                     #logger.debug('Found equivalent task %s -> %s', (row['taskhash'], row['unihash']))
+                elif self.upstream_client is not None:
+                    upstream = await self.upstream_client.get_unihash(method, taskhash)
+                    if upstream:
+                        msg = ("%s\n" % upstream).encode("utf-8")
+                    else:
+                        msg = "\n".encode("utf-8")
                 else:
                     msg = '\n'.encode('utf-8')
 
@@ -282,6 +328,11 @@
 
             await self.writer.drain()
 
+            # Post to the backfill queue after writing the result to minimize
+            # the turn around time on a request
+            if upstream is not None:
+                await self.backfill_queue.put((method, taskhash))
+
     async def handle_report(self, data):
         with closing(self.db.cursor()) as cursor:
             cursor.execute('''
@@ -324,11 +375,7 @@
                     if k in data:
                         insert_data[k] = data[k]
 
-                cursor.execute('''INSERT INTO tasks_v2 (%s) VALUES (%s)''' % (
-                    ', '.join(sorted(insert_data.keys())),
-                    ', '.join(':' + k for k in sorted(insert_data.keys()))),
-                    insert_data)
-
+                insert_task(cursor, insert_data)
                 self.db.commit()
 
                 logger.info('Adding taskhash %s with unihash %s',
@@ -358,11 +405,7 @@
                 if k in data:
                     insert_data[k] = data[k]
 
-            cursor.execute('''INSERT OR IGNORE INTO tasks_v2 (%s) VALUES (%s)''' % (
-                ', '.join(sorted(insert_data.keys())),
-                ', '.join(':' + k for k in sorted(insert_data.keys()))),
-                insert_data)
-
+            insert_task(cursor, insert_data, ignore=True)
             self.db.commit()
 
             # Fetch the unihash that will be reported for the taskhash. If the
@@ -394,6 +437,13 @@
         self.request_stats.reset()
         self.write_message(d)
 
+    async def handle_backfill_wait(self, request):
+        d = {
+            'tasks': self.backfill_queue.qsize(),
+        }
+        await self.backfill_queue.join()
+        self.write_message(d)
+
     def query_equivalent(self, method, taskhash, query):
         # This is part of the inner loop and must be as fast as possible
         try:
@@ -405,7 +455,7 @@
 
 
 class Server(object):
-    def __init__(self, db, loop=None):
+    def __init__(self, db, loop=None, upstream=None):
         self.request_stats = Stats()
         self.db = db
 
@@ -416,6 +466,8 @@
             self.loop = loop
             self.close_loop = False
 
+        self.upstream = upstream
+
         self._cleanup_socket = None
 
     def start_tcp_server(self, host, port):
@@ -458,7 +510,7 @@
     async def handle_client(self, reader, writer):
         # writer.transport.set_write_buffer_limits(0)
         try:
-            client = ServerClient(reader, writer, self.db, self.request_stats)
+            client = ServerClient(reader, writer, self.db, self.request_stats, self.backfill_queue, self.upstream)
             await client.process_requests()
         except Exception as e:
             import traceback
@@ -467,23 +519,60 @@
             writer.close()
         logger.info('Client disconnected')
 
+    @contextmanager
+    def _backfill_worker(self):
+        async def backfill_worker_task():
+            client = await create_async_client(self.upstream)
+            try:
+                while True:
+                    item = await self.backfill_queue.get()
+                    if item is None:
+                        self.backfill_queue.task_done()
+                        break
+                    method, taskhash = item
+                    await copy_from_upstream(client, self.db, method, taskhash)
+                    self.backfill_queue.task_done()
+            finally:
+                await client.close()
+
+        async def join_worker(worker):
+            await self.backfill_queue.put(None)
+            await worker
+
+        if self.upstream is not None:
+            worker = asyncio.ensure_future(backfill_worker_task())
+            try:
+                yield
+            finally:
+                self.loop.run_until_complete(join_worker(worker))
+        else:
+            yield
+
     def serve_forever(self):
         def signal_handler():
             self.loop.stop()
 
-        self.loop.add_signal_handler(signal.SIGTERM, signal_handler)
-
+        asyncio.set_event_loop(self.loop)
         try:
-            self.loop.run_forever()
-        except KeyboardInterrupt:
-            pass
+            self.backfill_queue = asyncio.Queue()
 
-        self.server.close()
-        self.loop.run_until_complete(self.server.wait_closed())
-        logger.info('Server shutting down')
+            self.loop.add_signal_handler(signal.SIGTERM, signal_handler)
 
-        if self.close_loop:
-            self.loop.close()
+            with self._backfill_worker():
+                try:
+                    self.loop.run_forever()
+                except KeyboardInterrupt:
+                    pass
 
-        if self._cleanup_socket is not None:
-            self._cleanup_socket()
+                self.server.close()
+
+            self.loop.run_until_complete(self.server.wait_closed())
+            logger.info('Server shutting down')
+        finally:
+            if self.close_loop:
+                if sys.version_info >= (3, 6):
+                    self.loop.run_until_complete(self.loop.shutdown_asyncgens())
+                self.loop.close()
+
+            if self._cleanup_socket is not None:
+                self._cleanup_socket()
diff --git a/poky/bitbake/lib/hashserv/tests.py b/poky/bitbake/lib/hashserv/tests.py
index 4566f24..3dd9a31 100644
--- a/poky/bitbake/lib/hashserv/tests.py
+++ b/poky/bitbake/lib/hashserv/tests.py
@@ -16,35 +16,54 @@
 import unittest
 import socket
 
+def _run_server(server, idx):
+    # logging.basicConfig(level=logging.DEBUG, filename='bbhashserv.log', filemode='w',
+    #                     format='%(levelname)s %(filename)s:%(lineno)d %(message)s')
+    sys.stdout = open('bbhashserv-%d.log' % idx, 'w')
+    sys.stderr = sys.stdout
+    server.serve_forever()
 
 class TestHashEquivalenceServer(object):
     METHOD = 'TestMethod'
 
-    def _run_server(self):
-        # logging.basicConfig(level=logging.DEBUG, filename='bbhashserv.log', filemode='w',
-        #                     format='%(levelname)s %(filename)s:%(lineno)d %(message)s')
-        self.server.serve_forever()
+    server_index = 0
+
+    def start_server(self, dbpath=None, upstream=None):
+        self.server_index += 1
+        if dbpath is None:
+            dbpath = os.path.join(self.temp_dir.name, "db%d.sqlite" % self.server_index)
+
+        def cleanup_thread(thread):
+            thread.terminate()
+            thread.join()
+
+        server = create_server(self.get_server_addr(self.server_index), dbpath, upstream=upstream)
+        server.dbpath = dbpath
+
+        server.thread = multiprocessing.Process(target=_run_server, args=(server, self.server_index))
+        server.thread.start()
+        self.addCleanup(cleanup_thread, server.thread)
+
+        def cleanup_client(client):
+            client.close()
+
+        client = create_client(server.address)
+        self.addCleanup(cleanup_client, client)
+
+        return (client, server)
 
     def setUp(self):
         if sys.version_info < (3, 5, 0):
             self.skipTest('Python 3.5 or later required')
 
         self.temp_dir = tempfile.TemporaryDirectory(prefix='bb-hashserv')
-        self.dbfile = os.path.join(self.temp_dir.name, 'db.sqlite')
+        self.addCleanup(self.temp_dir.cleanup)
 
-        self.server = create_server(self.get_server_addr(), self.dbfile)
-        self.server_thread = multiprocessing.Process(target=self._run_server)
-        self.server_thread.start()
-        self.client = create_client(self.server.address)
+        (self.client, self.server) = self.start_server()
 
-    def tearDown(self):
-        # Shutdown server
-        s = getattr(self, 'server', None)
-        if s is not None:
-            self.server_thread.terminate()
-            self.server_thread.join()
-        self.client.close()
-        self.temp_dir.cleanup()
+    def assertClientGetHash(self, client, taskhash, unihash):
+        result = client.get_unihash(self.METHOD, taskhash)
+        self.assertEqual(result, unihash)
 
     def test_create_hash(self):
         # Simple test that hashes can be created
@@ -52,8 +71,7 @@
         outhash = '2765d4a5884be49b28601445c2760c5f21e7e5c0ee2b7e3fce98fd7e5970796f'
         unihash = 'f46d3fbb439bd9b921095da657a4de906510d2cd'
 
-        result = self.client.get_unihash(self.METHOD, taskhash)
-        self.assertIsNone(result, msg='Found unexpected task, %r' % result)
+        self.assertClientGetHash(self.client, taskhash, None)
 
         result = self.client.report_unihash(taskhash, self.METHOD, outhash, unihash)
         self.assertEqual(result['unihash'], unihash, 'Server returned bad unihash')
@@ -84,22 +102,19 @@
         unihash = '218e57509998197d570e2c98512d0105985dffc9'
         self.client.report_unihash(taskhash, self.METHOD, outhash, unihash)
 
-        result = self.client.get_unihash(self.METHOD, taskhash)
-        self.assertEqual(result, unihash)
+        self.assertClientGetHash(self.client, taskhash, unihash)
 
         outhash2 = '0904a7fe3dc712d9fd8a74a616ddca2a825a8ee97adf0bd3fc86082c7639914d'
         unihash2 = 'ae9a7d252735f0dafcdb10e2e02561ca3a47314c'
         self.client.report_unihash(taskhash, self.METHOD, outhash2, unihash2)
 
-        result = self.client.get_unihash(self.METHOD, taskhash)
-        self.assertEqual(result, unihash)
+        self.assertClientGetHash(self.client, taskhash, unihash)
 
         outhash3 = '77623a549b5b1a31e3732dfa8fe61d7ce5d44b3370f253c5360e136b852967b4'
         unihash3 = '9217a7d6398518e5dc002ed58f2cbbbc78696603'
         self.client.report_unihash(taskhash, self.METHOD, outhash3, unihash3)
 
-        result = self.client.get_unihash(self.METHOD, taskhash)
-        self.assertEqual(result, unihash)
+        self.assertClientGetHash(self.client, taskhash, unihash)
 
     def test_huge_message(self):
         # Simple test that hashes can be created
@@ -107,8 +122,7 @@
         outhash = '3c979c3db45c569f51ab7626a4651074be3a9d11a84b1db076f5b14f7d39db44'
         unihash = '90e9bc1d1f094c51824adca7f8ea79a048d68824'
 
-        result = self.client.get_unihash(self.METHOD, taskhash)
-        self.assertIsNone(result, msg='Found unexpected task, %r' % result)
+        self.assertClientGetHash(self.client, taskhash, None)
 
         siginfo = "0" * (self.client.max_chunk * 4)
 
@@ -156,14 +170,83 @@
 
         self.assertFalse(failures)
 
+    def test_upstream_server(self):
+        # Tests upstream server support. This is done by creating two servers
+        # that share a database file. The downstream server has it upstream
+        # set to the test server, whereas the side server doesn't. This allows
+        # verification that the hash requests are being proxied to the upstream
+        # server by verifying that they appear on the downstream client, but not
+        # the side client. It also verifies that the results are pulled into
+        # the downstream database by checking that the downstream and side servers
+        # match after the downstream is done waiting for all backfill tasks
+        (down_client, down_server) = self.start_server(upstream=self.server.address)
+        (side_client, side_server) = self.start_server(dbpath=down_server.dbpath)
+
+        def check_hash(taskhash, unihash, old_sidehash):
+            nonlocal down_client
+            nonlocal side_client
+
+            # check upstream server
+            self.assertClientGetHash(self.client, taskhash, unihash)
+
+            # Hash should *not* be present on the side server
+            self.assertClientGetHash(side_client, taskhash, old_sidehash)
+
+            # Hash should be present on the downstream server, since it
+            # will defer to the upstream server. This will trigger
+            # the backfill in the downstream server
+            self.assertClientGetHash(down_client, taskhash, unihash)
+
+            # After waiting for the downstream client to finish backfilling the
+            # task from the upstream server, it should appear in the side server
+            # since the database is populated
+            down_client.backfill_wait()
+            self.assertClientGetHash(side_client, taskhash, unihash)
+
+        # Basic report
+        taskhash = '8aa96fcffb5831b3c2c0cb75f0431e3f8b20554a'
+        outhash = 'afe240a439959ce86f5e322f8c208e1fedefea9e813f2140c81af866cc9edf7e'
+        unihash = '218e57509998197d570e2c98512d0105985dffc9'
+        self.client.report_unihash(taskhash, self.METHOD, outhash, unihash)
+
+        check_hash(taskhash, unihash, None)
+
+        # Duplicated taskhash with multiple output hashes and unihashes.
+        # All servers should agree with the originally reported hash
+        outhash2 = '0904a7fe3dc712d9fd8a74a616ddca2a825a8ee97adf0bd3fc86082c7639914d'
+        unihash2 = 'ae9a7d252735f0dafcdb10e2e02561ca3a47314c'
+        self.client.report_unihash(taskhash, self.METHOD, outhash2, unihash2)
+
+        check_hash(taskhash, unihash, unihash)
+
+        # Report an equivalent task. The sideload will originally report
+        # no unihash until backfilled
+        taskhash3 = "044c2ec8aaf480685a00ff6ff49e6162e6ad34e1"
+        unihash3 = "def64766090d28f627e816454ed46894bb3aab36"
+        self.client.report_unihash(taskhash3, self.METHOD, outhash, unihash3)
+
+        check_hash(taskhash3, unihash, None)
+
+        # Test that reporting a unihash in the downstream client isn't
+        # propagating to the upstream server
+        taskhash4 = "e3da00593d6a7fb435c7e2114976c59c5fd6d561"
+        outhash4 = "1cf8713e645f491eb9c959d20b5cae1c47133a292626dda9b10709857cbe688a"
+        unihash4 = "3b5d3d83f07f259e9086fcb422c855286e18a57d"
+        down_client.report_unihash(taskhash4, self.METHOD, outhash4, unihash4)
+        down_client.backfill_wait()
+
+        self.assertClientGetHash(down_client, taskhash4, unihash4)
+        self.assertClientGetHash(side_client, taskhash4, unihash4)
+        self.assertClientGetHash(self.client, taskhash4, None)
+
 
 class TestHashEquivalenceUnixServer(TestHashEquivalenceServer, unittest.TestCase):
-    def get_server_addr(self):
-        return "unix://" + os.path.join(self.temp_dir.name, 'sock')
+    def get_server_addr(self, server_idx):
+        return "unix://" + os.path.join(self.temp_dir.name, 'sock%d' % server_idx)
 
 
 class TestHashEquivalenceTCPServer(TestHashEquivalenceServer, unittest.TestCase):
-    def get_server_addr(self):
+    def get_server_addr(self, server_idx):
         # Some hosts cause asyncio module to misbehave, when IPv6 is not enabled.
         # If IPv6 is enabled, it should be safe to use localhost directly, in general
         # case it is more reliable to resolve the IP address explicitly.