1 /*
2 * storage_backend_scsi.c: storage backend for SCSI handling
3 *
4 * Copyright (C) 2007-2008, 2013-2014 Red Hat, Inc.
5 * Copyright (C) 2007-2008 Daniel P. Berrange
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23
24 #include <unistd.h>
25 #include <fcntl.h>
26
27 #include "virerror.h"
28 #include "storage_backend_scsi.h"
29 #include "viralloc.h"
30 #include "virlog.h"
31 #include "virfile.h"
32 #include "vircommand.h"
33 #include "virstring.h"
34 #include "storage_util.h"
35 #include "node_device_conf.h"
36 #include "node_device_util.h"
37 #include "driver.h"
38
39 #define VIR_FROM_THIS VIR_FROM_STORAGE
40
41 VIR_LOG_INIT("storage.storage_backend_scsi");
42
43 #define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host"
44 #define LINUX_SYSFS_SCSI_HOST_SCAN_STRING "- - -"
45
46 typedef struct _virStoragePoolFCRefreshInfo virStoragePoolFCRefreshInfo;
47 struct _virStoragePoolFCRefreshInfo {
48 char *fchost_name;
49 unsigned char pool_uuid[VIR_UUID_BUFLEN];
50 };
51
52
53 static int
virStorageBackendSCSITriggerRescan(uint32_t host)54 virStorageBackendSCSITriggerRescan(uint32_t host)
55 {
56 VIR_AUTOCLOSE fd = -1;
57 g_autofree char *path = NULL;
58
59 VIR_DEBUG("Triggering rescan of host %d", host);
60
61 path = g_strdup_printf("%s/host%u/scan", LINUX_SYSFS_SCSI_HOST_PREFIX, host);
62
63 VIR_DEBUG("Scan trigger path is '%s'", path);
64
65 fd = open(path, O_WRONLY);
66
67 if (fd < 0) {
68 virReportSystemError(errno,
69 _("Could not open '%s' to trigger host scan"),
70 path);
71 return -1;
72 }
73
74 if (safewrite(fd,
75 LINUX_SYSFS_SCSI_HOST_SCAN_STRING,
76 sizeof(LINUX_SYSFS_SCSI_HOST_SCAN_STRING)) < 0) {
77 virReportSystemError(errno,
78 _("Write to '%s' to trigger host scan failed"),
79 path);
80 return -1;
81 }
82
83 VIR_DEBUG("Rescan of host %d complete", host);
84 return 0;
85 }
86
87 /**
88 * Frees opaque data
89 *
90 * @opaque Data to be freed
91 */
92 static void
virStoragePoolFCRefreshDataFree(void * opaque)93 virStoragePoolFCRefreshDataFree(void *opaque)
94 {
95 virStoragePoolFCRefreshInfo *cbdata = opaque;
96
97 g_free(cbdata->fchost_name);
98 g_free(cbdata);
99 }
100
101 /**
102 * Thread to handle the pool refresh after a VPORT_CREATE is done. In this
103 * case the 'udevEventHandleCallback' will be executed asynchronously as a
104 * result of the node device driver callback routine to handle when udev
105 * notices some sort of device change (such as adding a new device). It takes
106 * some amount of time (usually a few seconds) for udev to go through the
107 * process of setting up the new device. Unfortunately, there is nothing
108 * that says "when" it's done. The immediate virStorageBackendSCSIRefreshPool
109 * done after virStorageBackendSCSIStartPool (and createVport) occurs too
110 * quickly to find any devices.
111 *
112 * So this thread is designed to wait a few seconds (5), then make the query
113 * to find the LUs for the pool. If none yet exist, we'll try once more
114 * to find the LUs before giving up.
115 *
116 * Attempting to find devices prior to allowing udev to settle down may result
117 * in finding devices that then get deleted.
118 *
119 * @opaque Pool's Refresh Info containing name and pool object pointer
120 */
121 static void
virStoragePoolFCRefreshThread(void * opaque)122 virStoragePoolFCRefreshThread(void *opaque)
123 {
124 virStoragePoolFCRefreshInfo *cbdata = opaque;
125 const char *fchost_name = cbdata->fchost_name;
126 const unsigned char *pool_uuid = cbdata->pool_uuid;
127 virStoragePoolObj *pool = NULL;
128 virStoragePoolDef *def;
129 unsigned int host;
130 int found = 0;
131 int tries = 2;
132
133 do {
134 sleep(5); /* Give it time */
135
136 /* Let's see if the pool still exists - */
137 if (!(pool = virStoragePoolObjFindPoolByUUID(pool_uuid)))
138 break;
139 def = virStoragePoolObjGetDef(pool);
140
141 /* Return with pool lock, if active, we can get the host number,
142 * successfully, rescan, and find LUN's, then we are happy
143 */
144 VIR_DEBUG("Attempt FC Refresh for pool='%s' name='%s' tries='%d'",
145 def->name, fchost_name, tries);
146
147 def->allocation = def->capacity = def->available = 0;
148
149 if (virStoragePoolObjIsActive(pool) &&
150 virSCSIHostGetNumber(fchost_name, &host) == 0 &&
151 virStorageBackendSCSITriggerRescan(host) == 0) {
152 virStoragePoolObjClearVols(pool);
153 found = virStorageBackendSCSIFindLUs(pool, host);
154 }
155 virStoragePoolObjEndAPI(&pool);
156 } while (!found && --tries);
157
158 if (pool && !found)
159 VIR_DEBUG("FC Refresh Thread failed to find LU's");
160
161 virStoragePoolFCRefreshDataFree(cbdata);
162 }
163
164 static char *
getAdapterName(virStorageAdapter * adapter)165 getAdapterName(virStorageAdapter *adapter)
166 {
167 char *name = NULL;
168
169 if (adapter->type == VIR_STORAGE_ADAPTER_TYPE_SCSI_HOST) {
170 virStorageAdapterSCSIHost *scsi_host = &adapter->data.scsi_host;
171
172 if (scsi_host->has_parent) {
173 virPCIDeviceAddress *addr = &scsi_host->parentaddr;
174 unsigned int unique_id = scsi_host->unique_id;
175
176 if (!(name = virSCSIHostGetNameByParentaddr(addr->domain,
177 addr->bus,
178 addr->slot,
179 addr->function,
180 unique_id)))
181 return NULL;
182 } else {
183 name = g_strdup(scsi_host->name);
184 }
185 } else if (adapter->type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST) {
186 virStorageAdapterFCHost *fchost = &adapter->data.fchost;
187
188 if (!(name = virVHBAGetHostByWWN(NULL, fchost->wwnn, fchost->wwpn))) {
189 virReportError(VIR_ERR_XML_ERROR,
190 _("Failed to find SCSI host with wwnn='%s', "
191 "wwpn='%s'"), fchost->wwnn, fchost->wwpn);
192 }
193 }
194
195 return name;
196 }
197
198
199 /**
200 * @name: Name from a wwnn/wwpn lookup
201 *
202 * Validate that the @name fetched from the wwnn/wwpn is a vHBA
203 * and not an HBA as that should be a configuration error. It's only
204 * possible to use an existing wwnn/wwpn of a vHBA because that's
205 * what someone would have created using the node device create via XML
206 * functionality. Using the HBA "just because" it has a wwnn/wwpn and
207 * the characteristics of a vHBA is just not valid
208 *
209 * Returns true if the @name is OK, false on error
210 */
211 static bool
checkName(const char * name)212 checkName(const char *name)
213 {
214 unsigned int host_num;
215
216 if (virSCSIHostGetNumber(name, &host_num) &&
217 virVHBAIsVportCapable(NULL, host_num))
218 return true;
219
220 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
221 _("the wwnn/wwpn for '%s' are assigned to an HBA"), name);
222 return false;
223 }
224
225
226 /*
227 * Using the host# name found via wwnn/wwpn lookup in the fc_host
228 * sysfs tree to get the parent 'scsi_host#' to ensure it matches.
229 */
230 static bool
checkParent(const char * name,const char * parent_name)231 checkParent(const char *name,
232 const char *parent_name)
233 {
234 unsigned int host_num;
235 bool retval = false;
236 virConnectPtr conn = NULL;
237 g_autofree char *scsi_host_name = NULL;
238 g_autofree char *vhba_parent = NULL;
239
240 VIR_DEBUG("name=%s, parent_name=%s", name, parent_name);
241
242 conn = virGetConnectNodeDev();
243 if (!conn)
244 goto cleanup;
245
246 if (virSCSIHostGetNumber(parent_name, &host_num) < 0) {
247 virReportError(VIR_ERR_XML_ERROR,
248 _("parent '%s' is not properly formatted"),
249 parent_name);
250 goto cleanup;
251 }
252
253 if (!virVHBAPathExists(NULL, host_num)) {
254 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
255 _("parent '%s' is not an fc_host for the wwnn/wwpn"),
256 parent_name);
257 goto cleanup;
258 }
259
260 scsi_host_name = g_strdup_printf("scsi_%s", name);
261
262 if (!(vhba_parent = virNodeDeviceGetParentName(conn, scsi_host_name)))
263 goto cleanup;
264
265 if (STRNEQ(parent_name, vhba_parent)) {
266 virReportError(VIR_ERR_XML_ERROR,
267 _("Parent attribute '%s' does not match parent '%s' "
268 "determined for the '%s' wwnn/wwpn lookup."),
269 parent_name, vhba_parent, name);
270 goto cleanup;
271 }
272
273 retval = true;
274
275 cleanup:
276 virObjectUnref(conn);
277 return retval;
278 }
279
280
281 static int
createVport(virStoragePoolDef * def,const char * configFile,virStorageAdapterFCHost * fchost)282 createVport(virStoragePoolDef *def,
283 const char *configFile,
284 virStorageAdapterFCHost *fchost)
285 {
286 virStoragePoolFCRefreshInfo *cbdata = NULL;
287 virThread thread;
288 g_autofree char *name = NULL;
289
290 VIR_DEBUG("configFile='%s' parent='%s', wwnn='%s' wwpn='%s'",
291 NULLSTR(configFile), NULLSTR(fchost->parent),
292 fchost->wwnn, fchost->wwpn);
293
294 /* If we find an existing HBA/vHBA within the fc_host sysfs
295 * using the wwnn/wwpn, then a nodedev is already created for
296 * this pool and we don't have to create the vHBA
297 */
298 if ((name = virVHBAGetHostByWWN(NULL, fchost->wwnn, fchost->wwpn))) {
299 if (!(checkName(name)))
300 return -1;
301
302 /* If a parent was provided, let's make sure the 'name' we've
303 * retrieved has the same parent. If not this will cause failure. */
304 if (!fchost->parent || checkParent(name, fchost->parent))
305 return 0;
306
307 return -1;
308 }
309
310 /* Since we're creating the vHBA, then we need to manage removing it
311 * as well. Since we need this setting to "live" through a libvirtd
312 * restart, we need to save the persistent configuration. So if not
313 * already defined as YES, then force the issue.
314 */
315 if (fchost->managed != VIR_TRISTATE_BOOL_YES) {
316 fchost->managed = VIR_TRISTATE_BOOL_YES;
317 if (configFile) {
318 if (virStoragePoolSaveConfig(configFile, def) < 0)
319 return -1;
320 }
321 }
322
323 if (!(name = virNodeDeviceCreateVport(fchost)))
324 return -1;
325
326 /* Creating our own VPORT didn't leave enough time to find any LUN's,
327 * so, let's create a thread whose job it is to call the FindLU's with
328 * retry logic set to true. If the thread isn't created, then no big
329 * deal since it's still possible to refresh the pool later.
330 */
331 cbdata = g_new0(virStoragePoolFCRefreshInfo, 1);
332 memcpy(cbdata->pool_uuid, def->uuid, VIR_UUID_BUFLEN);
333 cbdata->fchost_name = g_steal_pointer(&name);
334
335 if (virThreadCreateFull(&thread, false, virStoragePoolFCRefreshThread,
336 "scsi-refresh", false, cbdata) < 0) {
337 /* Oh well - at least someone can still refresh afterwards */
338 VIR_DEBUG("Failed to create FC Pool Refresh Thread");
339 virStoragePoolFCRefreshDataFree(cbdata);
340 }
341
342 return 0;
343 }
344
345
346 static int
virStorageBackendSCSICheckPool(virStoragePoolObj * pool,bool * isActive)347 virStorageBackendSCSICheckPool(virStoragePoolObj *pool,
348 bool *isActive)
349 {
350 virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
351 unsigned int host;
352 g_autofree char *path = NULL;
353 g_autofree char *name = NULL;
354
355 *isActive = false;
356
357 if (!(name = getAdapterName(&def->source.adapter))) {
358 /* It's normal for the pool with "fc_host" type source
359 * adapter fails to get the adapter name, since the vHBA
360 * the adapter based on might be not created yet.
361 */
362 if (def->source.adapter.type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST) {
363 virResetLastError();
364 return 0;
365 } else {
366 return -1;
367 }
368 }
369
370 if (virSCSIHostGetNumber(name, &host) < 0)
371 return -1;
372
373 path = g_strdup_printf("%s/host%d", LINUX_SYSFS_SCSI_HOST_PREFIX, host);
374
375 *isActive = virFileExists(path);
376
377 return 0;
378 }
379
380 static int
virStorageBackendSCSIRefreshPool(virStoragePoolObj * pool)381 virStorageBackendSCSIRefreshPool(virStoragePoolObj *pool)
382 {
383 virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
384 unsigned int host;
385 g_autofree char *name = NULL;
386
387 def->allocation = def->capacity = def->available = 0;
388
389 if (!(name = getAdapterName(&def->source.adapter)))
390 return -1;
391
392 if (virSCSIHostGetNumber(name, &host) < 0)
393 return -1;
394
395 VIR_DEBUG("Scanning host%u", host);
396
397 if (virStorageBackendSCSITriggerRescan(host) < 0)
398 return -1;
399
400 if (virStorageBackendSCSIFindLUs(pool, host) < 0)
401 return -1;
402
403 return 0;
404 }
405
406
407 static int
virStorageBackendSCSIStartPool(virStoragePoolObj * pool)408 virStorageBackendSCSIStartPool(virStoragePoolObj *pool)
409 {
410 virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
411 const char *configFile = virStoragePoolObjGetConfigFile(pool);
412
413 if (def->source.adapter.type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST)
414 return createVport(def, configFile,
415 &def->source.adapter.data.fchost);
416
417 return 0;
418 }
419
420
421 static int
virStorageBackendSCSIStopPool(virStoragePoolObj * pool)422 virStorageBackendSCSIStopPool(virStoragePoolObj *pool)
423 {
424 virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
425
426 if (def->source.adapter.type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST) {
427 virConnectPtr conn;
428 int ret;
429 conn = virGetConnectNodeDev();
430 if (!conn)
431 return -1;
432
433 ret = virNodeDeviceDeleteVport(conn,
434 &def->source.adapter.data.fchost);
435 virObjectUnref(conn);
436 return ret;
437 }
438
439 return 0;
440 }
441
442 virStorageBackend virStorageBackendSCSI = {
443 .type = VIR_STORAGE_POOL_SCSI,
444
445 .checkPool = virStorageBackendSCSICheckPool,
446 .refreshPool = virStorageBackendSCSIRefreshPool,
447 .startPool = virStorageBackendSCSIStartPool,
448 .stopPool = virStorageBackendSCSIStopPool,
449 .uploadVol = virStorageBackendVolUploadLocal,
450 .downloadVol = virStorageBackendVolDownloadLocal,
451 .wipeVol = virStorageBackendVolWipeLocal,
452 };
453
454
455 int
virStorageBackendSCSIRegister(void)456 virStorageBackendSCSIRegister(void)
457 {
458 return virStorageBackendRegister(&virStorageBackendSCSI);
459 }
460