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