pc-dimm.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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.1 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 "system/hostmem.h"
  31. #include "system/numa.h"
  32. #include "trace.h"
  33. static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp);
  34. static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp)
  35. {
  36. if (!dimm->hostmem) {
  37. error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set");
  38. return NULL;
  39. }
  40. return host_memory_backend_get_memory(dimm->hostmem);
  41. }
  42. void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp)
  43. {
  44. Error *local_err = NULL;
  45. int slot;
  46. slot = object_property_get_int(OBJECT(dimm), PC_DIMM_SLOT_PROP,
  47. &error_abort);
  48. if ((slot < 0 || slot >= machine->ram_slots) &&
  49. slot != PC_DIMM_UNASSIGNED_SLOT) {
  50. error_setg(errp,
  51. "invalid slot number %d, valid range is [0-%" PRIu64 "]",
  52. slot, machine->ram_slots - 1);
  53. return;
  54. }
  55. slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot,
  56. machine->ram_slots, &local_err);
  57. if (local_err) {
  58. error_propagate(errp, local_err);
  59. return;
  60. }
  61. object_property_set_int(OBJECT(dimm), PC_DIMM_SLOT_PROP, slot,
  62. &error_abort);
  63. trace_mhp_pc_dimm_assigned_slot(slot);
  64. memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, errp);
  65. }
  66. void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine)
  67. {
  68. MemoryRegion *vmstate_mr = pc_dimm_get_memory_region(dimm,
  69. &error_abort);
  70. memory_device_plug(MEMORY_DEVICE(dimm), machine);
  71. vmstate_register_ram(vmstate_mr, DEVICE(dimm));
  72. /* count only "real" DIMMs, not NVDIMMs */
  73. if (!object_dynamic_cast(OBJECT(dimm), TYPE_NVDIMM)) {
  74. machine->device_memory->dimm_size += memory_region_size(vmstate_mr);
  75. }
  76. }
  77. void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine)
  78. {
  79. MemoryRegion *vmstate_mr = pc_dimm_get_memory_region(dimm,
  80. &error_abort);
  81. memory_device_unplug(MEMORY_DEVICE(dimm), machine);
  82. vmstate_unregister_ram(vmstate_mr, DEVICE(dimm));
  83. if (!object_dynamic_cast(OBJECT(dimm), TYPE_NVDIMM)) {
  84. machine->device_memory->dimm_size -= memory_region_size(vmstate_mr);
  85. }
  86. }
  87. static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
  88. {
  89. unsigned long *bitmap = opaque;
  90. if (object_dynamic_cast(obj, TYPE_PC_DIMM)) {
  91. DeviceState *dev = DEVICE(obj);
  92. if (dev->realized) { /* count only realized DIMMs */
  93. PCDIMMDevice *d = PC_DIMM(obj);
  94. set_bit(d->slot, bitmap);
  95. }
  96. }
  97. object_child_foreach(obj, pc_dimm_slot2bitmap, opaque);
  98. return 0;
  99. }
  100. static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp)
  101. {
  102. unsigned long *bitmap;
  103. int slot = 0;
  104. if (max_slots <= 0) {
  105. error_setg(errp, "no slots where allocated, please specify "
  106. "the 'slots' option");
  107. return slot;
  108. }
  109. bitmap = bitmap_new(max_slots);
  110. object_child_foreach(qdev_get_machine(), pc_dimm_slot2bitmap, bitmap);
  111. /* check if requested slot is not occupied */
  112. if (hint) {
  113. if (*hint >= max_slots) {
  114. error_setg(errp, "invalid slot# %d, should be less than %d",
  115. *hint, max_slots);
  116. } else if (!test_bit(*hint, bitmap)) {
  117. slot = *hint;
  118. } else {
  119. error_setg(errp, "slot %d is busy", *hint);
  120. }
  121. goto out;
  122. }
  123. /* search for free slot */
  124. slot = find_first_zero_bit(bitmap, max_slots);
  125. if (slot == max_slots) {
  126. error_setg(errp, "no free slots available");
  127. }
  128. out:
  129. g_free(bitmap);
  130. return slot;
  131. }
  132. static const Property pc_dimm_properties[] = {
  133. DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0),
  134. DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0),
  135. DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot,
  136. PC_DIMM_UNASSIGNED_SLOT),
  137. DEFINE_PROP_LINK(PC_DIMM_MEMDEV_PROP, PCDIMMDevice, hostmem,
  138. TYPE_MEMORY_BACKEND, HostMemoryBackend *),
  139. };
  140. static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name,
  141. void *opaque, Error **errp)
  142. {
  143. Error *local_err = NULL;
  144. uint64_t value;
  145. value = memory_device_get_region_size(MEMORY_DEVICE(obj), &local_err);
  146. if (local_err) {
  147. error_propagate(errp, local_err);
  148. return;
  149. }
  150. visit_type_uint64(v, name, &value, errp);
  151. }
  152. static void pc_dimm_init(Object *obj)
  153. {
  154. object_property_add(obj, PC_DIMM_SIZE_PROP, "uint64", pc_dimm_get_size,
  155. NULL, NULL, NULL);
  156. }
  157. static void pc_dimm_realize(DeviceState *dev, Error **errp)
  158. {
  159. PCDIMMDevice *dimm = PC_DIMM(dev);
  160. PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
  161. MachineState *ms = MACHINE(qdev_get_machine());
  162. if (ms->numa_state) {
  163. int nb_numa_nodes = ms->numa_state->num_nodes;
  164. if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) ||
  165. (!nb_numa_nodes && dimm->node)) {
  166. error_setg(errp, "'DIMM property " PC_DIMM_NODE_PROP " has value %"
  167. PRIu32 "' which exceeds the number of numa nodes: %d",
  168. dimm->node, nb_numa_nodes ? nb_numa_nodes : 1);
  169. return;
  170. }
  171. } else if (dimm->node > 0) {
  172. error_setg(errp, "machine doesn't support NUMA");
  173. return;
  174. }
  175. if (!dimm->hostmem) {
  176. error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set");
  177. return;
  178. } else if (host_memory_backend_is_mapped(dimm->hostmem)) {
  179. error_setg(errp, "can't use already busy memdev: %s",
  180. object_get_canonical_path_component(OBJECT(dimm->hostmem)));
  181. return;
  182. }
  183. if (ddc->realize) {
  184. ddc->realize(dimm, errp);
  185. }
  186. host_memory_backend_set_mapped(dimm->hostmem, true);
  187. }
  188. static void pc_dimm_unrealize(DeviceState *dev)
  189. {
  190. PCDIMMDevice *dimm = PC_DIMM(dev);
  191. PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
  192. if (ddc->unrealize) {
  193. ddc->unrealize(dimm);
  194. }
  195. host_memory_backend_set_mapped(dimm->hostmem, false);
  196. }
  197. static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md)
  198. {
  199. return object_property_get_uint(OBJECT(md), PC_DIMM_ADDR_PROP,
  200. &error_abort);
  201. }
  202. static void pc_dimm_md_set_addr(MemoryDeviceState *md, uint64_t addr,
  203. Error **errp)
  204. {
  205. object_property_set_uint(OBJECT(md), PC_DIMM_ADDR_PROP, addr, errp);
  206. }
  207. static MemoryRegion *pc_dimm_md_get_memory_region(MemoryDeviceState *md,
  208. Error **errp)
  209. {
  210. return pc_dimm_get_memory_region(PC_DIMM(md), errp);
  211. }
  212. static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md,
  213. MemoryDeviceInfo *info)
  214. {
  215. PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1);
  216. const DeviceClass *dc = DEVICE_GET_CLASS(md);
  217. const PCDIMMDevice *dimm = PC_DIMM(md);
  218. const DeviceState *dev = DEVICE(md);
  219. if (dev->id) {
  220. di->id = g_strdup(dev->id);
  221. }
  222. di->hotplugged = dev->hotplugged;
  223. di->hotpluggable = dc->hotpluggable;
  224. di->addr = dimm->addr;
  225. di->slot = dimm->slot;
  226. di->node = dimm->node;
  227. di->size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP,
  228. NULL);
  229. di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));
  230. if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
  231. info->u.nvdimm.data = di;
  232. info->type = MEMORY_DEVICE_INFO_KIND_NVDIMM;
  233. } else {
  234. info->u.dimm.data = di;
  235. info->type = MEMORY_DEVICE_INFO_KIND_DIMM;
  236. }
  237. }
  238. static void pc_dimm_class_init(ObjectClass *oc, void *data)
  239. {
  240. DeviceClass *dc = DEVICE_CLASS(oc);
  241. MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
  242. dc->realize = pc_dimm_realize;
  243. dc->unrealize = pc_dimm_unrealize;
  244. device_class_set_props(dc, pc_dimm_properties);
  245. dc->desc = "DIMM memory module";
  246. mdc->get_addr = pc_dimm_md_get_addr;
  247. mdc->set_addr = pc_dimm_md_set_addr;
  248. /* for a dimm plugged_size == region_size */
  249. mdc->get_plugged_size = memory_device_get_region_size;
  250. mdc->get_memory_region = pc_dimm_md_get_memory_region;
  251. mdc->fill_device_info = pc_dimm_md_fill_device_info;
  252. }
  253. static const TypeInfo pc_dimm_info = {
  254. .name = TYPE_PC_DIMM,
  255. .parent = TYPE_DEVICE,
  256. .instance_size = sizeof(PCDIMMDevice),
  257. .instance_init = pc_dimm_init,
  258. .class_init = pc_dimm_class_init,
  259. .class_size = sizeof(PCDIMMDeviceClass),
  260. .interfaces = (InterfaceInfo[]) {
  261. { TYPE_MEMORY_DEVICE },
  262. { }
  263. },
  264. };
  265. static void pc_dimm_register_types(void)
  266. {
  267. type_register_static(&pc_dimm_info);
  268. }
  269. type_init(pc_dimm_register_types)