2 * QEMU NVM Express Subsystem: nvme-subsys
4 * Copyright (c) 2021 Minwoo Im <minwoo.im.dev@gmail.com>
6 * This code is licensed under the GNU GPL v2. Refer COPYING.
9 #include "qemu/osdep.h"
10 #include "qemu/units.h"
11 #include "qapi/error.h"
15 #define NVME_DEFAULT_RU_SIZE (96 * MiB)
17 static int nvme_subsys_reserve_cntlids(NvmeCtrl
*n
, int start
, int num
)
19 NvmeSubsystem
*subsys
= n
->subsys
;
20 NvmeSecCtrlEntry
*list
= n
->sec_ctrl_list
;
21 NvmeSecCtrlEntry
*sctrl
;
24 for (i
= start
; i
< ARRAY_SIZE(subsys
->ctrls
) && cnt
< num
; i
++) {
25 if (!subsys
->ctrls
[i
]) {
27 sctrl
->scid
= cpu_to_le16(i
);
28 subsys
->ctrls
[i
] = SUBSYS_SLOT_RSVD
;
36 static void nvme_subsys_unreserve_cntlids(NvmeCtrl
*n
)
38 NvmeSubsystem
*subsys
= n
->subsys
;
39 NvmeSecCtrlEntry
*list
= n
->sec_ctrl_list
;
40 NvmeSecCtrlEntry
*sctrl
;
43 for (i
= 0; i
< n
->params
.sriov_max_vfs
; i
++) {
45 cntlid
= le16_to_cpu(sctrl
->scid
);
48 assert(subsys
->ctrls
[cntlid
] == SUBSYS_SLOT_RSVD
);
49 subsys
->ctrls
[cntlid
] = NULL
;
55 int nvme_subsys_register_ctrl(NvmeCtrl
*n
, Error
**errp
)
57 NvmeSubsystem
*subsys
= n
->subsys
;
58 NvmeSecCtrlEntry
*sctrl
= nvme_sctrl(n
);
59 int cntlid
, nsid
, num_rsvd
, num_vfs
= n
->params
.sriov_max_vfs
;
61 if (pci_is_vf(&n
->parent_obj
)) {
62 cntlid
= le16_to_cpu(sctrl
->scid
);
64 n
->sec_ctrl_list
= g_new0(NvmeSecCtrlEntry
, num_vfs
);
66 for (cntlid
= 0; cntlid
< ARRAY_SIZE(subsys
->ctrls
); cntlid
++) {
67 if (!subsys
->ctrls
[cntlid
]) {
72 if (cntlid
== ARRAY_SIZE(subsys
->ctrls
)) {
73 error_setg(errp
, "no more free controller id");
77 num_rsvd
= nvme_subsys_reserve_cntlids(n
, cntlid
+ 1, num_vfs
);
78 if (num_rsvd
!= num_vfs
) {
79 nvme_subsys_unreserve_cntlids(n
);
81 "no more free controller ids for secondary controllers");
86 if (!subsys
->serial
) {
87 subsys
->serial
= g_strdup(n
->params
.serial
);
88 } else if (strcmp(subsys
->serial
, n
->params
.serial
)) {
89 error_setg(errp
, "invalid controller serial");
93 subsys
->ctrls
[cntlid
] = n
;
95 for (nsid
= 1; nsid
< ARRAY_SIZE(subsys
->namespaces
); nsid
++) {
96 NvmeNamespace
*ns
= subsys
->namespaces
[nsid
];
97 if (ns
&& ns
->params
.shared
&& !ns
->params
.detached
) {
98 nvme_attach_ns(n
, ns
);
105 void nvme_subsys_unregister_ctrl(NvmeSubsystem
*subsys
, NvmeCtrl
*n
)
107 if (pci_is_vf(&n
->parent_obj
)) {
108 subsys
->ctrls
[n
->cntlid
] = SUBSYS_SLOT_RSVD
;
110 subsys
->ctrls
[n
->cntlid
] = NULL
;
111 nvme_subsys_unreserve_cntlids(n
);
117 static bool nvme_calc_rgif(uint16_t nruh
, uint16_t nrg
, uint8_t *rgif
)
122 if (unlikely(nrg
== 1)) {
123 /* PIDRG_NORGI scenario, all of pid is used for PHID */
136 /* ensure remaining bits suffice to represent number of phids in a RG */
137 if (unlikely((UINT16_MAX
>> i
) < nruh
)) {
145 static bool nvme_subsys_setup_fdp(NvmeSubsystem
*subsys
, Error
**errp
)
147 NvmeEnduranceGroup
*endgrp
= &subsys
->endgrp
;
149 if (!subsys
->params
.fdp
.runs
) {
150 error_setg(errp
, "fdp.runs must be non-zero");
154 endgrp
->fdp
.runs
= subsys
->params
.fdp
.runs
;
156 if (!subsys
->params
.fdp
.nrg
) {
157 error_setg(errp
, "fdp.nrg must be non-zero");
161 endgrp
->fdp
.nrg
= subsys
->params
.fdp
.nrg
;
163 if (!subsys
->params
.fdp
.nruh
||
164 subsys
->params
.fdp
.nruh
> NVME_FDP_MAXPIDS
) {
165 error_setg(errp
, "fdp.nruh must be non-zero and less than %u",
170 endgrp
->fdp
.nruh
= subsys
->params
.fdp
.nruh
;
172 if (!nvme_calc_rgif(endgrp
->fdp
.nruh
, endgrp
->fdp
.nrg
, &endgrp
->fdp
.rgif
)) {
174 "cannot derive a valid rgif (nruh %"PRIu16
" nrg %"PRIu32
")",
175 endgrp
->fdp
.nruh
, endgrp
->fdp
.nrg
);
179 endgrp
->fdp
.ruhs
= g_new(NvmeRuHandle
, endgrp
->fdp
.nruh
);
181 for (uint16_t ruhid
= 0; ruhid
< endgrp
->fdp
.nruh
; ruhid
++) {
182 endgrp
->fdp
.ruhs
[ruhid
] = (NvmeRuHandle
) {
183 .ruht
= NVME_RUHT_INITIALLY_ISOLATED
,
184 .ruha
= NVME_RUHA_UNUSED
,
187 endgrp
->fdp
.ruhs
[ruhid
].rus
= g_new(NvmeReclaimUnit
, endgrp
->fdp
.nrg
);
190 endgrp
->fdp
.enabled
= true;
195 static bool nvme_subsys_setup(NvmeSubsystem
*subsys
, Error
**errp
)
197 const char *nqn
= subsys
->params
.nqn
?
198 subsys
->params
.nqn
: subsys
->parent_obj
.id
;
200 snprintf((char *)subsys
->subnqn
, sizeof(subsys
->subnqn
),
201 "nqn.2019-08.org.qemu:%s", nqn
);
203 if (subsys
->params
.fdp
.enabled
&& !nvme_subsys_setup_fdp(subsys
, errp
)) {
210 static void nvme_subsys_realize(DeviceState
*dev
, Error
**errp
)
212 NvmeSubsystem
*subsys
= NVME_SUBSYS(dev
);
214 qbus_init(&subsys
->bus
, sizeof(NvmeBus
), TYPE_NVME_BUS
, dev
, dev
->id
);
216 nvme_subsys_setup(subsys
, errp
);
219 static Property nvme_subsystem_props
[] = {
220 DEFINE_PROP_STRING("nqn", NvmeSubsystem
, params
.nqn
),
221 DEFINE_PROP_BOOL("fdp", NvmeSubsystem
, params
.fdp
.enabled
, false),
222 DEFINE_PROP_SIZE("fdp.runs", NvmeSubsystem
, params
.fdp
.runs
,
223 NVME_DEFAULT_RU_SIZE
),
224 DEFINE_PROP_UINT32("fdp.nrg", NvmeSubsystem
, params
.fdp
.nrg
, 1),
225 DEFINE_PROP_UINT16("fdp.nruh", NvmeSubsystem
, params
.fdp
.nruh
, 0),
226 DEFINE_PROP_END_OF_LIST(),
229 static void nvme_subsys_class_init(ObjectClass
*oc
, void *data
)
231 DeviceClass
*dc
= DEVICE_CLASS(oc
);
233 set_bit(DEVICE_CATEGORY_STORAGE
, dc
->categories
);
235 dc
->realize
= nvme_subsys_realize
;
236 dc
->desc
= "Virtual NVMe subsystem";
237 dc
->hotpluggable
= false;
239 device_class_set_props(dc
, nvme_subsystem_props
);
242 static const TypeInfo nvme_subsys_info
= {
243 .name
= TYPE_NVME_SUBSYS
,
244 .parent
= TYPE_DEVICE
,
245 .class_init
= nvme_subsys_class_init
,
246 .instance_size
= sizeof(NvmeSubsystem
),
249 static void nvme_subsys_register_types(void)
251 type_register_static(&nvme_subsys_info
);
254 type_init(nvme_subsys_register_types
)