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