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 NvmeSecCtrlList
*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
]) {
26 sctrl
= &list
->sec
[cnt
];
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 NvmeSecCtrlList
*list
= &n
->sec_ctrl_list
;
40 NvmeSecCtrlEntry
*sctrl
;
43 for (i
= 0; i
< n
->params
.sriov_max_vfs
; i
++) {
44 sctrl
= &list
->sec
[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 for (cntlid
= 0; cntlid
< ARRAY_SIZE(subsys
->ctrls
); cntlid
++) {
65 if (!subsys
->ctrls
[cntlid
]) {
70 if (cntlid
== ARRAY_SIZE(subsys
->ctrls
)) {
71 error_setg(errp
, "no more free controller id");
75 num_rsvd
= nvme_subsys_reserve_cntlids(n
, cntlid
+ 1, num_vfs
);
76 if (num_rsvd
!= num_vfs
) {
77 nvme_subsys_unreserve_cntlids(n
);
79 "no more free controller ids for secondary controllers");
84 if (!subsys
->serial
) {
85 subsys
->serial
= g_strdup(n
->params
.serial
);
86 } else if (strcmp(subsys
->serial
, n
->params
.serial
)) {
87 error_setg(errp
, "invalid controller serial");
91 subsys
->ctrls
[cntlid
] = n
;
93 for (nsid
= 1; nsid
< ARRAY_SIZE(subsys
->namespaces
); nsid
++) {
94 NvmeNamespace
*ns
= subsys
->namespaces
[nsid
];
95 if (ns
&& ns
->params
.shared
&& !ns
->params
.detached
) {
96 nvme_attach_ns(n
, ns
);
103 void nvme_subsys_unregister_ctrl(NvmeSubsystem
*subsys
, NvmeCtrl
*n
)
105 if (pci_is_vf(&n
->parent_obj
)) {
106 subsys
->ctrls
[n
->cntlid
] = SUBSYS_SLOT_RSVD
;
108 subsys
->ctrls
[n
->cntlid
] = NULL
;
109 nvme_subsys_unreserve_cntlids(n
);
115 static bool nvme_calc_rgif(uint16_t nruh
, uint16_t nrg
, uint8_t *rgif
)
120 if (unlikely(nrg
== 1)) {
121 /* PIDRG_NORGI scenario, all of pid is used for PHID */
134 /* ensure remaining bits suffice to represent number of phids in a RG */
135 if (unlikely((UINT16_MAX
>> i
) < nruh
)) {
143 static bool nvme_subsys_setup_fdp(NvmeSubsystem
*subsys
, Error
**errp
)
145 NvmeEnduranceGroup
*endgrp
= &subsys
->endgrp
;
147 if (!subsys
->params
.fdp
.runs
) {
148 error_setg(errp
, "fdp.runs must be non-zero");
152 endgrp
->fdp
.runs
= subsys
->params
.fdp
.runs
;
154 if (!subsys
->params
.fdp
.nrg
) {
155 error_setg(errp
, "fdp.nrg must be non-zero");
159 endgrp
->fdp
.nrg
= subsys
->params
.fdp
.nrg
;
161 if (!subsys
->params
.fdp
.nruh
||
162 subsys
->params
.fdp
.nruh
> NVME_FDP_MAXPIDS
) {
163 error_setg(errp
, "fdp.nruh must be non-zero and less than %u",
168 endgrp
->fdp
.nruh
= subsys
->params
.fdp
.nruh
;
170 if (!nvme_calc_rgif(endgrp
->fdp
.nruh
, endgrp
->fdp
.nrg
, &endgrp
->fdp
.rgif
)) {
172 "cannot derive a valid rgif (nruh %"PRIu16
" nrg %"PRIu32
")",
173 endgrp
->fdp
.nruh
, endgrp
->fdp
.nrg
);
177 endgrp
->fdp
.ruhs
= g_new(NvmeRuHandle
, endgrp
->fdp
.nruh
);
179 for (uint16_t ruhid
= 0; ruhid
< endgrp
->fdp
.nruh
; ruhid
++) {
180 endgrp
->fdp
.ruhs
[ruhid
] = (NvmeRuHandle
) {
181 .ruht
= NVME_RUHT_INITIALLY_ISOLATED
,
182 .ruha
= NVME_RUHA_UNUSED
,
185 endgrp
->fdp
.ruhs
[ruhid
].rus
= g_new(NvmeReclaimUnit
, endgrp
->fdp
.nrg
);
188 endgrp
->fdp
.enabled
= true;
193 static bool nvme_subsys_setup(NvmeSubsystem
*subsys
, Error
**errp
)
195 const char *nqn
= subsys
->params
.nqn
?
196 subsys
->params
.nqn
: subsys
->parent_obj
.id
;
198 snprintf((char *)subsys
->subnqn
, sizeof(subsys
->subnqn
),
199 "nqn.2019-08.org.qemu:%s", nqn
);
201 if (subsys
->params
.fdp
.enabled
&& !nvme_subsys_setup_fdp(subsys
, errp
)) {
208 static void nvme_subsys_realize(DeviceState
*dev
, Error
**errp
)
210 NvmeSubsystem
*subsys
= NVME_SUBSYS(dev
);
212 qbus_init(&subsys
->bus
, sizeof(NvmeBus
), TYPE_NVME_BUS
, dev
, dev
->id
);
214 nvme_subsys_setup(subsys
, errp
);
217 static Property nvme_subsystem_props
[] = {
218 DEFINE_PROP_STRING("nqn", NvmeSubsystem
, params
.nqn
),
219 DEFINE_PROP_BOOL("fdp", NvmeSubsystem
, params
.fdp
.enabled
, false),
220 DEFINE_PROP_SIZE("fdp.runs", NvmeSubsystem
, params
.fdp
.runs
,
221 NVME_DEFAULT_RU_SIZE
),
222 DEFINE_PROP_UINT32("fdp.nrg", NvmeSubsystem
, params
.fdp
.nrg
, 1),
223 DEFINE_PROP_UINT16("fdp.nruh", NvmeSubsystem
, params
.fdp
.nruh
, 0),
224 DEFINE_PROP_END_OF_LIST(),
227 static void nvme_subsys_class_init(ObjectClass
*oc
, void *data
)
229 DeviceClass
*dc
= DEVICE_CLASS(oc
);
231 set_bit(DEVICE_CATEGORY_STORAGE
, dc
->categories
);
233 dc
->realize
= nvme_subsys_realize
;
234 dc
->desc
= "Virtual NVMe subsystem";
235 dc
->hotpluggable
= false;
237 device_class_set_props(dc
, nvme_subsystem_props
);
240 static const TypeInfo nvme_subsys_info
= {
241 .name
= TYPE_NVME_SUBSYS
,
242 .parent
= TYPE_DEVICE
,
243 .class_init
= nvme_subsys_class_init
,
244 .instance_size
= sizeof(NvmeSubsystem
),
247 static void nvme_subsys_register_types(void)
249 type_register_static(&nvme_subsys_info
);
252 type_init(nvme_subsys_register_types
)