pc-dimm.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. * Dimm device for Memory Hotplug
  3. *
  4. * Copyright ProfitBricks GmbH 2012
  5. * Copyright (C) 2014 Red Hat Inc
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, see <http://www.gnu.org/licenses/>
  19. */
  20. #include "qemu/osdep.h"
  21. #include "hw/boards.h"
  22. #include "hw/mem/pc-dimm.h"
  23. #include "hw/qdev-properties.h"
  24. #include "migration/vmstate.h"
  25. #include "hw/mem/nvdimm.h"
  26. #include "hw/mem/memory-device.h"
  27. #include "qapi/error.h"
  28. #include "qapi/visitor.h"
  29. #include "qemu/module.h"
  30. #include "sysemu/hostmem.h"
  31. #include "sysemu/numa.h"
  32. #include "trace.h"
  33. static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp);
  34. void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine,
  35. const uint64_t *legacy_align, Error **errp)
  36. {
  37. Error *local_err = NULL;
  38. int slot;
  39. slot = object_property_get_int(OBJECT(dimm), PC_DIMM_SLOT_PROP,
  40. &error_abort);
  41. if ((slot < 0 || slot >= machine->ram_slots) &&
  42. slot != PC_DIMM_UNASSIGNED_SLOT) {
  43. error_setg(&local_err, "invalid slot number, valid range is [0-%"
  44. PRIu64 "]", machine->ram_slots - 1);
  45. goto out;
  46. }
  47. slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot,
  48. machine->ram_slots, &local_err);
  49. if (local_err) {
  50. goto out;
  51. }
  52. object_property_set_int(OBJECT(dimm), slot, PC_DIMM_SLOT_PROP,
  53. &error_abort);
  54. trace_mhp_pc_dimm_assigned_slot(slot);
  55. memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, legacy_align,
  56. &local_err);
  57. out:
  58. error_propagate(errp, local_err);
  59. }
  60. void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp)
  61. {
  62. PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
  63. MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm,
  64. &error_abort);
  65. memory_device_plug(MEMORY_DEVICE(dimm), machine);
  66. vmstate_register_ram(vmstate_mr, DEVICE(dimm));
  67. }
  68. void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine)
  69. {
  70. PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
  71. MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm,
  72. &error_abort);
  73. memory_device_unplug(MEMORY_DEVICE(dimm), machine);
  74. vmstate_unregister_ram(vmstate_mr, DEVICE(dimm));
  75. }
  76. static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
  77. {
  78. unsigned long *bitmap = opaque;
  79. if (object_dynamic_cast(obj, TYPE_PC_DIMM)) {
  80. DeviceState *dev = DEVICE(obj);
  81. if (dev->realized) { /* count only realized DIMMs */
  82. PCDIMMDevice *d = PC_DIMM(obj);
  83. set_bit(d->slot, bitmap);
  84. }
  85. }
  86. object_child_foreach(obj, pc_dimm_slot2bitmap, opaque);
  87. return 0;
  88. }
  89. static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp)
  90. {
  91. unsigned long *bitmap;
  92. int slot = 0;
  93. if (max_slots <= 0) {
  94. error_setg(errp, "no slots where allocated, please specify "
  95. "the 'slots' option");
  96. return slot;
  97. }
  98. bitmap = bitmap_new(max_slots);
  99. object_child_foreach(qdev_get_machine(), pc_dimm_slot2bitmap, bitmap);
  100. /* check if requested slot is not occupied */
  101. if (hint) {
  102. if (*hint >= max_slots) {
  103. error_setg(errp, "invalid slot# %d, should be less than %d",
  104. *hint, max_slots);
  105. } else if (!test_bit(*hint, bitmap)) {
  106. slot = *hint;
  107. } else {
  108. error_setg(errp, "slot %d is busy", *hint);
  109. }
  110. goto out;
  111. }
  112. /* search for free slot */
  113. slot = find_first_zero_bit(bitmap, max_slots);
  114. if (slot == max_slots) {
  115. error_setg(errp, "no free slots available");
  116. }
  117. out:
  118. g_free(bitmap);
  119. return slot;
  120. }
  121. static Property pc_dimm_properties[] = {
  122. DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0),
  123. DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0),
  124. DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot,
  125. PC_DIMM_UNASSIGNED_SLOT),
  126. DEFINE_PROP_LINK(PC_DIMM_MEMDEV_PROP, PCDIMMDevice, hostmem,
  127. TYPE_MEMORY_BACKEND, HostMemoryBackend *),
  128. DEFINE_PROP_END_OF_LIST(),
  129. };
  130. static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name,
  131. void *opaque, Error **errp)
  132. {
  133. Error *local_err = NULL;
  134. uint64_t value;
  135. value = memory_device_get_region_size(MEMORY_DEVICE(obj), &local_err);
  136. if (local_err) {
  137. error_propagate(errp, local_err);
  138. return;
  139. }
  140. visit_type_uint64(v, name, &value, errp);
  141. }
  142. static void pc_dimm_init(Object *obj)
  143. {
  144. object_property_add(obj, PC_DIMM_SIZE_PROP, "uint64", pc_dimm_get_size,
  145. NULL, NULL, NULL, &error_abort);
  146. }
  147. static void pc_dimm_realize(DeviceState *dev, Error **errp)
  148. {
  149. PCDIMMDevice *dimm = PC_DIMM(dev);
  150. PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
  151. MachineState *ms = MACHINE(qdev_get_machine());
  152. int nb_numa_nodes = ms->numa_state->num_nodes;
  153. if (!dimm->hostmem) {
  154. error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set");
  155. return;
  156. } else if (host_memory_backend_is_mapped(dimm->hostmem)) {
  157. char *path = object_get_canonical_path_component(OBJECT(dimm->hostmem));
  158. error_setg(errp, "can't use already busy memdev: %s", path);
  159. g_free(path);
  160. return;
  161. }
  162. if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) ||
  163. (!nb_numa_nodes && dimm->node)) {
  164. error_setg(errp, "'DIMM property " PC_DIMM_NODE_PROP " has value %"
  165. PRIu32 "' which exceeds the number of numa nodes: %d",
  166. dimm->node, nb_numa_nodes ? nb_numa_nodes : 1);
  167. return;
  168. }
  169. if (ddc->realize) {
  170. ddc->realize(dimm, errp);
  171. }
  172. host_memory_backend_set_mapped(dimm->hostmem, true);
  173. }
  174. static void pc_dimm_unrealize(DeviceState *dev, Error **errp)
  175. {
  176. PCDIMMDevice *dimm = PC_DIMM(dev);
  177. host_memory_backend_set_mapped(dimm->hostmem, false);
  178. }
  179. static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp)
  180. {
  181. if (!dimm->hostmem) {
  182. error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set");
  183. return NULL;
  184. }
  185. return host_memory_backend_get_memory(dimm->hostmem);
  186. }
  187. static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md)
  188. {
  189. return object_property_get_uint(OBJECT(md), PC_DIMM_ADDR_PROP, &error_abort);
  190. }
  191. static void pc_dimm_md_set_addr(MemoryDeviceState *md, uint64_t addr,
  192. Error **errp)
  193. {
  194. object_property_set_uint(OBJECT(md), addr, PC_DIMM_ADDR_PROP, errp);
  195. }
  196. static MemoryRegion *pc_dimm_md_get_memory_region(MemoryDeviceState *md,
  197. Error **errp)
  198. {
  199. return pc_dimm_get_memory_region(PC_DIMM(md), errp);
  200. }
  201. static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md,
  202. MemoryDeviceInfo *info)
  203. {
  204. PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1);
  205. const DeviceClass *dc = DEVICE_GET_CLASS(md);
  206. const PCDIMMDevice *dimm = PC_DIMM(md);
  207. const DeviceState *dev = DEVICE(md);
  208. if (dev->id) {
  209. di->has_id = true;
  210. di->id = g_strdup(dev->id);
  211. }
  212. di->hotplugged = dev->hotplugged;
  213. di->hotpluggable = dc->hotpluggable;
  214. di->addr = dimm->addr;
  215. di->slot = dimm->slot;
  216. di->node = dimm->node;
  217. di->size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP,
  218. NULL);
  219. di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));
  220. if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
  221. info->u.nvdimm.data = di;
  222. info->type = MEMORY_DEVICE_INFO_KIND_NVDIMM;
  223. } else {
  224. info->u.dimm.data = di;
  225. info->type = MEMORY_DEVICE_INFO_KIND_DIMM;
  226. }
  227. }
  228. static void pc_dimm_class_init(ObjectClass *oc, void *data)
  229. {
  230. DeviceClass *dc = DEVICE_CLASS(oc);
  231. PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc);
  232. MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
  233. dc->realize = pc_dimm_realize;
  234. dc->unrealize = pc_dimm_unrealize;
  235. dc->props = pc_dimm_properties;
  236. dc->desc = "DIMM memory module";
  237. ddc->get_vmstate_memory_region = pc_dimm_get_memory_region;
  238. mdc->get_addr = pc_dimm_md_get_addr;
  239. mdc->set_addr = pc_dimm_md_set_addr;
  240. /* for a dimm plugged_size == region_size */
  241. mdc->get_plugged_size = memory_device_get_region_size;
  242. mdc->get_memory_region = pc_dimm_md_get_memory_region;
  243. mdc->fill_device_info = pc_dimm_md_fill_device_info;
  244. }
  245. static TypeInfo pc_dimm_info = {
  246. .name = TYPE_PC_DIMM,
  247. .parent = TYPE_DEVICE,
  248. .instance_size = sizeof(PCDIMMDevice),
  249. .instance_init = pc_dimm_init,
  250. .class_init = pc_dimm_class_init,
  251. .class_size = sizeof(PCDIMMDeviceClass),
  252. .interfaces = (InterfaceInfo[]) {
  253. { TYPE_MEMORY_DEVICE },
  254. { }
  255. },
  256. };
  257. static void pc_dimm_register_types(void)
  258. {
  259. type_register_static(&pc_dimm_info);
  260. }
  261. type_init(pc_dimm_register_types)