| 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | #ifndef _LINUX_NS_COMMON_H |
| 3 | #define _LINUX_NS_COMMON_H |
| 4 | |
| 5 | #include <linux/ns/ns_common_types.h> |
| 6 | #include <linux/refcount.h> |
| 7 | #include <linux/vfsdebug.h> |
| 8 | #include <uapi/linux/sched.h> |
| 9 | #include <uapi/linux/nsfs.h> |
| 10 | |
| 11 | bool is_current_namespace(struct ns_common *ns); |
| 12 | int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_operations *ops, int inum); |
| 13 | void __ns_common_free(struct ns_common *ns); |
| 14 | struct ns_common *__must_check ns_owner(struct ns_common *ns); |
| 15 | |
| 16 | static __always_inline bool is_ns_init_inum(const struct ns_common *ns) |
| 17 | { |
| 18 | VFS_WARN_ON_ONCE(ns->inum == 0); |
| 19 | return unlikely(in_range(ns->inum, MNT_NS_INIT_INO, |
| 20 | IPC_NS_INIT_INO - MNT_NS_INIT_INO + 1)); |
| 21 | } |
| 22 | |
| 23 | static __always_inline bool is_ns_init_id(const struct ns_common *ns) |
| 24 | { |
| 25 | VFS_WARN_ON_ONCE(ns->ns_id == 0); |
| 26 | return ns->ns_id <= NS_LAST_INIT_ID; |
| 27 | } |
| 28 | |
| 29 | #define NS_COMMON_INIT(nsname) \ |
| 30 | { \ |
| 31 | .ns_type = ns_common_type(&nsname), \ |
| 32 | .ns_id = ns_init_id(&nsname), \ |
| 33 | .inum = ns_init_inum(&nsname), \ |
| 34 | .ops = to_ns_operations(&nsname), \ |
| 35 | .stashed = NULL, \ |
| 36 | .__ns_ref = REFCOUNT_INIT(1), \ |
| 37 | .__ns_ref_active = ATOMIC_INIT(1), \ |
| 38 | .ns_unified_node.ns_list_entry = LIST_HEAD_INIT(nsname.ns.ns_unified_node.ns_list_entry), \ |
| 39 | .ns_tree_node.ns_list_entry = LIST_HEAD_INIT(nsname.ns.ns_tree_node.ns_list_entry), \ |
| 40 | .ns_owner_node.ns_list_entry = LIST_HEAD_INIT(nsname.ns.ns_owner_node.ns_list_entry), \ |
| 41 | .ns_owner_root.ns_list_head = LIST_HEAD_INIT(nsname.ns.ns_owner_root.ns_list_head), \ |
| 42 | } |
| 43 | |
| 44 | #define ns_common_init(__ns) \ |
| 45 | __ns_common_init(to_ns_common(__ns), \ |
| 46 | ns_common_type(__ns), \ |
| 47 | to_ns_operations(__ns), \ |
| 48 | (((__ns) == ns_init_ns(__ns)) ? ns_init_inum(__ns) : 0)) |
| 49 | |
| 50 | #define ns_common_init_inum(__ns, __inum) \ |
| 51 | __ns_common_init(to_ns_common(__ns), \ |
| 52 | ns_common_type(__ns), \ |
| 53 | to_ns_operations(__ns), \ |
| 54 | __inum) |
| 55 | |
| 56 | #define ns_common_free(__ns) __ns_common_free(to_ns_common((__ns))) |
| 57 | |
| 58 | static __always_inline __must_check int __ns_ref_active_read(const struct ns_common *ns) |
| 59 | { |
| 60 | return atomic_read(v: &ns->__ns_ref_active); |
| 61 | } |
| 62 | |
| 63 | static __always_inline __must_check int __ns_ref_read(const struct ns_common *ns) |
| 64 | { |
| 65 | return refcount_read(r: &ns->__ns_ref); |
| 66 | } |
| 67 | |
| 68 | static __always_inline __must_check bool __ns_ref_put(struct ns_common *ns) |
| 69 | { |
| 70 | if (is_ns_init_id(ns)) { |
| 71 | VFS_WARN_ON_ONCE(__ns_ref_read(ns) != 1); |
| 72 | VFS_WARN_ON_ONCE(__ns_ref_active_read(ns) != 1); |
| 73 | return false; |
| 74 | } |
| 75 | if (refcount_dec_and_test(r: &ns->__ns_ref)) { |
| 76 | VFS_WARN_ON_ONCE(__ns_ref_active_read(ns)); |
| 77 | return true; |
| 78 | } |
| 79 | return false; |
| 80 | } |
| 81 | |
| 82 | static __always_inline __must_check bool __ns_ref_get(struct ns_common *ns) |
| 83 | { |
| 84 | if (is_ns_init_id(ns)) { |
| 85 | VFS_WARN_ON_ONCE(__ns_ref_read(ns) != 1); |
| 86 | VFS_WARN_ON_ONCE(__ns_ref_active_read(ns) != 1); |
| 87 | return true; |
| 88 | } |
| 89 | if (refcount_inc_not_zero(r: &ns->__ns_ref)) |
| 90 | return true; |
| 91 | VFS_WARN_ON_ONCE(__ns_ref_active_read(ns)); |
| 92 | return false; |
| 93 | } |
| 94 | |
| 95 | static __always_inline void __ns_ref_inc(struct ns_common *ns) |
| 96 | { |
| 97 | if (is_ns_init_id(ns)) { |
| 98 | VFS_WARN_ON_ONCE(__ns_ref_read(ns) != 1); |
| 99 | VFS_WARN_ON_ONCE(__ns_ref_active_read(ns) != 1); |
| 100 | return; |
| 101 | } |
| 102 | refcount_inc(r: &ns->__ns_ref); |
| 103 | } |
| 104 | |
| 105 | static __always_inline __must_check bool __ns_ref_dec_and_lock(struct ns_common *ns, |
| 106 | spinlock_t *ns_lock) |
| 107 | { |
| 108 | if (is_ns_init_id(ns)) { |
| 109 | VFS_WARN_ON_ONCE(__ns_ref_read(ns) != 1); |
| 110 | VFS_WARN_ON_ONCE(__ns_ref_active_read(ns) != 1); |
| 111 | return false; |
| 112 | } |
| 113 | return refcount_dec_and_lock(r: &ns->__ns_ref, lock: ns_lock); |
| 114 | } |
| 115 | |
| 116 | #define ns_ref_read(__ns) __ns_ref_read(to_ns_common((__ns))) |
| 117 | #define ns_ref_inc(__ns) \ |
| 118 | do { if (__ns) __ns_ref_inc(to_ns_common((__ns))); } while (0) |
| 119 | #define ns_ref_get(__ns) \ |
| 120 | ((__ns) ? __ns_ref_get(to_ns_common((__ns))) : false) |
| 121 | #define ns_ref_put(__ns) \ |
| 122 | ((__ns) ? __ns_ref_put(to_ns_common((__ns))) : false) |
| 123 | #define ns_ref_put_and_lock(__ns, __ns_lock) \ |
| 124 | ((__ns) ? __ns_ref_dec_and_lock(to_ns_common((__ns)), __ns_lock) : false) |
| 125 | |
| 126 | #define ns_ref_active_read(__ns) \ |
| 127 | ((__ns) ? __ns_ref_active_read(to_ns_common(__ns)) : 0) |
| 128 | |
| 129 | void __ns_ref_active_put(struct ns_common *ns); |
| 130 | |
| 131 | #define ns_ref_active_put(__ns) \ |
| 132 | do { if (__ns) __ns_ref_active_put(to_ns_common(__ns)); } while (0) |
| 133 | |
| 134 | static __always_inline struct ns_common *__must_check ns_get_unless_inactive(struct ns_common *ns) |
| 135 | { |
| 136 | if (!__ns_ref_active_read(ns)) { |
| 137 | VFS_WARN_ON_ONCE(is_ns_init_id(ns)); |
| 138 | return NULL; |
| 139 | } |
| 140 | if (!__ns_ref_get(ns)) |
| 141 | return NULL; |
| 142 | return ns; |
| 143 | } |
| 144 | |
| 145 | void __ns_ref_active_get(struct ns_common *ns); |
| 146 | |
| 147 | #define ns_ref_active_get(__ns) \ |
| 148 | do { if (__ns) __ns_ref_active_get(to_ns_common(__ns)); } while (0) |
| 149 | |
| 150 | #endif |
| 151 | |