diff --git a/drivers/base/node.c b/drivers/base/node.c
index 98a31bafc8a2..32aeb85f1d4a 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -833,6 +833,15 @@ int link_mem_sections(int nid, unsigned long start_pfn, unsigned long end_pfn)
 				  register_mem_sect_under_node);
 }
 
+bool memory_block_registered_under_node(struct memory_block *mem, int nid)
+{
+	if (mem->nid == nid)
+		return true;
+	/* memory blocks can span multiple nodes. Check against the link. */
+	return sysfs_link_exists(&mem->dev.kobj,
+				 kobject_name(&node_devices[nid]->dev.kobj));
+}
+
 #ifdef CONFIG_HUGETLBFS
 /*
  * Handle per node hstate attribute [un]registration on transistions
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index c4deecc80f67..b99697a9dae6 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -153,6 +153,27 @@ void sysfs_remove_link(struct kobject *kobj, const char *name)
 }
 EXPORT_SYMBOL_GPL(sysfs_remove_link);
 
+/**
+ *	sysfs_link_exists - test if a symlink exists in object's directory.
+ *	@kobj:	object we're acting for.
+ *	@name:	name of the symlink to test.
+ */
+bool sysfs_link_exists(struct kobject *kobj, const char *name)
+{
+	struct kernfs_node *parent = NULL, *kn;
+
+	if (!kobj)
+		parent = sysfs_root_kn;
+	else
+		parent = kobj->sd;
+
+	kn = kernfs_find_and_get(parent, name);
+	kernfs_put(kn);
+
+	return kn != NULL;
+}
+EXPORT_SYMBOL_GPL(sysfs_link_exists);
+
 /**
  *	sysfs_rename_link_ns - rename symlink in object's directory.
  *	@kobj:	object we're acting for.
diff --git a/include/linux/node.h b/include/linux/node.h
index 4866f32a02d8..353853018689 100644
--- a/include/linux/node.h
+++ b/include/linux/node.h
@@ -138,6 +138,8 @@ extern void unregister_one_node(int nid);
 extern int register_cpu_under_node(unsigned int cpu, unsigned int nid);
 extern int unregister_cpu_under_node(unsigned int cpu, unsigned int nid);
 extern void unregister_memory_block_under_nodes(struct memory_block *mem_blk);
+extern bool memory_block_registered_under_node(struct memory_block *mem,
+					       int nid);
 
 extern int register_memory_node_under_compute_node(unsigned int mem_nid,
 						   unsigned int cpu_nid,
@@ -171,6 +173,11 @@ static inline int unregister_cpu_under_node(unsigned int cpu, unsigned int nid)
 static inline void unregister_memory_block_under_nodes(struct memory_block *mem_blk)
 {
 }
+static inline bool memory_block_registered_under_node(struct memory_block *mem,
+						      int nid)
+{
+	return true;
+}
 
 static inline void register_hugetlbfs_with_node(node_registration_func_t reg,
 						node_registration_func_t unreg)
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index fa7ee503fb76..3ae9a69a0786 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -265,6 +265,7 @@ int __must_check sysfs_create_link_nowarn(struct kobject *kobj,
 					  struct kobject *target,
 					  const char *name);
 void sysfs_remove_link(struct kobject *kobj, const char *name);
+bool sysfs_link_exists(struct kobject *kobj, const char *name);
 
 int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *target,
 			 const char *old_name, const char *new_name,
@@ -420,6 +421,11 @@ static inline void sysfs_remove_link(struct kobject *kobj, const char *name)
 {
 }
 
+static inline bool sysfs_link_exists(struct kobject *kobj, const char *name)
+{
+	return true;
+}
+
 static inline int sysfs_rename_link_ns(struct kobject *k, struct kobject *t,
 				       const char *old_name,
 				       const char *new_name, const void *ns)
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 0140c20837b6..c75e99b292cd 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1634,6 +1634,15 @@ static int check_cpu_on_node(pg_data_t *pgdat)
 	return 0;
 }
 
+static int check_no_memblock_registered_under_node_cb(struct memory_block *mem,
+						      void *arg)
+{
+
+	const int nid = *(int *)arg;
+
+	return memory_block_registered_under_node(mem, nid) ? -EEXIST : 0;
+}
+
 /**
  * try_offline_node
  * @nid: the node ID
@@ -1645,26 +1654,27 @@ static int check_cpu_on_node(pg_data_t *pgdat)
  */
 void try_offline_node(int nid)
 {
+	unsigned long end_pfn = section_nr_to_pfn(__highest_present_section_nr);
 	pg_data_t *pgdat = NODE_DATA(nid);
-	unsigned long start_pfn = pgdat->node_start_pfn;
-	unsigned long end_pfn = start_pfn + pgdat->node_spanned_pages;
-	unsigned long pfn;
-
-	for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
-		unsigned long section_nr = pfn_to_section_nr(pfn);
-
-		if (!present_section_nr(section_nr))
-			continue;
+	int rc;
 
-		if (pfn_to_nid(pfn) != nid)
-			continue;
+	/*
+	 * If the node still spans pages (especially ZONE_DEVICE), don't
+	 * offline it. A node spans memory after move_pfn_range_to_zone(),
+	 * e.g., after the memory block was onlined.
+	 */
+	if (pgdat->node_spanned_pages)
+		return;
 
-		/*
-		 * some memory sections of this node are not removed, and we
-		 * can't offline node now.
-		 */
+	/*
+	 * Especially offline memory blocks might not be spanned by the
+	 * node. They will get spanned by the node once they get onlined.
+	 * However, they link to the node in sysfs and can get onlined later.
+	 */
+	rc = walk_memory_blocks(0, PFN_PHYS(end_pfn), &nid,
+				check_no_memblock_registered_under_node_cb);
+	if (rc)
 		return;
-	}
 
 	if (check_cpu_on_node(pgdat))
 		return;
