master
c 247 lines 6.41 KB
Raw
1 /*
2 * QEMU NVM Express Subsystem: nvme-subsys
3 *
4 * Copyright (c) 2021 Minwoo Im <minwoo.im.dev@gmail.com>
5 *
6 * This code is licensed under the GNU GPL v2. Refer COPYING.
7 */
8
9 #include "qemu/osdep.h"
10 #include "qemu/units.h"
11 #include "qapi/error.h"
12 #include "hw/core/qdev.h"
13
14 #include "nvme.h"
15
16 #define NVME_DEFAULT_RU_SIZE (96 * MiB)
17
18 static int nvme_subsys_reserve_cntlids(NvmeCtrl *n, int start, int num)
19 {
20 NvmeSubsystem *subsys = n->subsys;
21 NvmeSecCtrlEntry *list = n->sec_ctrl_list;
22 NvmeSecCtrlEntry *sctrl;
23 int i, cnt = 0;
24
25 for (i = start; i < ARRAY_SIZE(subsys->ctrls) && cnt < num; i++) {
26 if (!subsys->ctrls[i]) {
27 sctrl = &list[cnt];
28 sctrl->scid = cpu_to_le16(i);
29 subsys->ctrls[i] = SUBSYS_SLOT_RSVD;
30 cnt++;
31 }
32 }
33
34 return cnt;
35 }
36
37 static void nvme_subsys_unreserve_cntlids(NvmeCtrl *n)
38 {
39 NvmeSubsystem *subsys = n->subsys;
40 NvmeSecCtrlEntry *list = n->sec_ctrl_list;
41 NvmeSecCtrlEntry *sctrl;
42 int i, cntlid;
43
44 for (i = 0; i < n->params.sriov_max_vfs; i++) {
45 sctrl = &list[i];
46 cntlid = le16_to_cpu(sctrl->scid);
47
48 if (cntlid) {
49 assert(subsys->ctrls[cntlid] == SUBSYS_SLOT_RSVD);
50 subsys->ctrls[cntlid] = NULL;
51 sctrl->scid = 0;
52 }
53 }
54 }
55
56 int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp)
57 {
58 NvmeSubsystem *subsys = n->subsys;
59 NvmeSecCtrlEntry *sctrl = nvme_sctrl(n);
60 int cntlid, num_rsvd, num_vfs = n->params.sriov_max_vfs;
61
62 if (pci_is_vf(&n->parent_obj)) {
63 cntlid = le16_to_cpu(sctrl->scid);
64 } else {
65 n->sec_ctrl_list = g_new0(NvmeSecCtrlEntry, num_vfs);
66
67 for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) {
68 if (!subsys->ctrls[cntlid]) {
69 break;
70 }
71 }
72
73 if (cntlid == ARRAY_SIZE(subsys->ctrls)) {
74 error_setg(errp, "no more free controller id");
75 return -1;
76 }
77
78 num_rsvd = nvme_subsys_reserve_cntlids(n, cntlid + 1, num_vfs);
79 if (num_rsvd != num_vfs) {
80 nvme_subsys_unreserve_cntlids(n);
81 error_setg(errp,
82 "no more free controller ids for secondary controllers");
83 return -1;
84 }
85 }
86
87 if (!subsys->serial) {
88 subsys->serial = g_strdup(n->params.serial);
89 } else if (strcmp(subsys->serial, n->params.serial)) {
90 error_setg(errp, "invalid controller serial");
91 return -1;
92 }
93
94 subsys->ctrls[cntlid] = n;
95
96 return cntlid;
97 }
98
99 void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n)
100 {
101 if (pci_is_vf(&n->parent_obj)) {
102 subsys->ctrls[n->cntlid] = SUBSYS_SLOT_RSVD;
103 } else {
104 subsys->ctrls[n->cntlid] = NULL;
105 nvme_subsys_unreserve_cntlids(n);
106 }
107
108 n->cntlid = -1;
109 }
110
111 static bool nvme_calc_rgif(uint16_t nruh, uint16_t nrg, uint8_t *rgif)
112 {
113 uint16_t val;
114 unsigned int i;
115
116 if (unlikely(nrg == 1)) {
117 /* PIDRG_NORGI scenario, all of pid is used for PHID */
118 *rgif = 0;
119 return true;
120 }
121
122 val = nrg;
123 i = 0;
124 while (val) {
125 val >>= 1;
126 i++;
127 }
128 *rgif = i;
129
130 /* ensure remaining bits suffice to represent number of phids in a RG */
131 if (unlikely((UINT16_MAX >> i) < nruh)) {
132 *rgif = 0;
133 return false;
134 }
135
136 return true;
137 }
138
139 static bool nvme_subsys_setup_fdp(NvmeSubsystem *subsys, Error **errp)
140 {
141 NvmeEnduranceGroup *endgrp = &subsys->endgrp;
142
143 if (!subsys->params.fdp.runs) {
144 error_setg(errp, "fdp.runs must be non-zero");
145 return false;
146 }
147
148 endgrp->fdp.runs = subsys->params.fdp.runs;
149
150 if (!subsys->params.fdp.nrg) {
151 error_setg(errp, "fdp.nrg must be non-zero");
152 return false;
153 }
154
155 endgrp->fdp.nrg = subsys->params.fdp.nrg;
156
157 if (!subsys->params.fdp.nruh ||
158 subsys->params.fdp.nruh > NVME_FDP_MAXPIDS) {
159 error_setg(errp, "fdp.nruh must be non-zero and less than %u",
160 NVME_FDP_MAXPIDS);
161 return false;
162 }
163
164 endgrp->fdp.nruh = subsys->params.fdp.nruh;
165
166 if (!nvme_calc_rgif(endgrp->fdp.nruh, endgrp->fdp.nrg, &endgrp->fdp.rgif)) {
167 error_setg(errp,
168 "cannot derive a valid rgif (nruh %"PRIu16" nrg %"PRIu32")",
169 endgrp->fdp.nruh, endgrp->fdp.nrg);
170 return false;
171 }
172
173 endgrp->fdp.ruhs = g_new(NvmeRuHandle, endgrp->fdp.nruh);
174
175 for (uint16_t ruhid = 0; ruhid < endgrp->fdp.nruh; ruhid++) {
176 endgrp->fdp.ruhs[ruhid] = (NvmeRuHandle) {
177 .ruht = NVME_RUHT_INITIALLY_ISOLATED,
178 .ruha = NVME_RUHA_UNUSED,
179 };
180
181 endgrp->fdp.ruhs[ruhid].rus = g_new(NvmeReclaimUnit, endgrp->fdp.nrg);
182 }
183
184 endgrp->fdp.enabled = true;
185
186 return true;
187 }
188
189 static bool nvme_subsys_setup(NvmeSubsystem *subsys, Error **errp)
190 {
191 const char *nqn = subsys->params.nqn ?
192 subsys->params.nqn : subsys->parent_obj.id;
193
194 snprintf((char *)subsys->subnqn, sizeof(subsys->subnqn),
195 "nqn.2019-08.org.qemu:%s", nqn);
196
197 if (subsys->params.fdp.enabled && !nvme_subsys_setup_fdp(subsys, errp)) {
198 return false;
199 }
200
201 return true;
202 }
203
204 static void nvme_subsys_realize(DeviceState *dev, Error **errp)
205 {
206 NvmeSubsystem *subsys = NVME_SUBSYS(dev);
207
208 qbus_init(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id);
209 qbus_set_bus_hotplug_handler(BUS(&subsys->bus));
210
211 nvme_subsys_setup(subsys, errp);
212 }
213
214 static const Property nvme_subsystem_props[] = {
215 DEFINE_PROP_STRING("nqn", NvmeSubsystem, params.nqn),
216 DEFINE_PROP_BOOL("fdp", NvmeSubsystem, params.fdp.enabled, false),
217 DEFINE_PROP_SIZE("fdp.runs", NvmeSubsystem, params.fdp.runs,
218 NVME_DEFAULT_RU_SIZE),
219 DEFINE_PROP_UINT32("fdp.nrg", NvmeSubsystem, params.fdp.nrg, 1),
220 DEFINE_PROP_UINT16("fdp.nruh", NvmeSubsystem, params.fdp.nruh, 0),
221 };
222
223 static void nvme_subsys_class_init(ObjectClass *oc, const void *data)
224 {
225 DeviceClass *dc = DEVICE_CLASS(oc);
226
227 set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
228
229 dc->realize = nvme_subsys_realize;
230 dc->desc = "Virtual NVMe subsystem";
231
232 device_class_set_props(dc, nvme_subsystem_props);
233 }
234
235 static const TypeInfo nvme_subsys_info = {
236 .name = TYPE_NVME_SUBSYS,
237 .parent = TYPE_DEVICE,
238 .class_init = nvme_subsys_class_init,
239 .instance_size = sizeof(NvmeSubsystem),
240 };
241
242 static void nvme_subsys_register_types(void)
243 {
244 type_register_static(&nvme_subsys_info);
245 }
246
247 type_init(nvme_subsys_register_types)