Commit 7ad6e9435596f692ff65f399da12816c94960185

Authored by Huang Ying
Committed by Len Brown
1 parent ad4ecef2f1

ACPI, APEI, Manage GHES as platform devices

Register GHES during HEST initialization as platform devices. And make
GHES driver into platform device driver. So that the GHES driver
module can be loaded automatically when there are GHES available.

Signed-off-by: Huang Ying <ying.huang@intel.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>

Showing 2 changed files with 145 additions and 71 deletions Side-by-side Diff

drivers/acpi/apei/ghes.c
... ... @@ -41,6 +41,8 @@
41 41 #include <linux/interrupt.h>
42 42 #include <linux/cper.h>
43 43 #include <linux/kdebug.h>
  44 +#include <linux/platform_device.h>
  45 +#include <linux/mutex.h>
44 46 #include <acpi/apei.h>
45 47 #include <acpi/atomicio.h>
46 48 #include <acpi/hed.h>
... ... @@ -87,6 +89,7 @@
87 89 * used for that.
88 90 */
89 91 static LIST_HEAD(ghes_sci);
  92 +static DEFINE_MUTEX(ghes_list_mutex);
90 93  
91 94 static struct ghes *ghes_new(struct acpi_hest_generic *generic)
92 95 {
93 96  
94 97  
95 98  
... ... @@ -293,18 +296,15 @@
293 296 .notifier_call = ghes_notify_sci,
294 297 };
295 298  
296   -static int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data)
  299 +static int __devinit ghes_probe(struct platform_device *ghes_dev)
297 300 {
298 301 struct acpi_hest_generic *generic;
299 302 struct ghes *ghes = NULL;
300   - int rc = 0;
  303 + int rc = -EINVAL;
301 304  
302   - if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR)
303   - return 0;
304   -
305   - generic = (struct acpi_hest_generic *)hest_hdr;
  305 + generic = ghes_dev->dev.platform_data;
306 306 if (!generic->enabled)
307   - return 0;
  307 + return -ENODEV;
308 308  
309 309 if (generic->error_block_length <
310 310 sizeof(struct acpi_hest_generic_status)) {
311 311  
312 312  
313 313  
314 314  
315 315  
316 316  
317 317  
318 318  
319 319  
320 320  
321 321  
322 322  
... ... @@ -327,62 +327,91 @@
327 327 ghes = NULL;
328 328 goto err;
329 329 }
330   - switch (generic->notify.type) {
331   - case ACPI_HEST_NOTIFY_POLLED:
332   - pr_warning(GHES_PFX
333   -"Generic hardware error source: %d notified via POLL is not supported!\n",
334   - generic->header.source_id);
335   - break;
336   - case ACPI_HEST_NOTIFY_EXTERNAL:
337   - case ACPI_HEST_NOTIFY_LOCAL:
338   - pr_warning(GHES_PFX
339   -"Generic hardware error source: %d notified via IRQ is not supported!\n",
340   - generic->header.source_id);
341   - break;
342   - case ACPI_HEST_NOTIFY_SCI:
  330 + if (generic->notify.type == ACPI_HEST_NOTIFY_SCI) {
  331 + mutex_lock(&ghes_list_mutex);
343 332 if (list_empty(&ghes_sci))
344 333 register_acpi_hed_notifier(&ghes_notifier_sci);
345 334 list_add_rcu(&ghes->list, &ghes_sci);
346   - break;
347   - case ACPI_HEST_NOTIFY_NMI:
348   - pr_warning(GHES_PFX
349   -"Generic hardware error source: %d notified via NMI is not supported!\n",
350   - generic->header.source_id);
351   - break;
352   - default:
353   - pr_warning(FW_WARN GHES_PFX
354   - "Unknown notification type: %u for generic hardware error source: %d\n",
355   - generic->notify.type, generic->header.source_id);
356   - break;
  335 + mutex_unlock(&ghes_list_mutex);
  336 + } else {
  337 + unsigned char *notify = NULL;
  338 +
  339 + switch (generic->notify.type) {
  340 + case ACPI_HEST_NOTIFY_POLLED:
  341 + notify = "POLL";
  342 + break;
  343 + case ACPI_HEST_NOTIFY_EXTERNAL:
  344 + case ACPI_HEST_NOTIFY_LOCAL:
  345 + notify = "IRQ";
  346 + break;
  347 + case ACPI_HEST_NOTIFY_NMI:
  348 + notify = "NMI";
  349 + break;
  350 + }
  351 + if (notify) {
  352 + pr_warning(GHES_PFX
  353 +"Generic hardware error source: %d notified via %s is not supported!\n",
  354 + generic->header.source_id, notify);
  355 + } else {
  356 + pr_warning(FW_WARN GHES_PFX
  357 +"Unknown notification type: %u for generic hardware error source: %d\n",
  358 + generic->notify.type, generic->header.source_id);
  359 + }
  360 + rc = -ENODEV;
  361 + goto err;
357 362 }
  363 + platform_set_drvdata(ghes_dev, ghes);
358 364  
359 365 return 0;
360 366 err:
361   - if (ghes)
  367 + if (ghes) {
362 368 ghes_fini(ghes);
  369 + kfree(ghes);
  370 + }
363 371 return rc;
364 372 }
365 373  
366   -static void ghes_cleanup(void)
  374 +static int __devexit ghes_remove(struct platform_device *ghes_dev)
