diff options
Diffstat (limited to 'arch/sparc64/kernel/mdesc.c')
-rw-r--r-- | arch/sparc64/kernel/mdesc.c | 78 |
1 files changed, 75 insertions, 3 deletions
diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c index de5310ffdb48..302ba5e5a0bb 100644 --- a/arch/sparc64/kernel/mdesc.c +++ b/arch/sparc64/kernel/mdesc.c | |||
@@ -137,7 +137,7 @@ static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) | |||
137 | sizeof(struct mdesc_hdr) + | 137 | sizeof(struct mdesc_hdr) + |
138 | mdesc_size); | 138 | mdesc_size); |
139 | 139 | ||
140 | base = kmalloc(handle_size + 15, GFP_KERNEL); | 140 | base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL); |
141 | if (base) { | 141 | if (base) { |
142 | struct mdesc_handle *hp; | 142 | struct mdesc_handle *hp; |
143 | unsigned long addr; | 143 | unsigned long addr; |
@@ -214,18 +214,83 @@ void mdesc_release(struct mdesc_handle *hp) | |||
214 | } | 214 | } |
215 | EXPORT_SYMBOL(mdesc_release); | 215 | EXPORT_SYMBOL(mdesc_release); |
216 | 216 | ||
217 | static DEFINE_MUTEX(mdesc_mutex); | ||
218 | static struct mdesc_notifier_client *client_list; | ||
219 | |||
220 | void mdesc_register_notifier(struct mdesc_notifier_client *client) | ||
221 | { | ||
222 | u64 node; | ||
223 | |||
224 | mutex_lock(&mdesc_mutex); | ||
225 | client->next = client_list; | ||
226 | client_list = client; | ||
227 | |||
228 | mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name) | ||
229 | client->add(cur_mdesc, node); | ||
230 | |||
231 | mutex_unlock(&mdesc_mutex); | ||
232 | } | ||
233 | |||
234 | /* Run 'func' on nodes which are in A but not in B. */ | ||
235 | static void invoke_on_missing(const char *name, | ||
236 | struct mdesc_handle *a, | ||
237 | struct mdesc_handle *b, | ||
238 | void (*func)(struct mdesc_handle *, u64)) | ||
239 | { | ||
240 | u64 node; | ||
241 | |||
242 | mdesc_for_each_node_by_name(a, node, name) { | ||
243 | const u64 *id = mdesc_get_property(a, node, "id", NULL); | ||
244 | int found = 0; | ||
245 | u64 fnode; | ||
246 | |||
247 | mdesc_for_each_node_by_name(b, fnode, name) { | ||
248 | const u64 *fid = mdesc_get_property(b, fnode, | ||
249 | "id", NULL); | ||
250 | |||
251 | if (*id == *fid) { | ||
252 | found = 1; | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | if (!found) | ||
257 | func(a, node); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | static void notify_one(struct mdesc_notifier_client *p, | ||
262 | struct mdesc_handle *old_hp, | ||
263 | struct mdesc_handle *new_hp) | ||
264 | { | ||
265 | invoke_on_missing(p->node_name, old_hp, new_hp, p->remove); | ||
266 | invoke_on_missing(p->node_name, new_hp, old_hp, p->add); | ||
267 | } | ||
268 | |||
269 | static void mdesc_notify_clients(struct mdesc_handle *old_hp, | ||
270 | struct mdesc_handle *new_hp) | ||
271 | { | ||
272 | struct mdesc_notifier_client *p = client_list; | ||
273 | |||
274 | while (p) { | ||
275 | notify_one(p, old_hp, new_hp); | ||
276 | p = p->next; | ||
277 | } | ||
278 | } | ||
279 | |||
217 | void mdesc_update(void) | 280 | void mdesc_update(void) |
218 | { | 281 | { |
219 | unsigned long len, real_len, status; | 282 | unsigned long len, real_len, status; |
220 | struct mdesc_handle *hp, *orig_hp; | 283 | struct mdesc_handle *hp, *orig_hp; |
221 | unsigned long flags; | 284 | unsigned long flags; |
222 | 285 | ||
286 | mutex_lock(&mdesc_mutex); | ||
287 | |||
223 | (void) sun4v_mach_desc(0UL, 0UL, &len); | 288 | (void) sun4v_mach_desc(0UL, 0UL, &len); |
224 | 289 | ||
225 | hp = mdesc_alloc(len, &kmalloc_mdesc_memops); | 290 | hp = mdesc_alloc(len, &kmalloc_mdesc_memops); |
226 | if (!hp) { | 291 | if (!hp) { |
227 | printk(KERN_ERR "MD: mdesc alloc fails\n"); | 292 | printk(KERN_ERR "MD: mdesc alloc fails\n"); |
228 | return; | 293 | goto out; |
229 | } | 294 | } |
230 | 295 | ||
231 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); | 296 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); |
@@ -234,18 +299,25 @@ void mdesc_update(void) | |||
234 | status); | 299 | status); |
235 | atomic_dec(&hp->refcnt); | 300 | atomic_dec(&hp->refcnt); |
236 | mdesc_free(hp); | 301 | mdesc_free(hp); |
237 | return; | 302 | goto out; |
238 | } | 303 | } |
239 | 304 | ||
240 | spin_lock_irqsave(&mdesc_lock, flags); | 305 | spin_lock_irqsave(&mdesc_lock, flags); |
241 | orig_hp = cur_mdesc; | 306 | orig_hp = cur_mdesc; |
242 | cur_mdesc = hp; | 307 | cur_mdesc = hp; |
308 | spin_unlock_irqrestore(&mdesc_lock, flags); | ||
243 | 309 | ||
310 | mdesc_notify_clients(orig_hp, hp); | ||
311 | |||
312 | spin_lock_irqsave(&mdesc_lock, flags); | ||
244 | if (atomic_dec_and_test(&orig_hp->refcnt)) | 313 | if (atomic_dec_and_test(&orig_hp->refcnt)) |
245 | mdesc_free(orig_hp); | 314 | mdesc_free(orig_hp); |
246 | else | 315 | else |
247 | list_add(&orig_hp->list, &mdesc_zombie_list); | 316 | list_add(&orig_hp->list, &mdesc_zombie_list); |
248 | spin_unlock_irqrestore(&mdesc_lock, flags); | 317 | spin_unlock_irqrestore(&mdesc_lock, flags); |
318 | |||
319 | out: | ||
320 | mutex_unlock(&mdesc_mutex); | ||
249 | } | 321 | } |
250 | 322 | ||
251 | static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) | 323 | static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) |