1 /*
2 * Copyright Intel Corp. 2020-2021
3 *
4 * ch_domain.c: Domain manager functions for Cloud-Hypervisor driver
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include "ch_domain.h"
24 #include "viralloc.h"
25 #include "virchrdev.h"
26 #include "virlog.h"
27 #include "virtime.h"
28
29 #define VIR_FROM_THIS VIR_FROM_CH
30
31 VIR_ENUM_IMPL(virCHDomainJob,
32 CH_JOB_LAST,
33 "none",
34 "query",
35 "destroy",
36 "modify",
37 );
38
39 VIR_LOG_INIT("ch.ch_domain");
40
41 static int
virCHDomainObjInitJob(virCHDomainObjPrivate * priv)42 virCHDomainObjInitJob(virCHDomainObjPrivate *priv)
43 {
44 memset(&priv->job, 0, sizeof(priv->job));
45
46 if (virCondInit(&priv->job.cond) < 0)
47 return -1;
48
49 return 0;
50 }
51
52 static void
virCHDomainObjResetJob(virCHDomainObjPrivate * priv)53 virCHDomainObjResetJob(virCHDomainObjPrivate *priv)
54 {
55 struct virCHDomainJobObj *job = &priv->job;
56
57 job->active = CH_JOB_NONE;
58 job->owner = 0;
59 }
60
61 static void
virCHDomainObjFreeJob(virCHDomainObjPrivate * priv)62 virCHDomainObjFreeJob(virCHDomainObjPrivate *priv)
63 {
64 ignore_value(virCondDestroy(&priv->job.cond));
65 }
66
67 /*
68 * obj must be locked before calling, virCHDriver must NOT be locked
69 *
70 * This must be called by anything that will change the VM state
71 * in any way
72 *
73 * Upon successful return, the object will have its ref count increased.
74 * Successful calls must be followed by EndJob eventually.
75 */
76 int
virCHDomainObjBeginJob(virDomainObj * obj,enum virCHDomainJob job)77 virCHDomainObjBeginJob(virDomainObj *obj, enum virCHDomainJob job)
78 {
79 virCHDomainObjPrivate *priv = obj->privateData;
80 unsigned long long now;
81 unsigned long long then;
82
83 if (virTimeMillisNow(&now) < 0)
84 return -1;
85 then = now + CH_JOB_WAIT_TIME;
86
87 while (priv->job.active) {
88 VIR_DEBUG("Wait normal job condition for starting job: %s",
89 virCHDomainJobTypeToString(job));
90 if (virCondWaitUntil(&priv->job.cond, &obj->parent.lock, then) < 0) {
91 VIR_WARN("Cannot start job (%s) for domain %s;"
92 " current job is (%s) owned by (%d)",
93 virCHDomainJobTypeToString(job),
94 obj->def->name,
95 virCHDomainJobTypeToString(priv->job.active),
96 priv->job.owner);
97
98 if (errno == ETIMEDOUT)
99 virReportError(VIR_ERR_OPERATION_TIMEOUT,
100 "%s", _("cannot acquire state change lock"));
101 else
102 virReportSystemError(errno,
103 "%s", _("cannot acquire job mutex"));
104 return -1;
105 }
106 }
107
108 virCHDomainObjResetJob(priv);
109
110 VIR_DEBUG("Starting job: %s", virCHDomainJobTypeToString(job));
111 priv->job.active = job;
112 priv->job.owner = virThreadSelfID();
113
114 return 0;
115 }
116
117 /*
118 * obj must be locked and have a reference before calling
119 *
120 * To be called after completing the work associated with the
121 * earlier virCHDomainBeginJob() call
122 */
123 void
virCHDomainObjEndJob(virDomainObj * obj)124 virCHDomainObjEndJob(virDomainObj *obj)
125 {
126 virCHDomainObjPrivate *priv = obj->privateData;
127 enum virCHDomainJob job = priv->job.active;
128
129 VIR_DEBUG("Stopping job: %s",
130 virCHDomainJobTypeToString(job));
131
132 virCHDomainObjResetJob(priv);
133 virCondSignal(&priv->job.cond);
134 }
135
136 static void *
virCHDomainObjPrivateAlloc(void * opaque G_GNUC_UNUSED)137 virCHDomainObjPrivateAlloc(void *opaque G_GNUC_UNUSED)
138 {
139 virCHDomainObjPrivate *priv;
140
141 priv = g_new0(virCHDomainObjPrivate, 1);
142
143 if (virCHDomainObjInitJob(priv) < 0) {
144 g_free(priv);
145 return NULL;
146 }
147
148 if (!(priv->chrdevs = virChrdevAlloc())) {
149 virCHDomainObjFreeJob(priv);
150 g_free(priv);
151 return NULL;
152 }
153
154 return priv;
155 }
156
157 static void
virCHDomainObjPrivateFree(void * data)158 virCHDomainObjPrivateFree(void *data)
159 {
160 virCHDomainObjPrivate *priv = data;
161
162 virChrdevFree(priv->chrdevs);
163 virCHDomainObjFreeJob(priv);
164 g_free(priv);
165 }
166
167 virDomainXMLPrivateDataCallbacks virCHDriverPrivateDataCallbacks = {
168 .alloc = virCHDomainObjPrivateAlloc,
169 .free = virCHDomainObjPrivateFree,
170 };
171
172 static int
virCHDomainDefPostParseBasic(virDomainDef * def,void * opaque G_GNUC_UNUSED)173 virCHDomainDefPostParseBasic(virDomainDef *def,
174 void *opaque G_GNUC_UNUSED)
175 {
176 /* check for emulator and create a default one if needed */
177 if (!def->emulator) {
178 if (!(def->emulator = g_find_program_in_path(CH_CMD))) {
179 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
180 _("No emulator found for cloud-hypervisor"));
181 return 1;
182 }
183 }
184
185 return 0;
186 }
187
188 static int
virCHDomainDefPostParse(virDomainDef * def,unsigned int parseFlags G_GNUC_UNUSED,void * opaque,void * parseOpaque G_GNUC_UNUSED)189 virCHDomainDefPostParse(virDomainDef *def,
190 unsigned int parseFlags G_GNUC_UNUSED,
191 void *opaque,
192 void *parseOpaque G_GNUC_UNUSED)
193 {
194 virCHDriver *driver = opaque;
195 g_autoptr(virCaps) caps = virCHDriverGetCapabilities(driver, false);
196 if (!caps)
197 return -1;
198 if (!virCapabilitiesDomainSupported(caps, def->os.type,
199 def->os.arch,
200 def->virtType))
201 return -1;
202
203 return 0;
204 }
205
206 static int
chValidateDomainDeviceDef(const virDomainDeviceDef * dev,const virDomainDef * def G_GNUC_UNUSED,void * opaque G_GNUC_UNUSED,void * parseOpaque G_GNUC_UNUSED)207 chValidateDomainDeviceDef(const virDomainDeviceDef *dev,
208 const virDomainDef *def G_GNUC_UNUSED,
209 void *opaque G_GNUC_UNUSED,
210 void *parseOpaque G_GNUC_UNUSED)
211 {
212 switch ((virDomainDeviceType)dev->type) {
213 case VIR_DOMAIN_DEVICE_DISK:
214 case VIR_DOMAIN_DEVICE_NET:
215 case VIR_DOMAIN_DEVICE_MEMORY:
216 case VIR_DOMAIN_DEVICE_VSOCK:
217 case VIR_DOMAIN_DEVICE_CONTROLLER:
218 case VIR_DOMAIN_DEVICE_CHR:
219 break;
220
221 case VIR_DOMAIN_DEVICE_LEASE:
222 case VIR_DOMAIN_DEVICE_FS:
223 case VIR_DOMAIN_DEVICE_INPUT:
224 case VIR_DOMAIN_DEVICE_SOUND:
225 case VIR_DOMAIN_DEVICE_VIDEO:
226 case VIR_DOMAIN_DEVICE_HOSTDEV:
227 case VIR_DOMAIN_DEVICE_WATCHDOG:
228 case VIR_DOMAIN_DEVICE_GRAPHICS:
229 case VIR_DOMAIN_DEVICE_HUB:
230 case VIR_DOMAIN_DEVICE_REDIRDEV:
231 case VIR_DOMAIN_DEVICE_SMARTCARD:
232 case VIR_DOMAIN_DEVICE_MEMBALLOON:
233 case VIR_DOMAIN_DEVICE_NVRAM:
234 case VIR_DOMAIN_DEVICE_RNG:
235 case VIR_DOMAIN_DEVICE_SHMEM:
236 case VIR_DOMAIN_DEVICE_TPM:
237 case VIR_DOMAIN_DEVICE_PANIC:
238 case VIR_DOMAIN_DEVICE_IOMMU:
239 case VIR_DOMAIN_DEVICE_AUDIO:
240 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
241 _("Cloud-Hypervisor doesn't support '%s' device"),
242 virDomainDeviceTypeToString(dev->type));
243 return -1;
244
245 case VIR_DOMAIN_DEVICE_NONE:
246 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
247 _("unexpected VIR_DOMAIN_DEVICE_NONE"));
248 return -1;
249
250 case VIR_DOMAIN_DEVICE_LAST:
251 default:
252 virReportEnumRangeError(virDomainDeviceType, dev->type);
253 return -1;
254 }
255
256 if ((def->nconsoles &&
257 def->consoles[0]->source->type == VIR_DOMAIN_CHR_TYPE_PTY)
258 && (def->nserials &&
259 def->serials[0]->source->type == VIR_DOMAIN_CHR_TYPE_PTY)) {
260 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
261 _("Only a single console or serial can be configured for this domain"));
262 return -1;
263 } else if (def->nconsoles > 1) {
264 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
265 _("Only a single console can be configured for this domain"));
266 return -1;
267 } else if (def->nserials > 1) {
268 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
269 _("Only a single serial can be configured for this domain"));
270 return -1;
271 }
272
273 if (def->nconsoles && def->consoles[0]->source->type != VIR_DOMAIN_CHR_TYPE_PTY) {
274 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
275 _("Console can only be enabled for a PTY"));
276 return -1;
277 }
278
279 if (def->nserials && def->serials[0]->source->type != VIR_DOMAIN_CHR_TYPE_PTY) {
280 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
281 _("Serial can only be enabled for a PTY"));
282 return -1;
283 }
284
285 return 0;
286 }
287
288 virDomainDefParserConfig virCHDriverDomainDefParserConfig = {
289 .domainPostParseBasicCallback = virCHDomainDefPostParseBasic,
290 .domainPostParseCallback = virCHDomainDefPostParse,
291 .deviceValidateCallback = chValidateDomainDeviceDef,
292 };
293