mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
mm/memory-tiers: unregister empty tiers outside memory_tier_lock
When offlining a node's last memory empties its tier,
clear_node_memory_tier() calls device_unregister() while holding
memory_tier_lock. A concurrent sysfs read can hold a kernfs active
reference and block in nodelist_show() on that mutex. device_unregister()
then waits in kernfs_drain() for the read to finish and release its active
reference, while the reader waits for the offlining task to release the
mutex.
Return the node's former tier from clear_node_memory_tier(), making the
helper more similar to set_node_memory_tier(), which also returns a
memory_tier pointer. Let the caller unlink an empty tier under
memory_tier_lock before rebuilding demotion targets, then unregister its
device after dropping the lock.
Fixes: 9832fb8783 ("mm/demotion: expose memory tier details via sysfs")
Cc: stable@vger.kernel.org
Reported-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Link: https://patch.msgid.link/20261031-fixes_core-v3-46-d6437171ecc3@kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
This commit is contained in:
parent
e8abc61d8a
commit
500624f28e
|
|
@ -572,21 +572,14 @@ static struct memory_tier *set_node_memory_tier(int node)
|
|||
return memtier;
|
||||
}
|
||||
|
||||
static void destroy_memory_tier(struct memory_tier *memtier)
|
||||
static struct memory_tier *clear_node_memory_tier(int node)
|
||||
{
|
||||
list_del(&memtier->list);
|
||||
device_unregister(&memtier->dev);
|
||||
}
|
||||
|
||||
static bool clear_node_memory_tier(int node)
|
||||
{
|
||||
bool cleared = false;
|
||||
pg_data_t *pgdat;
|
||||
struct memory_tier *memtier;
|
||||
|
||||
pgdat = NODE_DATA(node);
|
||||
if (!pgdat)
|
||||
return false;
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Make sure that anybody looking at NODE_DATA who finds
|
||||
|
|
@ -604,14 +597,10 @@ static bool clear_node_memory_tier(int node)
|
|||
synchronize_rcu();
|
||||
memtype = node_memory_types[node].memtype;
|
||||
node_clear(node, memtype->nodes);
|
||||
if (nodes_empty(memtype->nodes)) {
|
||||
if (nodes_empty(memtype->nodes))
|
||||
list_del_init(&memtype->tier_sibling);
|
||||
if (list_empty(&memtier->memory_types))
|
||||
destroy_memory_tier(memtier);
|
||||
}
|
||||
cleared = true;
|
||||
}
|
||||
return cleared;
|
||||
return memtier;
|
||||
}
|
||||
|
||||
static void release_memtype(struct kref *kref)
|
||||
|
|
@ -884,13 +873,21 @@ static int __meminit memtier_hotplug_callback(struct notifier_block *self,
|
|||
{
|
||||
struct memory_tier *memtier;
|
||||
struct node_notify *nn = _arg;
|
||||
bool empty = false;
|
||||
|
||||
switch (action) {
|
||||
case NODE_REMOVED_LAST_MEMORY:
|
||||
mutex_lock(&memory_tier_lock);
|
||||
if (clear_node_memory_tier(nn->nid))
|
||||
memtier = clear_node_memory_tier(nn->nid);
|
||||
if (memtier) {
|
||||
empty = list_empty(&memtier->memory_types);
|
||||
if (empty)
|
||||
list_del(&memtier->list);
|
||||
establish_demotion_targets();
|
||||
}
|
||||
mutex_unlock(&memory_tier_lock);
|
||||
if (empty)
|
||||
device_unregister(&memtier->dev);
|
||||
break;
|
||||
case NODE_ADDED_FIRST_MEMORY:
|
||||
mutex_lock(&memory_tier_lock);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user