Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

memcg: separate local_trylock for memcg and obj

The per-cpu stock_lock protects cached memcg and cached objcg and their
respective fields. However there is no dependency between these fields
and it is better to have fine grained separate locks for cached memcg and
cached objcg. This decoupling of locks allows us to make the memcg charge
cache and objcg charge cache to be nmi safe independently.

At the moment, memcg charge cache is already nmi safe and this decoupling
will allow to make memcg charge cache work without disabling irqs.

Link: https://lkml.kernel.org/r/20250506225533.2580386-3-shakeel.butt@linux.dev
Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Eric Dumaze <edumazet@google.com>
Cc: Jakub Kacinski <kuba@kernel.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Shakeel Butt and committed by
Andrew Morton
3523dd7a 2fba5961

+26 -23
+26 -23
mm/memcontrol.c
··· 1779 1779 */ 1780 1780 #define NR_MEMCG_STOCK 7 1781 1781 struct memcg_stock_pcp { 1782 - local_trylock_t stock_lock; 1782 + local_trylock_t memcg_lock; 1783 1783 uint8_t nr_pages[NR_MEMCG_STOCK]; 1784 1784 struct mem_cgroup *cached[NR_MEMCG_STOCK]; 1785 1785 1786 + local_trylock_t obj_lock; 1787 + unsigned int nr_bytes; 1786 1788 struct obj_cgroup *cached_objcg; 1787 1789 struct pglist_data *cached_pgdat; 1788 - unsigned int nr_bytes; 1789 1790 int nr_slab_reclaimable_b; 1790 1791 int nr_slab_unreclaimable_b; 1791 1792 ··· 1795 1794 #define FLUSHING_CACHED_CHARGE 0 1796 1795 }; 1797 1796 static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = { 1798 - .stock_lock = INIT_LOCAL_TRYLOCK(stock_lock), 1797 + .memcg_lock = INIT_LOCAL_TRYLOCK(memcg_lock), 1798 + .obj_lock = INIT_LOCAL_TRYLOCK(obj_lock), 1799 1799 }; 1800 1800 static DEFINE_MUTEX(percpu_charge_mutex); 1801 1801 ··· 1824 1822 int i; 1825 1823 1826 1824 if (nr_pages > MEMCG_CHARGE_BATCH || 1827 - !local_trylock_irqsave(&memcg_stock.stock_lock, flags)) 1825 + !local_trylock_irqsave(&memcg_stock.memcg_lock, flags)) 1828 1826 return ret; 1829 1827 1830 1828 stock = this_cpu_ptr(&memcg_stock); ··· 1841 1839 break; 1842 1840 } 1843 1841 1844 - local_unlock_irqrestore(&memcg_stock.stock_lock, flags); 1842 + local_unlock_irqrestore(&memcg_stock.memcg_lock, flags); 1845 1843 1846 1844 return ret; 1847 1845 } ··· 1887 1885 struct memcg_stock_pcp *stock; 1888 1886 unsigned long flags; 1889 1887 1890 - /* 1891 - * The only protection from cpu hotplug (memcg_hotplug_cpu_dead) vs. 1892 - * drain_stock races is that we always operate on local CPU stock 1893 - * here with IRQ disabled 1894 - */ 1895 - local_lock_irqsave(&memcg_stock.stock_lock, flags); 1888 + if (WARN_ONCE(!in_task(), "drain in non-task context")) 1889 + return; 1896 1890 1891 + local_lock_irqsave(&memcg_stock.obj_lock, flags); 1897 1892 stock = this_cpu_ptr(&memcg_stock); 1898 1893 drain_obj_stock(stock); 1894 + local_unlock_irqrestore(&memcg_stock.obj_lock, flags); 1895 + 1896 + local_lock_irqsave(&memcg_stock.memcg_lock, flags); 1897 + stock = this_cpu_ptr(&memcg_stock); 1899 1898 drain_stock_fully(stock); 1900 1899 clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags); 1901 - 1902 - local_unlock_irqrestore(&memcg_stock.stock_lock, flags); 1900 + local_unlock_irqrestore(&memcg_stock.memcg_lock, flags); 1903 1901 } 1904 1902 1905 1903 static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages) ··· 1922 1920 VM_WARN_ON_ONCE(mem_cgroup_is_root(memcg)); 1923 1921 1924 1922 if (nr_pages > MEMCG_CHARGE_BATCH || 1925 - !local_trylock_irqsave(&memcg_stock.stock_lock, flags)) { 1923 + !local_trylock_irqsave(&memcg_stock.memcg_lock, flags)) { 1926 1924 /* 1927 1925 * In case of larger than batch refill or unlikely failure to 1928 - * lock the percpu stock_lock, uncharge memcg directly. 1926 + * lock the percpu memcg_lock, uncharge memcg directly. 1929 1927 */ 1930 1928 memcg_uncharge(memcg, nr_pages); 1931 1929 return; ··· 1957 1955 WRITE_ONCE(stock->nr_pages[i], nr_pages); 1958 1956 } 1959 1957 1960 - local_unlock_irqrestore(&memcg_stock.stock_lock, flags); 1958 + local_unlock_irqrestore(&memcg_stock.memcg_lock, flags); 1961 1959 } 1962 1960 1963 1961 static bool is_drain_needed(struct memcg_stock_pcp *stock, ··· 2032 2030 2033 2031 stock = &per_cpu(memcg_stock, cpu); 2034 2032 2035 - /* drain_obj_stock requires stock_lock */ 2036 - local_lock_irqsave(&memcg_stock.stock_lock, flags); 2033 + /* drain_obj_stock requires obj_lock */ 2034 + local_lock_irqsave(&memcg_stock.obj_lock, flags); 2037 2035 drain_obj_stock(stock); 2038 - local_unlock_irqrestore(&memcg_stock.stock_lock, flags); 2036 + local_unlock_irqrestore(&memcg_stock.obj_lock, flags); 2039 2037 2038 + /* no need for the local lock */ 2040 2039 drain_stock_fully(stock); 2041 2040 2042 2041 return 0; ··· 2890 2887 unsigned long flags; 2891 2888 bool ret = false; 2892 2889 2893 - local_lock_irqsave(&memcg_stock.stock_lock, flags); 2890 + local_lock_irqsave(&memcg_stock.obj_lock, flags); 2894 2891 2895 2892 stock = this_cpu_ptr(&memcg_stock); 2896 2893 if (objcg == READ_ONCE(stock->cached_objcg) && stock->nr_bytes >= nr_bytes) { ··· 2901 2898 __account_obj_stock(objcg, stock, nr_bytes, pgdat, idx); 2902 2899 } 2903 2900 2904 - local_unlock_irqrestore(&memcg_stock.stock_lock, flags); 2901 + local_unlock_irqrestore(&memcg_stock.obj_lock, flags); 2905 2902 2906 2903 return ret; 2907 2904 } ··· 2990 2987 unsigned long flags; 2991 2988 unsigned int nr_pages = 0; 2992 2989 2993 - local_lock_irqsave(&memcg_stock.stock_lock, flags); 2990 + local_lock_irqsave(&memcg_stock.obj_lock, flags); 2994 2991 2995 2992 stock = this_cpu_ptr(&memcg_stock); 2996 2993 if (READ_ONCE(stock->cached_objcg) != objcg) { /* reset if necessary */ ··· 3012 3009 stock->nr_bytes &= (PAGE_SIZE - 1); 3013 3010 } 3014 3011 3015 - local_unlock_irqrestore(&memcg_stock.stock_lock, flags); 3012 + local_unlock_irqrestore(&memcg_stock.obj_lock, flags); 3016 3013 3017 3014 if (nr_pages) 3018 3015 obj_cgroup_uncharge_pages(objcg, nr_pages);