pc-dimm.c 9.6 KB

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