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