diff --git a/SOURCES/kmod-modprobe-ignore-builtin-module-on-recursive-removing.patch b/SOURCES/kmod-modprobe-ignore-builtin-module-on-recursive-removing.patch
new file mode 100644
index 0000000..69c1bd5
--- /dev/null
+++ b/SOURCES/kmod-modprobe-ignore-builtin-module-on-recursive-removing.patch
@@ -0,0 +1,83 @@
+From 52a0ba82e1ad180f9f91920db70a758fac49466a Mon Sep 17 00:00:00 2001
+From: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
+Date: Thu, 31 Oct 2019 20:12:53 +0200
+Subject: [PATCH] modprobe: ignore builtin module on recursive removing
+
+If there are built-in dependencies and any of them is built-in in
+the kernel, modprobe -r fails with
+
+modprobe: FATAL: Module module_name is builtin.
+
+It makes sense to ignore such dependencies for the case when
+removing is called for non-top level module.
+
+Example: cifs module, it declares bunch of softdeps and the first
+one fails on some kernel configs:
+
+modprobe: FATAL: Module gcm is builtin.
+
+Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
+---
+ tools/modprobe.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/tools/modprobe.c b/tools/modprobe.c
+index a9e2331567af..44cd15c2bf57 100644
+--- a/tools/modprobe.c
++++ b/tools/modprobe.c
+@@ -353,7 +353,8 @@ static int rmmod_do_remove_module(struct kmod_module *mod)
+ 	return err;
+ }
+ 
+-static int rmmod_do_module(struct kmod_module *mod, bool do_dependencies);
++static int rmmod_do_module(struct kmod_module *mod, bool do_dependencies,
++			   bool ignore_builtin);
+ 
+ static int rmmod_do_deps_list(struct kmod_list *list, bool stop_on_errors)
+ {
+@@ -361,7 +362,7 @@ static int rmmod_do_deps_list(struct kmod_list *list, bool stop_on_errors)
+ 
+ 	kmod_list_foreach_reverse(l, list) {
+ 		struct kmod_module *m = kmod_module_get_module(l);
+-		int r = rmmod_do_module(m, false);
++		int r = rmmod_do_module(m, false, true);
+ 		kmod_module_unref(m);
+ 
+ 		if (r < 0 && stop_on_errors)
+@@ -371,7 +372,8 @@ static int rmmod_do_deps_list(struct kmod_list *list, bool stop_on_errors)
+ 	return 0;
+ }
+ 
+-static int rmmod_do_module(struct kmod_module *mod, bool do_dependencies)
++static int rmmod_do_module(struct kmod_module *mod, bool do_dependencies,
++			   bool ignore_builtin)
+ {
+ 	const char *modname = kmod_module_get_name(mod);
+ 	struct kmod_list *pre = NULL, *post = NULL;
+@@ -401,8 +403,12 @@ static int rmmod_do_module(struct kmod_module *mod, bool do_dependencies)
+ 			}
+ 			goto error;
+ 		} else if (state == KMOD_MODULE_BUILTIN) {
+-			LOG("Module %s is builtin.\n", modname);
+-			err = -ENOENT;
++			if (ignore_builtin) {
++				err = 0;
++			} else {
++				LOG("Module %s is builtin.\n", modname);
++				err = -ENOENT;
++			}
+ 			goto error;
+ 		}
+ 	}
+@@ -462,7 +468,7 @@ static int rmmod(struct kmod_ctx *ctx, const char *alias)
+ 
+ 	kmod_list_foreach(l, list) {
+ 		struct kmod_module *mod = kmod_module_get_module(l);
+-		err = rmmod_do_module(mod, true);
++		err = rmmod_do_module(mod, true, false);
+ 		kmod_module_unref(mod);
+ 		if (err < 0)
+ 			break;
+-- 
+2.24.0
+
diff --git a/SOURCES/weak-modules b/SOURCES/weak-modules
index dd163cc..65806d0 100644
--- a/SOURCES/weak-modules
+++ b/SOURCES/weak-modules
@@ -3,15 +3,6 @@
 # weak-modules - determine which modules are kABI compatible with installed
 #                kernels and set up the symlinks in /lib/*/weak-updates.
 #
-# This is an updated version of the script which doesn't support
-# multiple installation of the same out-of-tree module (stored in the
-# 'extra' subdirectory) for multiple kernels. This assumption is
-# supposed to be verified at the rpm level of the packages delivering
-# these modules.  There are some checks for this assumption, however we
-# really don't solve this situation. This limitation allows for a much
-# simpler version of the script. Previous version tried to work in this
-# case but was incorrect in some cases.
-
 unset LANG LC_ALL LC_COLLATE
 
 tmpdir=$(mktemp -td ${0##*/}.XXXXXX)
@@ -32,6 +23,14 @@ declare -A weak_modules_before
 declare -A groups
 declare -A grouped_modules
 
+# output of validate_weak_links, one iteration
+# short_name -> path
+declare -A compatible_modules
+
+# state for update_modules_for_krel (needed for add_kernel case)
+# short_name -> path
+declare -A installed_modules
+
 # doit:
 # A wrapper used whenever we're going to perform a real operation.
 doit() {
@@ -473,6 +472,29 @@ prepare_sandbox() {
     depmod="$depmod_orig -C $conf"
 }
 
+# discard_installed:
+# remove installed_modules[] from modules[]
+discard_installed()
+{
+    local short_name
+
+    for m in "${!modules[@]}"; do
+        short_name="$(module_short_name "${modules[$m]}")"
+
+        [[ -z "${installed_modules[$short_name]}" ]] && continue
+
+        unset "modules[$m]"
+    done
+}
+
+# update_installed:
+# add compatible_modules[] to installed_modules[]
+update_installed()
+{
+    for m in "${!compatible_modules[@]}"; do
+        installed_modules[$m]="${compatible_modules[$m]}"
+    done
+}
 
 # finish_sandbox:
 # restore global state after sandboxing
@@ -568,6 +590,7 @@ find_systemmap_file() {
 #   the given kernel)
 # - check the state after validation to produce needed messages
 #   and trigger initrd regeneration if the list changed.
+#
 update_modules_for_krel() {
     local krel="$1"
     local func="$2"
@@ -579,12 +602,24 @@ update_modules_for_krel() {
 
     global_link_state_save $krel
 
+    # remove already installed from modules[]
+    discard_installed
+
+    # do not run heavy validation procedure if no modules to install
+    if [[ "${#modules[@]}" -eq 0 ]]; then
+        finish_sandbox $krel
+        return
+    fi
+
     $func $krel
 
     if ! validate_weak_links $krel && [[ -z "$force_update" ]]; then
         global_link_state_restore $krel
     fi
 
+    # add compatible to installed
+    update_installed
+
     global_link_state_announce_changes $krel
 
     finish_sandbox $krel
@@ -600,12 +635,16 @@ update_modules() {
     local func="$1"
     local force_update="$2"
     local module_krel
+    declare -a saved_modules
 
     read_modules_list || exit 1
     [[ ${#modules[@]} -gt 0 ]] || return
+    saved_modules=("${modules[@]}")
 
     for krel in $(find_installed_kernels); do
         update_modules_for_krel $krel $func $force_update
+        modules=("${saved_modules[@]}")
+        installed_modules=()
     done
 
     for module in "${modules[@]}"; do
@@ -630,7 +669,7 @@ add_weak_links() {
         module_krel="$(krel_of_module $module)"
 
         case "$module" in
-            /lib/modules/$krel/*)
+            $BASEDIR/lib/modules/$krel/*)
                 # Module already installed to the current kernel
                 continue ;;
         esac
@@ -698,6 +737,7 @@ remove_weak_links() {
 #
 # Returns 0 (success) if proposal is fine or
 #         1 (false) if some incompatible symlinks were removed
+# initializes global hashmap compatible_modules with all the valid ones
 validate_weak_links() {
     local krel="$1"
     local basedir=${BASEDIR:+-b $BASEDIR}
@@ -715,6 +755,7 @@ validate_weak_links() {
     local is_configuration_valid=0
 
     tmp=$(mktemp -p $tmpdir)
+    compatible_modules=()
 
     if ! [[ -e $tmpdir/symvers-$krel ]]; then
         local symvers_path=$(find_symvers_file "$krel")
@@ -779,6 +820,9 @@ validate_weak_links() {
         module_krel=$(krel_of_module $target)
 
         if [[ "$module" == "$target" ]]; then
+            short_name="$(module_short_name "$module")"
+            compatible_modules+=([$short_name]="$module")
+
             pr_verbose "Module ${module##*/} from kernel $module_krel is compatible with kernel $krel"
         fi
     done
@@ -877,9 +921,12 @@ add_modules() {
 # do_make_groups:
 # Takes tmp file which contains preprocessed modules.dep
 # output (or modules.dep)
+#
 # reads modules.dep format information from stdin
 # produces groups associative array
 # the group is a maximum subset of modules having at least a link
+#
+# more fine tuned extra filtering.
 do_make_groups()
 {
     local tmp="$1"
@@ -890,6 +937,8 @@ do_make_groups()
     while read i; do
         mods=($i)
 
+        echo "${mods[0]}" |grep -q "extra/" || continue
+
         # if the module already met, then its dependencies already counted
         module_group="${grouped_modules[${mods[0]}]}"
         [[ -n $module_group ]] && continue
@@ -898,6 +947,8 @@ do_make_groups()
         group_name="${mods[0]}"
 
         for mod in "${mods[@]}"; do
+            echo "$mod" |grep -q "extra/" || continue
+
             # if there is already such group,
             # it is a subset of the one being created
             # due to depmod output
@@ -915,26 +966,32 @@ do_make_groups()
 # preprocess output for make_groups
 # depmod -n produces also aliases, so it cuts them off
 # also it removes colon after the first module
-filter_depmod_deps()
+cut_depmod_deps()
 {
     awk 'BEGIN { pr = 1 } /^#/{ pr = 0 } pr == 1 {sub(":",""); print $0}'
 }
 
-# make_abs_path:
+# filter_extra_absoluted:
 # Takes kernel version
 # makes full path from the relative module path
 # (produced by depmod for in-kernel-dir modules)
-make_abs_path()
+# filter only extra/ modules
+filter_extra_absoluted()
 {
     local kver="$1"
     local mod
     declare -a mods
 
     while read i; do
+        # skip non-extra. The check is not perfect, but ok
+        # to speed up handling in general cases
+        echo "$i" |grep -q "extra/" || continue
+
         mods=($i)
         for j in "${!mods[@]}"; do
             mod="${mods[$j]}"
-            [[ ${mod:0:1} == "/" ]] || mod="/lib/modules/$kver/$mod"
+
+            [[ ${mod:0:1} == "/" ]] || mod="$BASEDIR/lib/modules/$kver/$mod"
             mods[$j]="$mod"
         done
         echo "${mods[@]}"
@@ -942,20 +999,20 @@ make_abs_path()
 }
 
 # make_groups:
-# takes krel and a file with the list of modules,
+# takes k -- kernel version, we are installing extras from
 # prepares and feeds to do_make_groups
 # to create the module groups (global)
 make_groups()
 {
-    local krel="$1"
-    local tmp1="$2"
+    local k="$1"
     local tmp2=$(mktemp -p $tmpdir)
+    local basedir=${BASEDIR:+-b $BASEDIR}
 
     groups=()
     grouped_modules=()
 
-    $depmod -n $krel $(cat $tmp1) 2>/dev/null |
-        filter_depmod_deps | make_abs_path $krel > $tmp2
+    $depmod -n $basedir $k 2>/dev/null |
+        cut_depmod_deps | filter_extra_absoluted $k > $tmp2
 
     do_make_groups $tmp2
 
@@ -976,11 +1033,11 @@ add_kernel() {
         exit 1
     fi
 
-    for k in $(find_kernels_with_extra | rpmsort); do
+    for k in $(find_kernels_with_extra | rpmsort -r); do
         [[ "$krel" == "$k" ]] && continue
         find_modules $k extra > $tmp
 
-        is_empty_file "$tmp" || make_groups $krel $tmp
+        is_empty_file "$tmp" || make_groups $k
 
         # reuse tmp
 
diff --git a/SPECS/kmod.spec b/SPECS/kmod.spec
index 0b4bb5e..cf0fad5 100644
--- a/SPECS/kmod.spec
+++ b/SPECS/kmod.spec
@@ -1,6 +1,6 @@
 Name:		kmod
 Version:	25
-Release:	13%{?dist}
+Release:	16%{?dist}
 Summary:	Linux kernel module management utilities
 
 Group:		System Environment/Kernel
@@ -13,6 +13,7 @@ Exclusiveos:	Linux
 
 Patch01:	kmod-signature-do-not-report-wrong-data-for-pkc-7-signatu.patch
 Patch02:	kmod-libkmod-signature-implement-pkcs7-parsing-with-opens.patch
+Patch03:	kmod-modprobe-ignore-builtin-module-on-recursive-removing.patch
 
 BuildRoot:     %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 BuildRequires:	chrpath
@@ -55,6 +56,7 @@ applications that wish to load or unload Linux kernel modules.
 %setup -q
 %patch01 -p1
 %patch02 -p1
+%patch03 -p1
 
 %build
 export V=1
@@ -123,6 +125,19 @@ install -m 0644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/depmod.d/dist.conf
 %{_libdir}/libkmod.so
 
 %changelog
+* Mon Dec  9 2019 Yauheni Kaliuta <ykaliuta@redhat.com> - 25-16
+- weak-modules: update_modules_for_krel: always finish sandbox
+- weak-modules: groupping: use dependencies of extra/ provider
+  Resolves: rhbz#1778889
+
+* Mon Dec  9 2019 Yauheni Kaliuta <ykaliuta@redhat.com> - 25-15
+- weak-modules: reverse checking order for add-kernel
+  Resolves: rhbz#1755196
+
+* Mon Dec  2 2019 Yauheni Kaliuta <ykaliuta@redhat.com> - 25-14
+- modprobe: do not fail on built-in modules
+  Resolves: rhbz#1767513
+
 * Tue Apr 16 2019 Yauheni Kaliuta <ykaliuta@redhat.com> - 25-13
 - weak-modules: handle independent modules in one run
   Resolves: rhbz#1695763