Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/acpi/acpi_memhotplug.c
15109 views
1
/*
2
* Copyright (C) 2004 Intel Corporation <[email protected]>
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 <acpi/acpi_drivers.h>
35
36
#define ACPI_MEMORY_DEVICE_CLASS "memory"
37
#define ACPI_MEMORY_DEVICE_HID "PNP0C80"
38
#define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device"
39
40
#define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT
41
42
#undef PREFIX
43
#define PREFIX "ACPI:memory_hp:"
44
45
ACPI_MODULE_NAME("acpi_memhotplug");
46
MODULE_AUTHOR("Naveen B S <[email protected]>");
47
MODULE_DESCRIPTION("Hotplug Mem Driver");
48
MODULE_LICENSE("GPL");
49
50
/* Memory Device States */
51
#define MEMORY_INVALID_STATE 0
52
#define MEMORY_POWER_ON_STATE 1
53
#define MEMORY_POWER_OFF_STATE 2
54
55
static int acpi_memory_device_add(struct acpi_device *device);
56
static int acpi_memory_device_remove(struct acpi_device *device, int type);
57
58
static const struct acpi_device_id memory_device_ids[] = {
59
{ACPI_MEMORY_DEVICE_HID, 0},
60
{"", 0},
61
};
62
MODULE_DEVICE_TABLE(acpi, memory_device_ids);
63
64
static struct acpi_driver acpi_memory_device_driver = {
65
.name = "acpi_memhotplug",
66
.class = ACPI_MEMORY_DEVICE_CLASS,
67
.ids = memory_device_ids,
68
.ops = {
69
.add = acpi_memory_device_add,
70
.remove = acpi_memory_device_remove,
71
},
72
};
73
74
struct acpi_memory_info {
75
struct list_head list;
76
u64 start_addr; /* Memory Range start physical addr */
77
u64 length; /* Memory Range length */
78
unsigned short caching; /* memory cache attribute */
79
unsigned short write_protect; /* memory read/write attribute */
80
unsigned int enabled:1;
81
};
82
83
struct acpi_memory_device {
84
struct acpi_device * device;
85
unsigned int state; /* State of the memory device */
86
struct list_head res_list;
87
};
88
89
static int acpi_hotmem_initialized;
90
91
static acpi_status
92
acpi_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
128
static int
129
acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
130
{
131
acpi_status status;
132
struct acpi_memory_info *info, *n;
133
134
135
if (!list_empty(&mem_device->res_list))
136
return 0;
137
138
status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
139
acpi_memory_get_resource, mem_device);
140
if (ACPI_FAILURE(status)) {
141
list_for_each_entry_safe(info, n, &mem_device->res_list, list)
142
kfree(info);
143
INIT_LIST_HEAD(&mem_device->res_list);
144
return -EINVAL;
145
}
146
147
return 0;
148
}
149
150
static int
151
acpi_memory_get_device(acpi_handle handle,
152
struct acpi_memory_device **mem_device)
153
{
154
acpi_status status;
155
acpi_handle phandle;
156
struct acpi_device *device = NULL;
157
struct acpi_device *pdevice = NULL;
158
int result;
159
160
161
if (!acpi_bus_get_device(handle, &device) && device)
162
goto end;
163
164
status = acpi_get_parent(handle, &phandle);
165
if (ACPI_FAILURE(status)) {
166
ACPI_EXCEPTION((AE_INFO, status, "Cannot find acpi parent"));
167
return -EINVAL;
168
}
169
170
/* Get the parent device */
171
result = acpi_bus_get_device(phandle, &pdevice);
172
if (result) {
173
printk(KERN_WARNING PREFIX "Cannot get acpi bus device");
174
return -EINVAL;
175
}
176
177
/*
178
* Now add the notified device. This creates the acpi_device
179
* and invokes .add function
180
*/
181
result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
182
if (result) {
183
printk(KERN_WARNING PREFIX "Cannot add acpi bus");
184
return -EINVAL;
185
}
186
187
end:
188
*mem_device = acpi_driver_data(device);
189
if (!(*mem_device)) {
190
printk(KERN_ERR "\n driver data not found");
191
return -ENODEV;
192
}
193
194
return 0;
195
}
196
197
static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
198
{
199
unsigned long long current_status;
200
201
/* Get device present/absent information from the _STA */
202
if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle, "_STA",
203
NULL, &current_status)))
204
return -ENODEV;
205
/*
206
* Check for device status. Device should be
207
* present/enabled/functioning.
208
*/
209
if (!((current_status & ACPI_STA_DEVICE_PRESENT)
210
&& (current_status & ACPI_STA_DEVICE_ENABLED)
211
&& (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
212
return -ENODEV;
213
214
return 0;
215
}
216
217
static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
218
{
219
int result, num_enabled = 0;
220
struct acpi_memory_info *info;
221
int node;
222
223
224
/* Get the range from the _CRS */
225
result = acpi_memory_get_device_resources(mem_device);
226
if (result) {
227
printk(KERN_ERR PREFIX "get_device_resources failed\n");
228
mem_device->state = MEMORY_INVALID_STATE;
229
return result;
230
}
231
232
node = acpi_get_node(mem_device->device->handle);
233
/*
234
* Tell the VM there is more memory here...
235
* Note: Assume that this function returns zero on success
236
* We don't have memory-hot-add rollback function,now.
237
* (i.e. memory-hot-remove function)
238
*/
239
list_for_each_entry(info, &mem_device->res_list, list) {
240
if (info->enabled) { /* just sanity check...*/
241
num_enabled++;
242
continue;
243
}
244
/*
245
* If the memory block size is zero, please ignore it.
246
* Don't try to do the following memory hotplug flowchart.
247
*/
248
if (!info->length)
249
continue;
250
if (node < 0)
251
node = memory_add_physaddr_to_nid(info->start_addr);
252
253
result = add_memory(node, info->start_addr, info->length);
254
if (result)
255
continue;
256
info->enabled = 1;
257
num_enabled++;
258
}
259
if (!num_enabled) {
260
printk(KERN_ERR PREFIX "add_memory failed\n");
261
mem_device->state = MEMORY_INVALID_STATE;
262
return -EINVAL;
263
}
264
/*
265
* Sometimes the memory device will contain several memory blocks.
266
* When one memory block is hot-added to the system memory, it will
267
* be regarded as a success.
268
* Otherwise if the last memory block can't be hot-added to the system
269
* memory, it will be failure and the memory device can't be bound with
270
* driver.
271
*/
272
return 0;
273
}
274
275
static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
276
{
277
acpi_status status;
278
struct acpi_object_list arg_list;
279
union acpi_object arg;
280
unsigned long long current_status;
281
282
283
/* Issue the _EJ0 command */
284
arg_list.count = 1;
285
arg_list.pointer = &arg;
286
arg.type = ACPI_TYPE_INTEGER;
287
arg.integer.value = 1;
288
status = acpi_evaluate_object(mem_device->device->handle,
289
"_EJ0", &arg_list, NULL);
290
/* Return on _EJ0 failure */
291
if (ACPI_FAILURE(status)) {
292
ACPI_EXCEPTION((AE_INFO, status, "_EJ0 failed"));
293
return -ENODEV;
294
}
295
296
/* Evalute _STA to check if the device is disabled */
297
status = acpi_evaluate_integer(mem_device->device->handle, "_STA",
298
NULL, &current_status);
299
if (ACPI_FAILURE(status))
300
return -ENODEV;
301
302
/* Check for device status. Device should be disabled */
303
if (current_status & ACPI_STA_DEVICE_ENABLED)
304
return -EINVAL;
305
306
return 0;
307
}
308
309
static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
310
{
311
int result;
312
struct acpi_memory_info *info, *n;
313
314
315
/*
316
* Ask the VM to offline this memory range.
317
* Note: Assume that this function returns zero on success
318
*/
319
list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
320
if (info->enabled) {
321
result = remove_memory(info->start_addr, info->length);
322
if (result)
323
return result;
324
}
325
kfree(info);
326
}
327
328
/* Power-off and eject the device */
329
result = acpi_memory_powerdown_device(mem_device);
330
if (result) {
331
/* Set the status of the device to invalid */
332
mem_device->state = MEMORY_INVALID_STATE;
333
return result;
334
}
335
336
mem_device->state = MEMORY_POWER_OFF_STATE;
337
return result;
338
}
339
340
static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
341
{
342
struct acpi_memory_device *mem_device;
343
struct acpi_device *device;
344
345
346
switch (event) {
347
case ACPI_NOTIFY_BUS_CHECK:
348
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
349
"\nReceived BUS CHECK notification for device\n"));
350
/* Fall Through */
351
case ACPI_NOTIFY_DEVICE_CHECK:
352
if (event == ACPI_NOTIFY_DEVICE_CHECK)
353
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
354
"\nReceived DEVICE CHECK notification for device\n"));
355
if (acpi_memory_get_device(handle, &mem_device)) {
356
printk(KERN_ERR PREFIX "Cannot find driver data\n");
357
return;
358
}
359
360
if (!acpi_memory_check_device(mem_device)) {
361
if (acpi_memory_enable_device(mem_device))
362
printk(KERN_ERR PREFIX
363
"Cannot enable memory device\n");
364
}
365
break;
366
case ACPI_NOTIFY_EJECT_REQUEST:
367
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
368
"\nReceived EJECT REQUEST notification for device\n"));
369
370
if (acpi_bus_get_device(handle, &device)) {
371
printk(KERN_ERR PREFIX "Device doesn't exist\n");
372
break;
373
}
374
mem_device = acpi_driver_data(device);
375
if (!mem_device) {
376
printk(KERN_ERR PREFIX "Driver Data is NULL\n");
377
break;
378
}
379
380
/*
381
* Currently disabling memory device from kernel mode
382
* TBD: Can also be disabled from user mode scripts
383
* TBD: Can also be disabled by Callback registration
384
* with generic sysfs driver
385
*/
386
if (acpi_memory_disable_device(mem_device))
387
printk(KERN_ERR PREFIX
388
"Disable memory device\n");
389
/*
390
* TBD: Invoke acpi_bus_remove to cleanup data structures
391
*/
392
break;
393
default:
394
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
395
"Unsupported event [0x%x]\n", event));
396
break;
397
}
398
399
return;
400
}
401
402
static int acpi_memory_device_add(struct acpi_device *device)
403
{
404
int result;
405
struct acpi_memory_device *mem_device = NULL;
406
407
408
if (!device)
409
return -EINVAL;
410
411
mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
412
if (!mem_device)
413
return -ENOMEM;
414
415
INIT_LIST_HEAD(&mem_device->res_list);
416
mem_device->device = device;
417
sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
418
sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
419
device->driver_data = mem_device;
420
421
/* Get the range from the _CRS */
422
result = acpi_memory_get_device_resources(mem_device);
423
if (result) {
424
kfree(mem_device);
425
return result;
426
}
427
428
/* Set the device state */
429
mem_device->state = MEMORY_POWER_ON_STATE;
430
431
printk(KERN_DEBUG "%s \n", acpi_device_name(device));
432
433
/*
434
* Early boot code has recognized memory area by EFI/E820.
435
* If DSDT shows these memory devices on boot, hotplug is not necessary
436
* for them. So, it just returns until completion of this driver's
437
* start up.
438
*/
439
if (!acpi_hotmem_initialized)
440
return 0;
441
442
if (!acpi_memory_check_device(mem_device)) {
443
/* call add_memory func */
444
result = acpi_memory_enable_device(mem_device);
445
if (result)
446
printk(KERN_ERR PREFIX
447
"Error in acpi_memory_enable_device\n");
448
}
449
return result;
450
}
451
452
static int acpi_memory_device_remove(struct acpi_device *device, int type)
453
{
454
struct acpi_memory_device *mem_device = NULL;
455
456
457
if (!device || !acpi_driver_data(device))
458
return -EINVAL;
459
460
mem_device = acpi_driver_data(device);
461
kfree(mem_device);
462
463
return 0;
464
}
465
466
/*
467
* Helper function to check for memory device
468
*/
469
static acpi_status is_memory_device(acpi_handle handle)
470
{
471
char *hardware_id;
472
acpi_status status;
473
struct acpi_device_info *info;
474
475
status = acpi_get_object_info(handle, &info);
476
if (ACPI_FAILURE(status))
477
return status;
478
479
if (!(info->valid & ACPI_VALID_HID)) {
480
kfree(info);
481
return AE_ERROR;
482
}
483
484
hardware_id = info->hardware_id.string;
485
if ((hardware_id == NULL) ||
486
(strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
487
status = AE_ERROR;
488
489
kfree(info);
490
return status;
491
}
492
493
static acpi_status
494
acpi_memory_register_notify_handler(acpi_handle handle,
495
u32 level, void *ctxt, void **retv)
496
{
497
acpi_status status;
498
499
500
status = is_memory_device(handle);
501
if (ACPI_FAILURE(status))
502
return AE_OK; /* continue */
503
504
status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
505
acpi_memory_device_notify, NULL);
506
/* continue */
507
return AE_OK;
508
}
509
510
static acpi_status
511
acpi_memory_deregister_notify_handler(acpi_handle handle,
512
u32 level, void *ctxt, void **retv)
513
{
514
acpi_status status;
515
516
517
status = is_memory_device(handle);
518
if (ACPI_FAILURE(status))
519
return AE_OK; /* continue */
520
521
status = acpi_remove_notify_handler(handle,
522
ACPI_SYSTEM_NOTIFY,
523
acpi_memory_device_notify);
524
525
return AE_OK; /* continue */
526
}
527
528
static int __init acpi_memory_device_init(void)
529
{
530
int result;
531
acpi_status status;
532
533
534
result = acpi_bus_register_driver(&acpi_memory_device_driver);
535
536
if (result < 0)
537
return -ENODEV;
538
539
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
540
ACPI_UINT32_MAX,
541
acpi_memory_register_notify_handler, NULL,
542
NULL, NULL);
543
544
if (ACPI_FAILURE(status)) {
545
ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
546
acpi_bus_unregister_driver(&acpi_memory_device_driver);
547
return -ENODEV;
548
}
549
550
acpi_hotmem_initialized = 1;
551
return 0;
552
}
553
554
static void __exit acpi_memory_device_exit(void)
555
{
556
acpi_status status;
557
558
559
/*
560
* Adding this to un-install notification handlers for all the device
561
* handles.
562
*/
563
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
564
ACPI_UINT32_MAX,
565
acpi_memory_deregister_notify_handler, NULL,
566
NULL, NULL);
567
568
if (ACPI_FAILURE(status))
569
ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
570
571
acpi_bus_unregister_driver(&acpi_memory_device_driver);
572
573
return;
574
}
575
576
module_init(acpi_memory_device_init);
577
module_exit(acpi_memory_device_exit);
578
579