diff options
Diffstat (limited to 'kernel/livepatch/shadow.c')
-rw-r--r-- | kernel/livepatch/shadow.c | 82 |
1 files changed, 53 insertions, 29 deletions
diff --git a/kernel/livepatch/shadow.c b/kernel/livepatch/shadow.c index fdac27588d60..b10a0bbb7f84 100644 --- a/kernel/livepatch/shadow.c +++ b/kernel/livepatch/shadow.c | |||
@@ -113,8 +113,10 @@ void *klp_shadow_get(void *obj, unsigned long id) | |||
113 | } | 113 | } |
114 | EXPORT_SYMBOL_GPL(klp_shadow_get); | 114 | EXPORT_SYMBOL_GPL(klp_shadow_get); |
115 | 115 | ||
116 | static void *__klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data, | 116 | static void *__klp_shadow_get_or_alloc(void *obj, unsigned long id, |
117 | size_t size, gfp_t gfp_flags, bool warn_on_exist) | 117 | size_t size, gfp_t gfp_flags, |
118 | klp_shadow_ctor_t ctor, void *ctor_data, | ||
119 | bool warn_on_exist) | ||
118 | { | 120 | { |
119 | struct klp_shadow *new_shadow; | 121 | struct klp_shadow *new_shadow; |
120 | void *shadow_data; | 122 | void *shadow_data; |
@@ -125,18 +127,15 @@ static void *__klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data, | |||
125 | if (shadow_data) | 127 | if (shadow_data) |
126 | goto exists; | 128 | goto exists; |
127 | 129 | ||
128 | /* Allocate a new shadow variable for use inside the lock below */ | 130 | /* |
131 | * Allocate a new shadow variable. Fill it with zeroes by default. | ||
132 | * More complex setting can be done by @ctor function. But it is | ||
133 | * called only when the buffer is really used (under klp_shadow_lock). | ||
134 | */ | ||
129 | new_shadow = kzalloc(size + sizeof(*new_shadow), gfp_flags); | 135 | new_shadow = kzalloc(size + sizeof(*new_shadow), gfp_flags); |
130 | if (!new_shadow) | 136 | if (!new_shadow) |
131 | return NULL; | 137 | return NULL; |
132 | 138 | ||
133 | new_shadow->obj = obj; | ||
134 | new_shadow->id = id; | ||
135 | |||
136 | /* Initialize the shadow variable if data provided */ | ||
137 | if (data) | ||
138 | memcpy(new_shadow->data, data, size); | ||
139 | |||
140 | /* Look for <obj, id> again under the lock */ | 139 | /* Look for <obj, id> again under the lock */ |
141 | spin_lock_irqsave(&klp_shadow_lock, flags); | 140 | spin_lock_irqsave(&klp_shadow_lock, flags); |
142 | shadow_data = klp_shadow_get(obj, id); | 141 | shadow_data = klp_shadow_get(obj, id); |
@@ -150,6 +149,22 @@ static void *__klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data, | |||
150 | goto exists; | 149 | goto exists; |
151 | } | 150 | } |
152 | 151 | ||
152 | new_shadow->obj = obj; | ||
153 | new_shadow->id = id; | ||
154 | |||
155 | if (ctor) { | ||
156 | int err; | ||
157 | |||
158 | err = ctor(obj, new_shadow->data, ctor_data); | ||
159 | if (err) { | ||
160 | spin_unlock_irqrestore(&klp_shadow_lock, flags); | ||
161 | kfree(new_shadow); | ||
162 | pr_err("Failed to construct shadow variable <%p, %lx> (%d)\n", | ||
163 | obj, id, err); | ||
164 | return NULL; | ||
165 | } | ||
166 | } | ||
167 | |||
153 | /* No <obj, id> found, so attach the newly allocated one */ | 168 | /* No <obj, id> found, so attach the newly allocated one */ |
154 | hash_add_rcu(klp_shadow_hash, &new_shadow->node, | 169 | hash_add_rcu(klp_shadow_hash, &new_shadow->node, |
155 | (unsigned long)new_shadow->obj); | 170 | (unsigned long)new_shadow->obj); |
@@ -170,26 +185,32 @@ exists: | |||
170 | * klp_shadow_alloc() - allocate and add a new shadow variable | 185 | * klp_shadow_alloc() - allocate and add a new shadow variable |
171 | * @obj: pointer to parent object | 186 | * @obj: pointer to parent object |
172 | * @id: data identifier | 187 | * @id: data identifier |
173 | * @data: pointer to data to attach to parent | ||
174 | * @size: size of attached data | 188 | * @size: size of attached data |
175 | * @gfp_flags: GFP mask for allocation | 189 | * @gfp_flags: GFP mask for allocation |
190 | * @ctor: custom constructor to initialize the shadow data (optional) | ||
191 | * @ctor_data: pointer to any data needed by @ctor (optional) | ||
192 | * | ||
193 | * Allocates @size bytes for new shadow variable data using @gfp_flags. | ||
194 | * The data are zeroed by default. They are further initialized by @ctor | ||
195 | * function if it is not NULL. The new shadow variable is then added | ||
196 | * to the global hashtable. | ||
176 | * | 197 | * |
177 | * Allocates @size bytes for new shadow variable data using @gfp_flags | 198 | * If an existing <obj, id> shadow variable can be found, this routine will |
178 | * and copies @size bytes from @data into the new shadow variable's own | 199 | * issue a WARN, exit early and return NULL. |
179 | * data space. If @data is NULL, @size bytes are still allocated, but | ||
180 | * no copy is performed. The new shadow variable is then added to the | ||
181 | * global hashtable. | ||
182 | * | 200 | * |
183 | * If an existing <obj, id> shadow variable can be found, this routine | 201 | * This function guarantees that the constructor function is called only when |
184 | * will issue a WARN, exit early and return NULL. | 202 | * the variable did not exist before. The cost is that @ctor is called |
203 | * in atomic context under a spin lock. | ||
185 | * | 204 | * |
186 | * Return: the shadow variable data element, NULL on duplicate or | 205 | * Return: the shadow variable data element, NULL on duplicate or |
187 | * failure. | 206 | * failure. |
188 | */ | 207 | */ |
189 | void *klp_shadow_alloc(void *obj, unsigned long id, void *data, | 208 | void *klp_shadow_alloc(void *obj, unsigned long id, |
190 | size_t size, gfp_t gfp_flags) | 209 | size_t size, gfp_t gfp_flags, |
210 | klp_shadow_ctor_t ctor, void *ctor_data) | ||
191 | { | 211 | { |
192 | return __klp_shadow_get_or_alloc(obj, id, data, size, gfp_flags, true); | 212 | return __klp_shadow_get_or_alloc(obj, id, size, gfp_flags, |
213 | ctor, ctor_data, true); | ||
193 | } | 214 | } |
194 | EXPORT_SYMBOL_GPL(klp_shadow_alloc); | 215 | EXPORT_SYMBOL_GPL(klp_shadow_alloc); |
195 | 216 | ||
@@ -197,25 +218,28 @@ EXPORT_SYMBOL_GPL(klp_shadow_alloc); | |||
197 | * klp_shadow_get_or_alloc() - get existing or allocate a new shadow variable | 218 | * klp_shadow_get_or_alloc() - get existing or allocate a new shadow variable |
198 | * @obj: pointer to parent object | 219 | * @obj: pointer to parent object |
199 | * @id: data identifier | 220 | * @id: data identifier |
200 | * @data: pointer to data to attach to parent | ||
201 | * @size: size of attached data | 221 | * @size: size of attached data |
202 | * @gfp_flags: GFP mask for allocation | 222 | * @gfp_flags: GFP mask for allocation |
223 | * @ctor: custom constructor to initialize the shadow data (optional) | ||
224 | * @ctor_data: pointer to any data needed by @ctor (optional) | ||
203 | * | 225 | * |
204 | * Returns a pointer to existing shadow data if an <obj, id> shadow | 226 | * Returns a pointer to existing shadow data if an <obj, id> shadow |
205 | * variable is already present. Otherwise, it creates a new shadow | 227 | * variable is already present. Otherwise, it creates a new shadow |
206 | * variable like klp_shadow_alloc(). | 228 | * variable like klp_shadow_alloc(). |
207 | * | 229 | * |
208 | * This function guarantees that only one shadow variable exists with | 230 | * This function guarantees that only one shadow variable exists with the given |
209 | * the given @id for the given @obj. It also guarantees that the shadow | 231 | * @id for the given @obj. It also guarantees that the constructor function |
210 | * variable will be initialized by the given @data only when it did not | 232 | * will be called only when the variable did not exist before. The cost is |
211 | * exist before. | 233 | * that @ctor is called in atomic context under a spin lock. |
212 | * | 234 | * |
213 | * Return: the shadow variable data element, NULL on failure. | 235 | * Return: the shadow variable data element, NULL on failure. |
214 | */ | 236 | */ |
215 | void *klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data, | 237 | void *klp_shadow_get_or_alloc(void *obj, unsigned long id, |
216 | size_t size, gfp_t gfp_flags) | 238 | size_t size, gfp_t gfp_flags, |
239 | klp_shadow_ctor_t ctor, void *ctor_data) | ||
217 | { | 240 | { |
218 | return __klp_shadow_get_or_alloc(obj, id, data, size, gfp_flags, false); | 241 | return __klp_shadow_get_or_alloc(obj, id, size, gfp_flags, |
242 | ctor, ctor_data, false); | ||
219 | } | 243 | } |
220 | EXPORT_SYMBOL_GPL(klp_shadow_get_or_alloc); | 244 | EXPORT_SYMBOL_GPL(klp_shadow_get_or_alloc); |
221 | 245 | ||