372 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2004, 2013 Intel Corporation4 * Author: Naveen B S <naveen.b.s@intel.com>5 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>6 *7 * All rights reserved.8 *9 * ACPI based HotPlug driver that supports Memory Hotplug10 * This driver fields notifications from firmware for memory add11 * and remove operations and alerts the VM of the affected memory12 * ranges.13 */14 15#include <linux/acpi.h>16#include <linux/memory.h>17#include <linux/memory_hotplug.h>18 19#include "internal.h"20 21#define ACPI_MEMORY_DEVICE_CLASS "memory"22#define ACPI_MEMORY_DEVICE_HID "PNP0C80"23#define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device"24 25static const struct acpi_device_id memory_device_ids[] = {26 {ACPI_MEMORY_DEVICE_HID, 0},27 {"", 0},28};29 30#ifdef CONFIG_ACPI_HOTPLUG_MEMORY31 32static int acpi_memory_device_add(struct acpi_device *device,33 const struct acpi_device_id *not_used);34static void acpi_memory_device_remove(struct acpi_device *device);35 36static struct acpi_scan_handler memory_device_handler = {37 .ids = memory_device_ids,38 .attach = acpi_memory_device_add,39 .detach = acpi_memory_device_remove,40 .hotplug = {41 .enabled = true,42 },43};44 45struct acpi_memory_info {46 struct list_head list;47 u64 start_addr; /* Memory Range start physical addr */48 u64 length; /* Memory Range length */49 unsigned short caching; /* memory cache attribute */50 unsigned short write_protect; /* memory read/write attribute */51 unsigned int enabled:1;52};53 54struct acpi_memory_device {55 struct acpi_device *device;56 struct list_head res_list;57 int mgid;58};59 60static acpi_status61acpi_memory_get_resource(struct acpi_resource *resource, void *context)62{63 struct acpi_memory_device *mem_device = context;64 struct acpi_resource_address64 address64;65 struct acpi_memory_info *info, *new;66 acpi_status status;67 68 status = acpi_resource_to_address64(resource, &address64);69 if (ACPI_FAILURE(status) ||70 (address64.resource_type != ACPI_MEMORY_RANGE))71 return AE_OK;72 73 list_for_each_entry(info, &mem_device->res_list, list) {74 /* Can we combine the resource range information? */75 if ((info->caching == address64.info.mem.caching) &&76 (info->write_protect == address64.info.mem.write_protect) &&77 (info->start_addr + info->length == address64.address.minimum)) {78 info->length += address64.address.address_length;79 return AE_OK;80 }81 }82 83 new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);84 if (!new)85 return AE_ERROR;86 87 INIT_LIST_HEAD(&new->list);88 new->caching = address64.info.mem.caching;89 new->write_protect = address64.info.mem.write_protect;90 new->start_addr = address64.address.minimum;91 new->length = address64.address.address_length;92 list_add_tail(&new->list, &mem_device->res_list);93 94 return AE_OK;95}96 97static void98acpi_memory_free_device_resources(struct acpi_memory_device *mem_device)99{100 struct acpi_memory_info *info, *n;101 102 list_for_each_entry_safe(info, n, &mem_device->res_list, list)103 kfree(info);104 INIT_LIST_HEAD(&mem_device->res_list);105}106 107static int108acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)109{110 acpi_status status;111 112 if (!list_empty(&mem_device->res_list))113 return 0;114 115 status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,116 acpi_memory_get_resource, mem_device);117 if (ACPI_FAILURE(status)) {118 acpi_memory_free_device_resources(mem_device);119 return -EINVAL;120 }121 122 return 0;123}124 125static int acpi_memory_check_device(struct acpi_memory_device *mem_device)126{127 unsigned long long current_status;128 129 /* Get device present/absent information from the _STA */130 if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle,131 METHOD_NAME__STA, NULL,132 ¤t_status)))133 return -ENODEV;134 /*135 * Check for device status. Device should be136 * present/enabled/functioning.137 */138 if (!((current_status & ACPI_STA_DEVICE_PRESENT)139 && (current_status & ACPI_STA_DEVICE_ENABLED)140 && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))141 return -ENODEV;142 143 return 0;144}145 146static int acpi_bind_memblk(struct memory_block *mem, void *arg)147{148 return acpi_bind_one(&mem->dev, arg);149}150 151static int acpi_bind_memory_blocks(struct acpi_memory_info *info,152 struct acpi_device *adev)153{154 return walk_memory_blocks(info->start_addr, info->length, adev,155 acpi_bind_memblk);156}157 158static int acpi_unbind_memblk(struct memory_block *mem, void *arg)159{160 acpi_unbind_one(&mem->dev);161 return 0;162}163 164static void acpi_unbind_memory_blocks(struct acpi_memory_info *info)165{166 walk_memory_blocks(info->start_addr, info->length, NULL,167 acpi_unbind_memblk);168}169 170static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)171{172 acpi_handle handle = mem_device->device->handle;173 mhp_t mhp_flags = MHP_NID_IS_MGID;174 int result, num_enabled = 0;175 struct acpi_memory_info *info;176 u64 total_length = 0;177 int node, mgid;178 179 node = acpi_get_node(handle);180 181 list_for_each_entry(info, &mem_device->res_list, list) {182 if (!info->length)183 continue;184 /* We want a single node for the whole memory group */185 if (node < 0)186 node = memory_add_physaddr_to_nid(info->start_addr);187 total_length += info->length;188 }189 190 if (!total_length) {191 dev_err(&mem_device->device->dev, "device is empty\n");192 return -EINVAL;193 }194 195 mgid = memory_group_register_static(node, PFN_UP(total_length));196 if (mgid < 0)197 return mgid;198 mem_device->mgid = mgid;199 200 /*201 * Tell the VM there is more memory here...202 * Note: Assume that this function returns zero on success203 * We don't have memory-hot-add rollback function,now.204 * (i.e. memory-hot-remove function)205 */206 list_for_each_entry(info, &mem_device->res_list, list) {207 /*208 * If the memory block size is zero, please ignore it.209 * Don't try to do the following memory hotplug flowchart.210 */211 if (!info->length)212 continue;213 214 mhp_flags |= MHP_MEMMAP_ON_MEMORY;215 result = __add_memory(mgid, info->start_addr, info->length,216 mhp_flags);217 218 /*219 * If the memory block has been used by the kernel, add_memory()220 * returns -EEXIST. If add_memory() returns the other error, it221 * means that this memory block is not used by the kernel.222 */223 if (result && result != -EEXIST)224 continue;225 226 result = acpi_bind_memory_blocks(info, mem_device->device);227 if (result) {228 acpi_unbind_memory_blocks(info);229 return -ENODEV;230 }231 232 info->enabled = 1;233 234 /*235 * Add num_enable even if add_memory() returns -EEXIST, so the236 * device is bound to this driver.237 */238 num_enabled++;239 }240 if (!num_enabled) {241 dev_err(&mem_device->device->dev, "add_memory failed\n");242 return -EINVAL;243 }244 /*245 * Sometimes the memory device will contain several memory blocks.246 * When one memory block is hot-added to the system memory, it will247 * be regarded as a success.248 * Otherwise if the last memory block can't be hot-added to the system249 * memory, it will be failure and the memory device can't be bound with250 * driver.251 */252 return 0;253}254 255static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device)256{257 struct acpi_memory_info *info, *n;258 259 list_for_each_entry_safe(info, n, &mem_device->res_list, list) {260 if (!info->enabled)261 continue;262 263 acpi_unbind_memory_blocks(info);264 __remove_memory(info->start_addr, info->length);265 list_del(&info->list);266 kfree(info);267 }268}269 270static void acpi_memory_device_free(struct acpi_memory_device *mem_device)271{272 if (!mem_device)273 return;274 275 /* In case we succeeded adding *some* memory, unregistering fails. */276 if (mem_device->mgid >= 0)277 memory_group_unregister(mem_device->mgid);278 279 acpi_memory_free_device_resources(mem_device);280 mem_device->device->driver_data = NULL;281 kfree(mem_device);282}283 284static int acpi_memory_device_add(struct acpi_device *device,285 const struct acpi_device_id *not_used)286{287 struct acpi_memory_device *mem_device;288 int result;289 290 if (!device)291 return -EINVAL;292 293 mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);294 if (!mem_device)295 return -ENOMEM;296 297 INIT_LIST_HEAD(&mem_device->res_list);298 mem_device->device = device;299 mem_device->mgid = -1;300 sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);301 sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);302 device->driver_data = mem_device;303 304 /* Get the range from the _CRS */305 result = acpi_memory_get_device_resources(mem_device);306 if (result) {307 device->driver_data = NULL;308 kfree(mem_device);309 return result;310 }311 312 result = acpi_memory_check_device(mem_device);313 if (result) {314 acpi_memory_device_free(mem_device);315 return 0;316 }317 318 result = acpi_memory_enable_device(mem_device);319 if (result) {320 dev_err(&device->dev, "acpi_memory_enable_device() error\n");321 acpi_memory_device_free(mem_device);322 return result;323 }324 325 dev_dbg(&device->dev, "Memory device configured by ACPI\n");326 return 1;327}328 329static void acpi_memory_device_remove(struct acpi_device *device)330{331 struct acpi_memory_device *mem_device;332 333 if (!device || !acpi_driver_data(device))334 return;335 336 mem_device = acpi_driver_data(device);337 acpi_memory_remove_memory(mem_device);338 acpi_memory_device_free(mem_device);339}340 341static bool __initdata acpi_no_memhotplug;342 343void __init acpi_memory_hotplug_init(void)344{345 if (acpi_no_memhotplug) {346 memory_device_handler.attach = NULL;347 acpi_scan_add_handler(&memory_device_handler);348 return;349 }350 acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory");351}352 353static int __init disable_acpi_memory_hotplug(char *str)354{355 acpi_no_memhotplug = true;356 return 1;357}358__setup("acpi_no_memhotplug", disable_acpi_memory_hotplug);359 360#else361 362static struct acpi_scan_handler memory_device_handler = {363 .ids = memory_device_ids,364};365 366void __init acpi_memory_hotplug_init(void)367{368 acpi_scan_add_handler(&memory_device_handler);369}370 371#endif /* CONFIG_ACPI_HOTPLUG_MEMORY */372