linux/drivers/acpi/acpi_memhotplug.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com>
   3 *
   4 * All rights reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or (at
   9 * your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful, but
  12 * WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  14 * NON INFRINGEMENT.  See the GNU General Public License for more
  15 * details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20 *
  21 *
  22 * ACPI based HotPlug driver that supports Memory Hotplug
  23 * This driver fields notifications from firmware for memory add
  24 * and remove operations and alerts the VM of the affected memory
  25 * ranges.
  26 */
  27
  28#include <linux/kernel.h>
  29#include <linux/module.h>
  30#include <linux/init.h>
  31#include <linux/types.h>
  32#include <linux/memory_hotplug.h>
  33#include <linux/slab.h>
  34#include <linux/acpi.h>
  35#include <acpi/acpi_drivers.h>
  36
  37#define ACPI_MEMORY_DEVICE_CLASS                "memory"
  38#define ACPI_MEMORY_DEVICE_HID                  "PNP0C80"
  39#define ACPI_MEMORY_DEVICE_NAME                 "Hotplug Mem Device"
  40
  41#define _COMPONENT              ACPI_MEMORY_DEVICE_COMPONENT
  42
  43#undef PREFIX
  44#define         PREFIX          "ACPI:memory_hp:"
  45
  46ACPI_MODULE_NAME("acpi_memhotplug");
  47MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
  48MODULE_DESCRIPTION("Hotplug Mem Driver");
  49MODULE_LICENSE("GPL");
  50
  51/* Memory Device States */
  52#define MEMORY_INVALID_STATE    0
  53#define MEMORY_POWER_ON_STATE   1
  54#define MEMORY_POWER_OFF_STATE  2
  55
  56static int acpi_memory_device_add(struct acpi_device *device);
  57static int acpi_memory_device_remove(struct acpi_device *device);
  58
  59static const struct acpi_device_id memory_device_ids[] = {
  60        {ACPI_MEMORY_DEVICE_HID, 0},
  61        {"", 0},
  62};
  63MODULE_DEVICE_TABLE(acpi, memory_device_ids);
  64
  65static struct acpi_driver acpi_memory_device_driver = {
  66        .name = "acpi_memhotplug",
  67        .class = ACPI_MEMORY_DEVICE_CLASS,
  68        .ids = memory_device_ids,
  69        .ops = {
  70                .add = acpi_memory_device_add,
  71                .remove = acpi_memory_device_remove,
  72                },
  73};
  74
  75struct acpi_memory_info {
  76        struct list_head list;
  77        u64 start_addr;         /* Memory Range start physical addr */
  78        u64 length;             /* Memory Range length */
  79        unsigned short caching; /* memory cache attribute */
  80        unsigned short write_protect;   /* memory read/write attribute */
  81        unsigned int enabled:1;
  82        unsigned int failed:1;
  83};
  84
  85struct acpi_memory_device {
  86        struct acpi_device * device;
  87        unsigned int state;     /* State of the memory device */
  88        struct list_head res_list;
  89};
  90
  91static acpi_status
  92acpi_memory_get_resource(struct acpi_resource *resource, void *context)
  93{
  94        struct acpi_memory_device *mem_device = context;
  95        struct acpi_resource_address64 address64;
  96        struct acpi_memory_info *info, *new;
  97        acpi_status status;
  98
  99        status = acpi_resource_to_address64(resource, &address64);
 100        if (ACPI_FAILURE(status) ||
 101            (address64.resource_type != ACPI_MEMORY_RANGE))
 102                return AE_OK;
 103
 104        list_for_each_entry(info, &mem_device->res_list, list) {
 105                /* Can we combine the resource range information? */
 106                if ((info->caching == address64.info.mem.caching) &&
 107                    (info->write_protect == address64.info.mem.write_protect) &&
 108                    (info->start_addr + info->length == address64.minimum)) {
 109                        info->length += address64.address_length;
 110                        return AE_OK;
 111                }
 112        }
 113
 114        new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
 115        if (!new)
 116                return AE_ERROR;
 117
 118        INIT_LIST_HEAD(&new->list);
 119        new->caching = address64.info.mem.caching;
 120        new->write_protect = address64.info.mem.write_protect;
 121        new->start_addr = address64.minimum;
 122        new->length = address64.address_length;
 123        list_add_tail(&new->list, &mem_device->res_list);
 124
 125        return AE_OK;
 126}
 127
 128static void
 129acpi_memory_free_device_resources(struct acpi_memory_device *mem_device)
 130{
 131        struct acpi_memory_info *info, *n;
 132
 133        list_for_each_entry_safe(info, n, &mem_device->res_list, list)
 134                kfree(info);
 135        INIT_LIST_HEAD(&mem_device->res_list);
 136}
 137
 138static int
 139acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
 140{
 141        acpi_status status;
 142
 143        if (!list_empty(&mem_device->res_list))
 144                return 0;
 145
 146        status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
 147                                     acpi_memory_get_resource, mem_device);
 148        if (ACPI_FAILURE(status)) {
 149                acpi_memory_free_device_resources(mem_device);
 150                return -EINVAL;
 151        }
 152
 153        return 0;
 154}
 155
 156static int acpi_memory_get_device(acpi_handle handle,
 157                                  struct acpi_memory_device **mem_device)
 158{
 159        struct acpi_device *device = NULL;
 160        int result = 0;
 161
 162        acpi_scan_lock_acquire();
 163
 164        acpi_bus_get_device(handle, &device);
 165        if (device)
 166                goto end;
 167
 168        /*
 169         * Now add the notified device.  This creates the acpi_device
 170         * and invokes .add function
 171         */
 172        result = acpi_bus_scan(handle);
 173        if (result) {
 174                acpi_handle_warn(handle, "ACPI namespace scan failed\n");
 175                result = -EINVAL;
 176                goto out;
 177        }
 178        result = acpi_bus_get_device(handle, &device);
 179        if (result) {
 180                acpi_handle_warn(handle, "Missing device object\n");
 181                result = -EINVAL;
 182                goto out;
 183        }
 184
 185 end:
 186        *mem_device = acpi_driver_data(device);
 187        if (!(*mem_device)) {
 188                dev_err(&device->dev, "driver data not found\n");
 189                result = -ENODEV;
 190                goto out;
 191        }
 192
 193 out:
 194        acpi_scan_lock_release();
 195        return result;
 196}
 197
 198static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
 199{
 200        unsigned long long current_status;
 201
 202        /* Get device present/absent information from the _STA */
 203        if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle, "_STA",
 204                                               NULL, &current_status)))
 205                return -ENODEV;
 206        /*
 207         * Check for device status. Device should be
 208         * present/enabled/functioning.
 209         */
 210        if (!((current_status & ACPI_STA_DEVICE_PRESENT)
 211              && (current_status & ACPI_STA_DEVICE_ENABLED)
 212              && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
 213                return -ENODEV;
 214
 215        return 0;
 216}
 217
 218static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
 219{
 220        int result, num_enabled = 0;
 221        struct acpi_memory_info *info;
 222        int node;
 223
 224        node = acpi_get_node(mem_device->device->handle);
 225        /*
 226         * Tell the VM there is more memory here...
 227         * Note: Assume that this function returns zero on success
 228         * We don't have memory-hot-add rollback function,now.
 229         * (i.e. memory-hot-remove function)
 230         */
 231        list_for_each_entry(info, &mem_device->res_list, list) {
 232                if (info->enabled) { /* just sanity check...*/
 233                        num_enabled++;
 234                        continue;
 235                }
 236                /*
 237                 * If the memory block size is zero, please ignore it.
 238                 * Don't try to do the following memory hotplug flowchart.
 239                 */
 240                if (!info->length)
 241                        continue;
 242                if (node < 0)
 243                        node = memory_add_physaddr_to_nid(info->start_addr);
 244
 245                result = add_memory(node, info->start_addr, info->length);
 246
 247                /*
 248                 * If the memory block has been used by the kernel, add_memory()
 249                 * returns -EEXIST. If add_memory() returns the other error, it
 250                 * means that this memory block is not used by the kernel.
 251                 */
 252                if (result && result != -EEXIST) {
 253                        info->failed = 1;
 254                        continue;
 255                }
 256
 257                if (!result)
 258                        info->enabled = 1;
 259                /*
 260                 * Add num_enable even if add_memory() returns -EEXIST, so the
 261                 * device is bound to this driver.
 262                 */
 263                num_enabled++;
 264        }
 265        if (!num_enabled) {
 266                dev_err(&mem_device->device->dev, "add_memory failed\n");
 267                mem_device->state = MEMORY_INVALID_STATE;
 268                return -EINVAL;
 269        }
 270        /*
 271         * Sometimes the memory device will contain several memory blocks.
 272         * When one memory block is hot-added to the system memory, it will
 273         * be regarded as a success.
 274         * Otherwise if the last memory block can't be hot-added to the system
 275         * memory, it will be failure and the memory device can't be bound with
 276         * driver.
 277         */
 278        return 0;
 279}
 280
 281static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
 282{
 283        int result = 0, nid;
 284        struct acpi_memory_info *info, *n;
 285
 286        nid = acpi_get_node(mem_device->device->handle);
 287
 288        list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
 289                if (info->failed)
 290                        /* The kernel does not use this memory block */
 291                        continue;
 292
 293                if (!info->enabled)
 294                        /*
 295                         * The kernel uses this memory block, but it may be not
 296                         * managed by us.
 297                         */
 298                        return -EBUSY;
 299
 300                if (nid < 0)
 301                        nid = memory_add_physaddr_to_nid(info->start_addr);
 302                result = remove_memory(nid, info->start_addr, info->length);
 303                if (result)
 304                        return result;
 305
 306                list_del(&info->list);
 307                kfree(info);
 308        }
 309
 310        return result;
 311}
 312
 313static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
 314{
 315        struct acpi_memory_device *mem_device;
 316        struct acpi_device *device;
 317        struct acpi_eject_event *ej_event = NULL;
 318        u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
 319        acpi_status status;
 320
 321        switch (event) {
 322        case ACPI_NOTIFY_BUS_CHECK:
 323                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 324                                  "\nReceived BUS CHECK notification for device\n"));
 325                /* Fall Through */
 326        case ACPI_NOTIFY_DEVICE_CHECK:
 327                if (event == ACPI_NOTIFY_DEVICE_CHECK)
 328                        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 329                                          "\nReceived DEVICE CHECK notification for device\n"));
 330                if (acpi_memory_get_device(handle, &mem_device)) {
 331                        acpi_handle_err(handle, "Cannot find driver data\n");
 332                        break;
 333                }
 334
 335                ost_code = ACPI_OST_SC_SUCCESS;
 336                break;
 337
 338        case ACPI_NOTIFY_EJECT_REQUEST:
 339                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 340                                  "\nReceived EJECT REQUEST notification for device\n"));
 341
 342                status = AE_ERROR;
 343                acpi_scan_lock_acquire();
 344
 345                if (acpi_bus_get_device(handle, &device)) {
 346                        acpi_handle_err(handle, "Device doesn't exist\n");
 347                        goto unlock;
 348                }
 349                mem_device = acpi_driver_data(device);
 350                if (!mem_device) {
 351                        acpi_handle_err(handle, "Driver Data is NULL\n");
 352                        goto unlock;
 353                }
 354
 355                ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
 356                if (!ej_event) {
 357                        pr_err(PREFIX "No memory, dropping EJECT\n");
 358                        goto unlock;
 359                }
 360
 361                get_device(&device->dev);
 362                ej_event->device = device;
 363                ej_event->event = ACPI_NOTIFY_EJECT_REQUEST;
 364                /* The eject is carried out asynchronously. */
 365                status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device,
 366                                                 ej_event);
 367                if (ACPI_FAILURE(status)) {
 368                        put_device(&device->dev);
 369                        kfree(ej_event);
 370                }
 371
 372 unlock:
 373                acpi_scan_lock_release();
 374                if (ACPI_SUCCESS(status))
 375                        return;
 376        default:
 377                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 378                                  "Unsupported event [0x%x]\n", event));
 379
 380                /* non-hotplug event; possibly handled by other handler */
 381                return;
 382        }
 383
 384        /* Inform firmware that the hotplug operation has completed */
 385        (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL);
 386}
 387
 388static void acpi_memory_device_free(struct acpi_memory_device *mem_device)
 389{
 390        if (!mem_device)
 391                return;
 392
 393        acpi_memory_free_device_resources(mem_device);
 394        kfree(mem_device);
 395}
 396
 397static int acpi_memory_device_add(struct acpi_device *device)
 398{
 399        int result;
 400        struct acpi_memory_device *mem_device = NULL;
 401
 402
 403        if (!device)
 404                return -EINVAL;
 405
 406        mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
 407        if (!mem_device)
 408                return -ENOMEM;
 409
 410        INIT_LIST_HEAD(&mem_device->res_list);
 411        mem_device->device = device;
 412        sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
 413        sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
 414        device->driver_data = mem_device;
 415
 416        /* Get the range from the _CRS */
 417        result = acpi_memory_get_device_resources(mem_device);
 418        if (result) {
 419                kfree(mem_device);
 420                return result;
 421        }
 422
 423        /* Set the device state */
 424        mem_device->state = MEMORY_POWER_ON_STATE;
 425
 426        pr_debug("%s\n", acpi_device_name(device));
 427
 428        if (!acpi_memory_check_device(mem_device)) {
 429                /* call add_memory func */
 430                result = acpi_memory_enable_device(mem_device);
 431                if (result) {
 432                        dev_err(&device->dev,
 433                                "Error in acpi_memory_enable_device\n");
 434                        acpi_memory_device_free(mem_device);
 435                }
 436        }
 437        return result;
 438}
 439
 440static int acpi_memory_device_remove(struct acpi_device *device)
 441{
 442        struct acpi_memory_device *mem_device = NULL;
 443        int result;
 444
 445        if (!device || !acpi_driver_data(device))
 446                return -EINVAL;
 447
 448        mem_device = acpi_driver_data(device);
 449
 450        result = acpi_memory_remove_memory(mem_device);
 451        if (result)
 452                return result;
 453
 454        acpi_memory_device_free(mem_device);
 455
 456        return 0;
 457}
 458
 459/*
 460 * Helper function to check for memory device
 461 */
 462static acpi_status is_memory_device(acpi_handle handle)
 463{
 464        char *hardware_id;
 465        acpi_status status;
 466        struct acpi_device_info *info;
 467
 468        status = acpi_get_object_info(handle, &info);
 469        if (ACPI_FAILURE(status))
 470                return status;
 471
 472        if (!(info->valid & ACPI_VALID_HID)) {
 473                kfree(info);
 474                return AE_ERROR;
 475        }
 476
 477        hardware_id = info->hardware_id.string;
 478        if ((hardware_id == NULL) ||
 479            (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
 480                status = AE_ERROR;
 481
 482        kfree(info);
 483        return status;
 484}
 485
 486static acpi_status
 487acpi_memory_register_notify_handler(acpi_handle handle,
 488                                    u32 level, void *ctxt, void **retv)
 489{
 490        acpi_status status;
 491
 492
 493        status = is_memory_device(handle);
 494        if (ACPI_FAILURE(status))
 495                return AE_OK;   /* continue */
 496
 497        status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
 498                                             acpi_memory_device_notify, NULL);
 499        /* continue */
 500        return AE_OK;
 501}
 502
 503static acpi_status
 504acpi_memory_deregister_notify_handler(acpi_handle handle,
 505                                      u32 level, void *ctxt, void **retv)
 506{
 507        acpi_status status;
 508
 509
 510        status = is_memory_device(handle);
 511        if (ACPI_FAILURE(status))
 512                return AE_OK;   /* continue */
 513
 514        status = acpi_remove_notify_handler(handle,
 515                                            ACPI_SYSTEM_NOTIFY,
 516                                            acpi_memory_device_notify);
 517
 518        return AE_OK;   /* continue */
 519}
 520
 521static int __init acpi_memory_device_init(void)
 522{
 523        int result;
 524        acpi_status status;
 525
 526
 527        result = acpi_bus_register_driver(&acpi_memory_device_driver);
 528
 529        if (result < 0)
 530                return -ENODEV;
 531
 532        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
 533                                     ACPI_UINT32_MAX,
 534                                     acpi_memory_register_notify_handler, NULL,
 535                                     NULL, NULL);
 536
 537        if (ACPI_FAILURE(status)) {
 538                ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
 539                acpi_bus_unregister_driver(&acpi_memory_device_driver);
 540                return -ENODEV;
 541        }
 542
 543        return 0;
 544}
 545
 546static void __exit acpi_memory_device_exit(void)
 547{
 548        acpi_status status;
 549
 550
 551        /*
 552         * Adding this to un-install notification handlers for all the device
 553         * handles.
 554         */
 555        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
 556                                     ACPI_UINT32_MAX,
 557                                     acpi_memory_deregister_notify_handler, NULL,
 558                                     NULL, NULL);
 559
 560        if (ACPI_FAILURE(status))
 561                ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
 562
 563        acpi_bus_unregister_driver(&acpi_memory_device_driver);
 564
 565        return;
 566}
 567
 568module_init(acpi_memory_device_init);
 569module_exit(acpi_memory_device_exit);
 570
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.