367 375 {
368   - struct ghes *ghes, *nghes;
  376 + struct ghes *ghes;
  377 + struct acpi_hest_generic *generic;
369 378  
370   - if (!list_empty(&ghes_sci))
371   - unregister_acpi_hed_notifier(&ghes_notifier_sci);
  379 + ghes = platform_get_drvdata(ghes_dev);
  380 + generic = ghes->generic;
372 381  
  382 + switch (generic->notify.type) {
  383 + case ACPI_HEST_NOTIFY_SCI:
  384 + mutex_lock(&ghes_list_mutex);
  385 + list_del_rcu(&ghes->list);
  386 + if (list_empty(&ghes_sci))
  387 + unregister_acpi_hed_notifier(&ghes_notifier_sci);
  388 + mutex_unlock(&ghes_list_mutex);
  389 + break;
  390 + default:
  391 + BUG();
  392 + break;
  393 + }
  394 +
373 395 synchronize_rcu();
  396 + ghes_fini(ghes);
  397 + kfree(ghes);
374 398  
375   - list_for_each_entry_safe(ghes, nghes, &ghes_sci, list) {
376   - list_del(&ghes->list);
377   - ghes_fini(ghes);
378   - kfree(ghes);
379   - }
  399 + platform_set_drvdata(ghes_dev, NULL);
  400 +
  401 + return 0;
380 402 }
381 403  
  404 +static struct platform_driver ghes_platform_driver = {
  405 + .driver = {
  406 + .name = "GHES",
  407 + .owner = THIS_MODULE,
  408 + },
  409 + .probe = ghes_probe,
  410 + .remove = ghes_remove,
  411 +};
  412 +
382 413 static int __init ghes_init(void)
383 414 {
384   - int rc;
385   -
386 415 if (acpi_disabled)
387 416 return -ENODEV;
388 417  
389 418  
... ... @@ -391,32 +420,12 @@
391 420 return -EINVAL;
392 421 }
393 422  
394   - rc = apei_hest_parse(hest_ghes_parse, NULL);
395   - if (rc) {
396   - pr_err(GHES_PFX
397   - "Error during parsing HEST generic hardware error sources.\n");
398   - goto err_cleanup;
399   - }
400   -
401   - if (list_empty(&ghes_sci)) {
402   - pr_info(GHES_PFX
403   - "No functional generic hardware error sources.\n");
404   - rc = -ENODEV;
405   - goto err_cleanup;
406   - }
407   -
408   - pr_info(GHES_PFX
409   - "Generic Hardware Error Source support is initialized.\n");
410   -
411   - return 0;
412   -err_cleanup:
413   - ghes_cleanup();
414   - return rc;
  423 + return platform_driver_register(&ghes_platform_driver);
