/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2018-2019 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * simple long counter wrapper */ #ifndef __AUFS_LCNT_H__ #define __AUFS_LCNT_H__ #ifdef __KERNEL__ #include "debug.h" #define AuLCntATOMIC 1 #define AuLCntPCPUCNT 2 /* * why does percpu_refcount require extra synchronize_rcu()s in * au_br_do_free() */ #define AuLCntPCPUREF 3 /* #define AuLCntChosen AuLCntATOMIC */ #define AuLCntChosen AuLCntPCPUCNT /* #define AuLCntChosen AuLCntPCPUREF */ #if AuLCntChosen == AuLCntATOMIC #include typedef atomic_long_t au_lcnt_t; static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused) { atomic_long_set(cnt, 0); return 0; } static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) { /* empty */ } static inline void au_lcnt_fin(au_lcnt_t *cnt __maybe_unused, int do_sync __maybe_unused) { /* empty */ } static inline void au_lcnt_inc(au_lcnt_t *cnt) { atomic_long_inc(cnt); } static inline void au_lcnt_dec(au_lcnt_t *cnt) { atomic_long_dec(cnt); } static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused) { return atomic_long_read(cnt); } #endif #if AuLCntChosen == AuLCntPCPUCNT #include typedef struct percpu_counter au_lcnt_t; static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused) { return percpu_counter_init(cnt, 0, GFP_NOFS); } static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) { /* empty */ } static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync __maybe_unused) { percpu_counter_destroy(cnt); } static inline void au_lcnt_inc(au_lcnt_t *cnt) { percpu_counter_inc(cnt); } static inline void au_lcnt_dec(au_lcnt_t *cnt) { percpu_counter_dec(cnt); } static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused) { s64 n; n = percpu_counter_sum(cnt); BUG_ON(n < 0); if (LONG_MAX != LLONG_MAX && n > LONG_MAX) AuWarn1("%s\n", "wrap-around"); return n; } #endif #if AuLCntChosen == AuLCntPCPUREF #include typedef struct percpu_ref au_lcnt_t; static inline int au_lcnt_init(au_lcnt_t *cnt, percpu_ref_func_t *release) { if (!release) release = percpu_ref_exit; return percpu_ref_init(cnt, release, /*percpu mode*/0, GFP_NOFS); } static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) { synchronize_rcu(); } static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync) { percpu_ref_kill(cnt); if (do_sync) au_lcnt_wait_for_fin(cnt); } static inline void au_lcnt_inc(au_lcnt_t *cnt) { percpu_ref_get(cnt); } static inline void au_lcnt_dec(au_lcnt_t *cnt) { percpu_ref_put(cnt); } /* * avoid calling this func as possible. */ static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev) { long l; percpu_ref_switch_to_atomic_sync(cnt); l = atomic_long_read(&cnt->count); if (do_rev) percpu_ref_switch_to_percpu(cnt); /* percpu_ref is initialized by 1 instead of 0 */ return l - 1; } #endif #ifdef CONFIG_AUFS_DEBUG #define AuLCntZero(val) do { \ long l = val; \ if (l) \ AuDbg("%s = %ld\n", #val, l); \ } while (0) #else #define AuLCntZero(val) do {} while (0) #endif #endif /* __KERNEL__ */ #endif /* __AUFS_LCNT_H__ */