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