415 424 }
416 425  
417 426 static void __exit ghes_exit(void)
418 427 {
419   - ghes_cleanup();
  428 + platform_driver_unregister(&ghes_platform_driver);
420 429 }
421 430  
422 431 module_init(ghes_init);
... ... @@ -425,4 +434,5 @@
425 434 MODULE_AUTHOR("Huang Ying");
426 435 MODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
427 436 MODULE_LICENSE("GPL");
  437 +MODULE_ALIAS("platform:GHES");
drivers/acpi/apei/hest.c
... ... @@ -34,6 +34,7 @@
34 34 #include <linux/kdebug.h>
35 35 #include <linux/highmem.h>
36 36 #include <linux/io.h>
  37 +#include <linux/platform_device.h>
37 38 #include <acpi/apei.h>
38 39  
39 40 #include "apei-internal.h"
... ... @@ -47,11 +48,6 @@
47 48  
48 49 static struct acpi_table_hest *hest_tab;
49 50  
50   -static int hest_void_parse(struct acpi_hest_header *hest_hdr, void *data)
51   -{
52   - return 0;
53   -}
54   -
55 51 static int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
56 52 [ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */
57 53 [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1,
... ... @@ -125,6 +121,69 @@
125 121 }
126 122 EXPORT_SYMBOL_GPL(apei_hest_parse);
127 123  
  124 +struct ghes_arr {
  125 + struct platform_device **ghes_devs;
  126 + unsigned int count;
  127 +};
  128 +
  129 +static int hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void *data)
  130 +{
  131 + int *count = data;
  132 +
  133 + if (hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR)
  134 + (*count)++;
  135 + return 0;
  136 +}
  137 +
  138 +static int hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data)
  139 +{
  140 + struct acpi_hest_generic *generic;
  141 + struct platform_device *ghes_dev;
  142 + struct ghes_arr *ghes_arr = data;
  143 + int rc;
  144 +
  145 + if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR)
  146 + return 0;
  147 + generic = (struct acpi_hest_generic *)hest_hdr;
  148 + if (!generic->enabled)
  149 + return 0;
  150 + ghes_dev = platform_device_alloc("GHES", hest_hdr->source_id);
  151 + if (!ghes_dev)
  152 + return -ENOMEM;
  153 + ghes_dev->dev.platform_data = generic;
  154 + rc = platform_device_add(ghes_dev);
  155 + if (rc)
  156 + goto err;
  157 + ghes_arr->ghes_devs[ghes_arr->count++] = ghes_dev;
  158 +
  159 + return 0;
  160 +err:
  161 + platform_device_put(ghes_dev);
  162 + return rc;
  163 +}
  164 +
  165 +static int hest_ghes_dev_register(unsigned int ghes_count)
  166 +{
  167 + int rc, i;
  168 + struct ghes_arr ghes_arr;
  169 +
  170 + ghes_arr.count = 0;
  171 + ghes_arr.ghes_devs = kmalloc(sizeof(void *) * ghes_count, GFP_KERNEL);
  172 + if (!ghes_arr.ghes_devs)
  173 + return -ENOMEM;
  174 +
  175 + rc = apei_hest_parse(hest_parse_ghes, &ghes_arr);
  176 + if (rc)
  177 + goto err;
  178 +out:
  179 + kfree(ghes_arr.ghes_devs);
  180 + return rc;
  181 +err:
  182 + for (i = 0; i < ghes_arr.count; i++)
  183 + platform_device_unregister(ghes_arr.ghes_devs[i]);
  184 + goto out;
  185 +}
  186 +
128 187 static int __init setup_hest_disable(char *str)
129 188 {
130 189 hest_disable = 1;
... ... @@ -137,6 +196,7 @@
137 196 {
138 197 acpi_status status;
139 198 int rc = -ENODEV;
  199 + unsigned int ghes_count = 0;
140 200  
141 201 if (acpi_disabled)
142 202 goto err;
... ... @@ -158,7 +218,11 @@
158 218 goto err;
159 219 }
160 220  
161   - rc = apei_hest_parse(hest_void_parse, NULL);
  221 + rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
  222 + if (rc)
  223 + goto err;
  224 +
  225 + rc = hest_ghes_dev_register(ghes_count);
162 226 if (rc)
163 227 goto err;
164 228