1 /*
2 * virscsivhost.c: helper APIs for managing scsi_host devices
3 *
4 * Copyright (C) 2016 IBM Corporation
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 #include <fcntl.h>
23
24 #include "virscsivhost.h"
25 #include "virlog.h"
26 #include "virerror.h"
27 #include "virfile.h"
28 #include "virstring.h"
29 #include "viralloc.h"
30
31 #define VIR_FROM_THIS VIR_FROM_NONE
32
33 VIR_LOG_INIT("util.scsihost");
34
35 #define SYSFS_VHOST_SCSI_DEVICES "/sys/kernel/config/target/vhost/"
36 #define VHOST_SCSI_DEVICE "/dev/vhost-scsi"
37
38 struct _virSCSIVHostDevice {
39 char *name; /* naa.<wwn> */
40 char *path;
41 char *used_by_drvname;
42 char *used_by_domname;
43 };
44
45 struct _virSCSIVHostDeviceList {
46 virObjectLockable parent;
47 size_t count;
48 virSCSIVHostDevice **devs;
49 };
50
51 static virClass *virSCSIVHostDeviceListClass;
52
53 static void
virSCSIVHostDeviceListDispose(void * obj)54 virSCSIVHostDeviceListDispose(void *obj)
55 {
56 virSCSIVHostDeviceList *list = obj;
57 size_t i;
58
59 for (i = 0; i < list->count; i++)
60 virSCSIVHostDeviceFree(list->devs[i]);
61
62 g_free(list->devs);
63 }
64
65
66 static int
virSCSIVHostOnceInit(void)67 virSCSIVHostOnceInit(void)
68 {
69 if (!VIR_CLASS_NEW(virSCSIVHostDeviceList, virClassForObjectLockable()))
70 return -1;
71
72 return 0;
73 }
74
75
76 VIR_ONCE_GLOBAL_INIT(virSCSIVHost);
77
78
79 int
virSCSIVHostOpenVhostSCSI(int * vhostfd)80 virSCSIVHostOpenVhostSCSI(int *vhostfd)
81 {
82 if (!virFileExists(VHOST_SCSI_DEVICE)) {
83 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
84 _("vhost-scsi device file '%s' cannot be found"),
85 VHOST_SCSI_DEVICE);
86 return -1;
87 }
88
89 *vhostfd = open(VHOST_SCSI_DEVICE, O_RDWR);
90
91 if (*vhostfd < 0) {
92 virReportSystemError(errno, _("Failed to open %s"), VHOST_SCSI_DEVICE);
93 return -1;
94 }
95
96 return 0;
97 }
98
99
100 void
virSCSIVHostDeviceListDel(virSCSIVHostDeviceList * list,virSCSIVHostDevice * dev)101 virSCSIVHostDeviceListDel(virSCSIVHostDeviceList *list,
102 virSCSIVHostDevice *dev)
103 {
104 virSCSIVHostDeviceFree(virSCSIVHostDeviceListSteal(list, dev));
105 }
106
107
108 static int
virSCSIVHostDeviceListFindIndex(virSCSIVHostDeviceList * list,virSCSIVHostDevice * dev)109 virSCSIVHostDeviceListFindIndex(virSCSIVHostDeviceList *list,
110 virSCSIVHostDevice *dev)
111 {
112 size_t i;
113
114 for (i = 0; i < list->count; i++) {
115 virSCSIVHostDevice *other = list->devs[i];
116 if (STREQ_NULLABLE(other->name, dev->name))
117 return i;
118 }
119 return -1;
120 }
121
122
123 virSCSIVHostDevice *
virSCSIVHostDeviceListGet(virSCSIVHostDeviceList * list,int idx)124 virSCSIVHostDeviceListGet(virSCSIVHostDeviceList *list, int idx)
125 {
126 if (idx >= list->count || idx < 0)
127 return NULL;
128
129 return list->devs[idx];
130 }
131
132
133 size_t
virSCSIVHostDeviceListCount(virSCSIVHostDeviceList * list)134 virSCSIVHostDeviceListCount(virSCSIVHostDeviceList *list)
135 {
136 return list->count;
137 }
138
139
140 virSCSIVHostDevice *
virSCSIVHostDeviceListSteal(virSCSIVHostDeviceList * list,virSCSIVHostDevice * dev)141 virSCSIVHostDeviceListSteal(virSCSIVHostDeviceList *list,
142 virSCSIVHostDevice *dev)
143 {
144 virSCSIVHostDevice *ret = NULL;
145 size_t i;
146
147 for (i = 0; i < list->count; i++) {
148 if (STREQ_NULLABLE(list->devs[i]->name, dev->name)) {
149 ret = list->devs[i];
150 VIR_DELETE_ELEMENT(list->devs, i, list->count);
151 break;
152 }
153 }
154
155 return ret;
156 }
157
158
159 virSCSIVHostDevice *
virSCSIVHostDeviceListFind(virSCSIVHostDeviceList * list,virSCSIVHostDevice * dev)160 virSCSIVHostDeviceListFind(virSCSIVHostDeviceList *list,
161 virSCSIVHostDevice *dev)
162 {
163 int idx;
164
165 if ((idx = virSCSIVHostDeviceListFindIndex(list, dev)) >= 0)
166 return list->devs[idx];
167 else
168 return NULL;
169 }
170
171
172 int
virSCSIVHostDeviceListAdd(virSCSIVHostDeviceList * list,virSCSIVHostDevice * dev)173 virSCSIVHostDeviceListAdd(virSCSIVHostDeviceList *list,
174 virSCSIVHostDevice *dev)
175 {
176 if (virSCSIVHostDeviceListFind(list, dev)) {
177 virReportError(VIR_ERR_INTERNAL_ERROR,
178 _("Device %s is already in use"), dev->name);
179 return -1;
180 }
181 VIR_APPEND_ELEMENT(list->devs, list->count, dev);
182
183 return 0;
184 }
185
186
187 virSCSIVHostDeviceList *
virSCSIVHostDeviceListNew(void)188 virSCSIVHostDeviceListNew(void)
189 {
190 if (virSCSIVHostInitialize() < 0)
191 return NULL;
192
193 return virObjectLockableNew(virSCSIVHostDeviceListClass);
194 }
195
196
197 int
virSCSIVHostDeviceSetUsedBy(virSCSIVHostDevice * dev,const char * drvname,const char * domname)198 virSCSIVHostDeviceSetUsedBy(virSCSIVHostDevice *dev,
199 const char *drvname,
200 const char *domname)
201 {
202 VIR_FREE(dev->used_by_drvname);
203 VIR_FREE(dev->used_by_domname);
204 dev->used_by_drvname = g_strdup(drvname);
205 dev->used_by_domname = g_strdup(domname);
206
207 return 0;
208 }
209
210
211 void
virSCSIVHostDeviceGetUsedBy(virSCSIVHostDevice * dev,const char ** drv_name,const char ** dom_name)212 virSCSIVHostDeviceGetUsedBy(virSCSIVHostDevice *dev,
213 const char **drv_name,
214 const char **dom_name)
215 {
216 *drv_name = dev->used_by_drvname;
217 *dom_name = dev->used_by_domname;
218 }
219
220
221 int
virSCSIVHostDeviceFileIterate(virSCSIVHostDevice * dev,virSCSIVHostDeviceFileActor actor,void * opaque)222 virSCSIVHostDeviceFileIterate(virSCSIVHostDevice *dev,
223 virSCSIVHostDeviceFileActor actor,
224 void *opaque)
225 {
226 return (actor)(dev, dev->path, opaque);
227 }
228
229
230 const char *
virSCSIVHostDeviceGetName(virSCSIVHostDevice * dev)231 virSCSIVHostDeviceGetName(virSCSIVHostDevice *dev)
232 {
233 return dev->name;
234 }
235
236
237 const char *
virSCSIVHostDeviceGetPath(virSCSIVHostDevice * dev)238 virSCSIVHostDeviceGetPath(virSCSIVHostDevice *dev)
239 {
240 return dev->path;
241 }
242
243
244 virSCSIVHostDevice *
virSCSIVHostDeviceNew(const char * name)245 virSCSIVHostDeviceNew(const char *name)
246 {
247 g_autoptr(virSCSIVHostDevice) dev = NULL;
248
249 dev = g_new0(virSCSIVHostDevice, 1);
250
251 dev->name = g_strdup(name);
252
253 dev->path = g_strdup_printf("%s/%s", SYSFS_VHOST_SCSI_DEVICES, name);
254
255 VIR_DEBUG("%s: initialized", dev->name);
256
257 return g_steal_pointer(&dev);
258 }
259
260
261 void
virSCSIVHostDeviceFree(virSCSIVHostDevice * dev)262 virSCSIVHostDeviceFree(virSCSIVHostDevice *dev)
263 {
264 if (!dev)
265 return;
266 VIR_DEBUG("%s: freeing", dev->name);
267 g_free(dev->name);
268 g_free(dev->path);
269 g_free(dev->used_by_drvname);
270 g_free(dev->used_by_domname);
271 g_free(dev);
272 }
273