root/drivers/xen/xen-acpi-cpuhotplug.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. xen_acpi_processor_enable
  2. xen_acpi_processor_add
  3. xen_acpi_processor_remove
  4. is_processor_present
  5. xen_apic_id
  6. xen_hotadd_cpu
  7. xen_acpi_cpu_hotadd
  8. acpi_processor_device_remove
  9. acpi_processor_hotplug_notify
  10. is_processor_device
  11. processor_walk_namespace_cb
  12. acpi_processor_install_hotplug_notify
  13. acpi_processor_uninstall_hotplug_notify
  14. xen_acpi_processor_init
  15. xen_acpi_processor_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright (C) 2012 Intel Corporation
   4  *    Author: Liu Jinsong <jinsong.liu@intel.com>
   5  *    Author: Jiang Yunhong <yunhong.jiang@intel.com>
   6  */
   7 
   8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   9 
  10 #include <linux/kernel.h>
  11 #include <linux/module.h>
  12 #include <linux/init.h>
  13 #include <linux/types.h>
  14 #include <linux/cpu.h>
  15 #include <linux/acpi.h>
  16 #include <linux/uaccess.h>
  17 #include <acpi/processor.h>
  18 #include <xen/acpi.h>
  19 #include <xen/interface/platform.h>
  20 #include <asm/xen/hypercall.h>
  21 
  22 #define PREFIX "ACPI:xen_cpu_hotplug:"
  23 
  24 #define INSTALL_NOTIFY_HANDLER          0
  25 #define UNINSTALL_NOTIFY_HANDLER        1
  26 
  27 static acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr);
  28 
  29 /* --------------------------------------------------------------------------
  30                                 Driver Interface
  31 -------------------------------------------------------------------------- */
  32 
  33 static int xen_acpi_processor_enable(struct acpi_device *device)
  34 {
  35         acpi_status status = 0;
  36         unsigned long long value;
  37         union acpi_object object = { 0 };
  38         struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
  39         struct acpi_processor *pr = acpi_driver_data(device);
  40 
  41         if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) {
  42                 /* Declared with "Processor" statement; match ProcessorID */
  43                 status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
  44                 if (ACPI_FAILURE(status)) {
  45                         pr_err(PREFIX "Evaluating processor object\n");
  46                         return -ENODEV;
  47                 }
  48 
  49                 pr->acpi_id = object.processor.proc_id;
  50         } else {
  51                 /* Declared with "Device" statement; match _UID */
  52                 status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID,
  53                                                 NULL, &value);
  54                 if (ACPI_FAILURE(status)) {
  55                         pr_err(PREFIX "Evaluating processor _UID\n");
  56                         return -ENODEV;
  57                 }
  58 
  59                 pr->acpi_id = value;
  60         }
  61 
  62         pr->id = xen_pcpu_id(pr->acpi_id);
  63 
  64         if (invalid_logical_cpuid(pr->id))
  65                 /* This cpu is not presented at hypervisor, try to hotadd it */
  66                 if (ACPI_FAILURE(xen_acpi_cpu_hotadd(pr))) {
  67                         pr_err(PREFIX "Hotadd CPU (acpi_id = %d) failed.\n",
  68                                         pr->acpi_id);
  69                         return -ENODEV;
  70                 }
  71 
  72         return 0;
  73 }
  74 
  75 static int xen_acpi_processor_add(struct acpi_device *device)
  76 {
  77         int ret;
  78         struct acpi_processor *pr;
  79 
  80         if (!device)
  81                 return -EINVAL;
  82 
  83         pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL);
  84         if (!pr)
  85                 return -ENOMEM;
  86 
  87         pr->handle = device->handle;
  88         strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME);
  89         strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS);
  90         device->driver_data = pr;
  91 
  92         ret = xen_acpi_processor_enable(device);
  93         if (ret)
  94                 pr_err(PREFIX "Error when enabling Xen processor\n");
  95 
  96         return ret;
  97 }
  98 
  99 static int xen_acpi_processor_remove(struct acpi_device *device)
 100 {
 101         struct acpi_processor *pr;
 102 
 103         if (!device)
 104                 return -EINVAL;
 105 
 106         pr = acpi_driver_data(device);
 107         if (!pr)
 108                 return -EINVAL;
 109 
 110         kfree(pr);
 111         return 0;
 112 }
 113 
 114 /*--------------------------------------------------------------
 115                 Acpi processor hotplug support
 116 --------------------------------------------------------------*/
 117 
 118 static int is_processor_present(acpi_handle handle)
 119 {
 120         acpi_status status;
 121         unsigned long long sta = 0;
 122 
 123 
 124         status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
 125 
 126         if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_PRESENT))
 127                 return 1;
 128 
 129         /*
 130          * _STA is mandatory for a processor that supports hot plug
 131          */
 132         if (status == AE_NOT_FOUND)
 133                 pr_info(PREFIX "Processor does not support hot plug\n");
 134         else
 135                 pr_info(PREFIX "Processor Device is not present");
 136         return 0;
 137 }
 138 
 139 static int xen_apic_id(acpi_handle handle)
 140 {
 141         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 142         union acpi_object *obj;
 143         struct acpi_madt_local_apic *lapic;
 144         int apic_id;
 145 
 146         if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
 147                 return -EINVAL;
 148 
 149         if (!buffer.length || !buffer.pointer)
 150                 return -EINVAL;
 151 
 152         obj = buffer.pointer;
 153         if (obj->type != ACPI_TYPE_BUFFER ||
 154             obj->buffer.length < sizeof(*lapic)) {
 155                 kfree(buffer.pointer);
 156                 return -EINVAL;
 157         }
 158 
 159         lapic = (struct acpi_madt_local_apic *)obj->buffer.pointer;
 160 
 161         if (lapic->header.type != ACPI_MADT_TYPE_LOCAL_APIC ||
 162             !(lapic->lapic_flags & ACPI_MADT_ENABLED)) {
 163                 kfree(buffer.pointer);
 164                 return -EINVAL;
 165         }
 166 
 167         apic_id = (uint32_t)lapic->id;
 168         kfree(buffer.pointer);
 169         buffer.length = ACPI_ALLOCATE_BUFFER;
 170         buffer.pointer = NULL;
 171 
 172         return apic_id;
 173 }
 174 
 175 static int xen_hotadd_cpu(struct acpi_processor *pr)
 176 {
 177         int cpu_id, apic_id, pxm;
 178         struct xen_platform_op op;
 179 
 180         apic_id = xen_apic_id(pr->handle);
 181         if (apic_id < 0) {
 182                 pr_err(PREFIX "Failed to get apic_id for acpi_id %d\n",
 183                                 pr->acpi_id);
 184                 return -ENODEV;
 185         }
 186 
 187         pxm = xen_acpi_get_pxm(pr->handle);
 188         if (pxm < 0) {
 189                 pr_err(PREFIX "Failed to get _PXM for acpi_id %d\n",
 190                                 pr->acpi_id);
 191                 return pxm;
 192         }
 193 
 194         op.cmd = XENPF_cpu_hotadd;
 195         op.u.cpu_add.apic_id = apic_id;
 196         op.u.cpu_add.acpi_id = pr->acpi_id;
 197         op.u.cpu_add.pxm = pxm;
 198 
 199         cpu_id = HYPERVISOR_platform_op(&op);
 200         if (cpu_id < 0)
 201                 pr_err(PREFIX "Failed to hotadd CPU for acpi_id %d\n",
 202                                 pr->acpi_id);
 203 
 204         return cpu_id;
 205 }
 206 
 207 static acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr)
 208 {
 209         if (!is_processor_present(pr->handle))
 210                 return AE_ERROR;
 211 
 212         pr->id = xen_hotadd_cpu(pr);
 213         if (invalid_logical_cpuid(pr->id))
 214                 return AE_ERROR;
 215 
 216         /*
 217          * Sync with Xen hypervisor, providing new /sys/.../xen_cpuX
 218          * interface after cpu hotadded.
 219          */
 220         xen_pcpu_hotplug_sync();
 221 
 222         return AE_OK;
 223 }
 224 
 225 static int acpi_processor_device_remove(struct acpi_device *device)
 226 {
 227         pr_debug(PREFIX "Xen does not support CPU hotremove\n");
 228 
 229         return -ENOSYS;
 230 }
 231 
 232 static void acpi_processor_hotplug_notify(acpi_handle handle,
 233                                           u32 event, void *data)
 234 {
 235         struct acpi_processor *pr;
 236         struct acpi_device *device = NULL;
 237         u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
 238         int result;
 239 
 240         acpi_scan_lock_acquire();
 241 
 242         switch (event) {
 243         case ACPI_NOTIFY_BUS_CHECK:
 244         case ACPI_NOTIFY_DEVICE_CHECK:
 245                 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 246                         "Processor driver received %s event\n",
 247                         (event == ACPI_NOTIFY_BUS_CHECK) ?
 248                         "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK"));
 249 
 250                 if (!is_processor_present(handle))
 251                         break;
 252 
 253                 acpi_bus_get_device(handle, &device);
 254                 if (acpi_device_enumerated(device))
 255                         break;
 256 
 257                 result = acpi_bus_scan(handle);
 258                 if (result) {
 259                         pr_err(PREFIX "Unable to add the device\n");
 260                         break;
 261                 }
 262                 device = NULL;
 263                 acpi_bus_get_device(handle, &device);
 264                 if (!acpi_device_enumerated(device)) {
 265                         pr_err(PREFIX "Missing device object\n");
 266                         break;
 267                 }
 268                 ost_code = ACPI_OST_SC_SUCCESS;
 269                 break;
 270 
 271         case ACPI_NOTIFY_EJECT_REQUEST:
 272                 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 273                                   "received ACPI_NOTIFY_EJECT_REQUEST\n"));
 274 
 275                 if (acpi_bus_get_device(handle, &device)) {
 276                         pr_err(PREFIX "Device don't exist, dropping EJECT\n");
 277                         break;
 278                 }
 279                 pr = acpi_driver_data(device);
 280                 if (!pr) {
 281                         pr_err(PREFIX "Driver data is NULL, dropping EJECT\n");
 282                         break;
 283                 }
 284 
 285                 /*
 286                  * TBD: implement acpi_processor_device_remove if Xen support
 287                  * CPU hotremove in the future.
 288                  */
 289                 acpi_processor_device_remove(device);
 290                 break;
 291 
 292         default:
 293                 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 294                                   "Unsupported event [0x%x]\n", event));
 295 
 296                 /* non-hotplug event; possibly handled by other handler */
 297                 goto out;
 298         }
 299 
 300         (void) acpi_evaluate_ost(handle, event, ost_code, NULL);
 301 
 302 out:
 303         acpi_scan_lock_release();
 304 }
 305 
 306 static acpi_status is_processor_device(acpi_handle handle)
 307 {
 308         struct acpi_device_info *info;
 309         char *hid;
 310         acpi_status status;
 311 
 312         status = acpi_get_object_info(handle, &info);
 313         if (ACPI_FAILURE(status))
 314                 return status;
 315 
 316         if (info->type == ACPI_TYPE_PROCESSOR) {
 317                 kfree(info);
 318                 return AE_OK;   /* found a processor object */
 319         }
 320 
 321         if (!(info->valid & ACPI_VALID_HID)) {
 322                 kfree(info);
 323                 return AE_ERROR;
 324         }
 325 
 326         hid = info->hardware_id.string;
 327         if ((hid == NULL) || strcmp(hid, ACPI_PROCESSOR_DEVICE_HID)) {
 328                 kfree(info);
 329                 return AE_ERROR;
 330         }
 331 
 332         kfree(info);
 333         return AE_OK;   /* found a processor device object */
 334 }
 335 
 336 static acpi_status
 337 processor_walk_namespace_cb(acpi_handle handle,
 338                             u32 lvl, void *context, void **rv)
 339 {
 340         acpi_status status;
 341         int *action = context;
 342 
 343         status = is_processor_device(handle);
 344         if (ACPI_FAILURE(status))
 345                 return AE_OK;   /* not a processor; continue to walk */
 346 
 347         switch (*action) {
 348         case INSTALL_NOTIFY_HANDLER:
 349                 acpi_install_notify_handler(handle,
 350                                             ACPI_SYSTEM_NOTIFY,
 351                                             acpi_processor_hotplug_notify,
 352                                             NULL);
 353                 break;
 354         case UNINSTALL_NOTIFY_HANDLER:
 355                 acpi_remove_notify_handler(handle,
 356                                            ACPI_SYSTEM_NOTIFY,
 357                                            acpi_processor_hotplug_notify);
 358                 break;
 359         default:
 360                 break;
 361         }
 362 
 363         /* found a processor; skip walking underneath */
 364         return AE_CTRL_DEPTH;
 365 }
 366 
 367 static
 368 void acpi_processor_install_hotplug_notify(void)
 369 {
 370         int action = INSTALL_NOTIFY_HANDLER;
 371         acpi_walk_namespace(ACPI_TYPE_ANY,
 372                             ACPI_ROOT_OBJECT,
 373                             ACPI_UINT32_MAX,
 374                             processor_walk_namespace_cb, NULL, &action, NULL);
 375 }
 376 
 377 static
 378 void acpi_processor_uninstall_hotplug_notify(void)
 379 {
 380         int action = UNINSTALL_NOTIFY_HANDLER;
 381         acpi_walk_namespace(ACPI_TYPE_ANY,
 382                             ACPI_ROOT_OBJECT,
 383                             ACPI_UINT32_MAX,
 384                             processor_walk_namespace_cb, NULL, &action, NULL);
 385 }
 386 
 387 static const struct acpi_device_id processor_device_ids[] = {
 388         {ACPI_PROCESSOR_OBJECT_HID, 0},
 389         {ACPI_PROCESSOR_DEVICE_HID, 0},
 390         {"", 0},
 391 };
 392 MODULE_DEVICE_TABLE(acpi, processor_device_ids);
 393 
 394 static struct acpi_driver xen_acpi_processor_driver = {
 395         .name = "processor",
 396         .class = ACPI_PROCESSOR_CLASS,
 397         .ids = processor_device_ids,
 398         .ops = {
 399                 .add = xen_acpi_processor_add,
 400                 .remove = xen_acpi_processor_remove,
 401                 },
 402 };
 403 
 404 static int __init xen_acpi_processor_init(void)
 405 {
 406         int result = 0;
 407 
 408         if (!xen_initial_domain())
 409                 return -ENODEV;
 410 
 411         /* unregister the stub which only used to reserve driver space */
 412         xen_stub_processor_exit();
 413 
 414         result = acpi_bus_register_driver(&xen_acpi_processor_driver);
 415         if (result < 0) {
 416                 xen_stub_processor_init();
 417                 return result;
 418         }
 419 
 420         acpi_processor_install_hotplug_notify();
 421         return 0;
 422 }
 423 
 424 static void __exit xen_acpi_processor_exit(void)
 425 {
 426         if (!xen_initial_domain())
 427                 return;
 428 
 429         acpi_processor_uninstall_hotplug_notify();
 430 
 431         acpi_bus_unregister_driver(&xen_acpi_processor_driver);
 432 
 433         /*
 434          * stub reserve space again to prevent any chance of native
 435          * driver loading.
 436          */
 437         xen_stub_processor_init();
 438         return;
 439 }
 440 
 441 module_init(xen_acpi_processor_init);
 442 module_exit(xen_acpi_processor_exit);
 443 ACPI_MODULE_NAME("xen-acpi-cpuhotplug");
 444 MODULE_AUTHOR("Liu Jinsong <jinsong.liu@intel.com>");
 445 MODULE_DESCRIPTION("Xen Hotplug CPU Driver");
 446 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */