xref: /openbmc/qemu/hw/nvme/subsys.c (revision ab1b2ba9)
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 "qapi/error.h"
11 
12 #include "nvme.h"
13 
14 static int nvme_subsys_reserve_cntlids(NvmeCtrl *n, int start, int num)
15 {
16     NvmeSubsystem *subsys = n->subsys;
17     NvmeSecCtrlList *list = &n->sec_ctrl_list;
18     NvmeSecCtrlEntry *sctrl;
19     int i, cnt = 0;
20 
21     for (i = start; i < ARRAY_SIZE(subsys->ctrls) && cnt < num; i++) {
22         if (!subsys->ctrls[i]) {
23             sctrl = &list->sec[cnt];
24             sctrl->scid = cpu_to_le16(i);
25             subsys->ctrls[i] = SUBSYS_SLOT_RSVD;
26             cnt++;
27         }
28     }
29 
30     return cnt;
31 }
32 
33 static void nvme_subsys_unreserve_cntlids(NvmeCtrl *n)
34 {
35     NvmeSubsystem *subsys = n->subsys;
36     NvmeSecCtrlList *list = &n->sec_ctrl_list;
37     NvmeSecCtrlEntry *sctrl;
38     int i, cntlid;
39 
40     for (i = 0; i < n->params.sriov_max_vfs; i++) {
41         sctrl = &list->sec[i];
42         cntlid = le16_to_cpu(sctrl->scid);
43 
44         if (cntlid) {
45             assert(subsys->ctrls[cntlid] == SUBSYS_SLOT_RSVD);
46             subsys->ctrls[cntlid] = NULL;
47             sctrl->scid = 0;
48         }
49     }
50 }
51 
52 int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp)
53 {
54     NvmeSubsystem *subsys = n->subsys;
55     NvmeSecCtrlEntry *sctrl = nvme_sctrl(n);
56     int cntlid, nsid, num_rsvd, num_vfs = n->params.sriov_max_vfs;
57 
58     if (pci_is_vf(&n->parent_obj)) {
59         cntlid = le16_to_cpu(sctrl->scid);
60     } else {
61         for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) {
62             if (!subsys->ctrls[cntlid]) {
63                 break;
64             }
65         }
66 
67         if (cntlid == ARRAY_SIZE(subsys->ctrls)) {
68             error_setg(errp, "no more free controller id");
69             return -1;
70         }
71 
72         num_rsvd = nvme_subsys_reserve_cntlids(n, cntlid + 1, num_vfs);
73         if (num_rsvd != num_vfs) {
74             nvme_subsys_unreserve_cntlids(n);
75             error_setg(errp,
76                        "no more free controller ids for secondary controllers");
77             return -1;
78         }
79     }
80 
81     if (!subsys->serial) {
82         subsys->serial = g_strdup(n->params.serial);
83     } else if (strcmp(subsys->serial, n->params.serial)) {
84         error_setg(errp, "invalid controller serial");
85         return -1;
86     }
87 
88     subsys->ctrls[cntlid] = n;
89 
90     for (nsid = 1; nsid < ARRAY_SIZE(subsys->namespaces); nsid++) {
91         NvmeNamespace *ns = subsys->namespaces[nsid];
92         if (ns && ns->params.shared && !ns->params.detached) {
93             nvme_attach_ns(n, ns);
94         }
95     }
96 
97     return cntlid;
98 }
99 
100 void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n)
101 {
102     if (pci_is_vf(&n->parent_obj)) {
103         subsys->ctrls[n->cntlid] = SUBSYS_SLOT_RSVD;
104     } else {
105         subsys->ctrls[n->cntlid] = NULL;
106         nvme_subsys_unreserve_cntlids(n);
107     }
108 
109     n->cntlid = -1;
110 }
111 
112 static void nvme_subsys_setup(NvmeSubsystem *subsys)
113 {
114     const char *nqn = subsys->params.nqn ?
115         subsys->params.nqn : subsys->parent_obj.id;
116 
117     snprintf((char *)subsys->subnqn, sizeof(subsys->subnqn),
118              "nqn.2019-08.org.qemu:%s", nqn);
119 }
120 
121 static void nvme_subsys_realize(DeviceState *dev, Error **errp)
122 {
123     NvmeSubsystem *subsys = NVME_SUBSYS(dev);
124 
125     qbus_init(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id);
126 
127     nvme_subsys_setup(subsys);
128 }
129 
130 static Property nvme_subsystem_props[] = {
131     DEFINE_PROP_STRING("nqn", NvmeSubsystem, params.nqn),
132     DEFINE_PROP_END_OF_LIST(),
133 };
134 
135 static void nvme_subsys_class_init(ObjectClass *oc, void *data)
136 {
137     DeviceClass *dc = DEVICE_CLASS(oc);
138 
139     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
140 
141     dc->realize = nvme_subsys_realize;
142     dc->desc = "Virtual NVMe subsystem";
143     dc->hotpluggable = false;
144 
145     device_class_set_props(dc, nvme_subsystem_props);
146 }
147 
148 static const TypeInfo nvme_subsys_info = {
149     .name = TYPE_NVME_SUBSYS,
150     .parent = TYPE_DEVICE,
151     .class_init = nvme_subsys_class_init,
152     .instance_size = sizeof(NvmeSubsystem),
153 };
154 
155 static void nvme_subsys_register_types(void)
156 {
157     type_register_static(&nvme_subsys_info);
158 }
159 
160 type_init(nvme_subsys_register_types)
161