storage_backend_scsi.c 28.6 KB
Newer Older
1 2 3
/*
 * storage_backend_scsi.c: storage backend for SCSI handling
 *
C
Cédric Bosdonnat 已提交
4
 * Copyright (C) 2007-2008, 2013-2014 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17
 * Copyright (C) 2007-2008 Daniel P. Berrange
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library.  If not, see
O
Osier Yang 已提交
19
 * <http://www.gnu.org/licenses/>.
20 21 22 23 24 25 26 27 28 29 30
 *
 * Author: Daniel P. Berrange <berrange redhat com>
 */

#include <config.h>

#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <fcntl.h>

31
#include "virerror.h"
32
#include "storage_backend_scsi.h"
33
#include "viralloc.h"
34
#include "virlog.h"
E
Eric Blake 已提交
35
#include "virfile.h"
36
#include "vircommand.h"
37
#include "virstring.h"
38 39 40

#define VIR_FROM_THIS VIR_FROM_STORAGE

41 42
VIR_LOG_INIT("storage.storage_backend_scsi");

43 44 45
typedef struct _virStoragePoolFCRefreshInfo virStoragePoolFCRefreshInfo;
typedef virStoragePoolFCRefreshInfo *virStoragePoolFCRefreshInfoPtr;
struct _virStoragePoolFCRefreshInfo {
46
    char *fchost_name;
47
    unsigned char pool_uuid[VIR_UUID_BUFLEN];
48 49
};

50 51 52 53 54
/* Function to check if the type file in the given sysfs_path is a
 * Direct-Access device (i.e. type 0).  Return -1 on failure, type of
 * the device otherwise.
 */
static int
55
getDeviceType(uint32_t host,
56 57 58 59 60 61 62 63 64 65 66 67
              uint32_t bus,
              uint32_t target,
              uint32_t lun,
              int *type)
{
    char *type_path = NULL;
    char typestr[3];
    char *gottype, *p;
    FILE *typefile;
    int retval = 0;

    if (virAsprintf(&type_path, "/sys/bus/scsi/devices/%u:%u:%u:%u/type",
68
                    host, bus, target, lun) < 0)
69 70 71 72
        goto out;

    typefile = fopen(type_path, "r");
    if (typefile == NULL) {
73
        virReportSystemError(errno,
74 75 76 77 78 79 80 81
                             _("Could not find typefile '%s'"),
                             type_path);
        /* there was no type file; that doesn't seem right */
        retval = -1;
        goto out;
    }

    gottype = fgets(typestr, 3, typefile);
82
    VIR_FORCE_FCLOSE(typefile);
83 84

    if (gottype == NULL) {
85
        virReportSystemError(errno,
86 87 88 89 90 91 92 93 94 95 96
                             _("Could not read typefile '%s'"),
                             type_path);
        /* we couldn't read the type file; have to give up */
        retval = -1;
        goto out;
    }

    /* we don't actually care about p, but if you pass NULL and the last
     * character is not \0, virStrToLong_i complains
     */
    if (virStrToLong_i(typestr, &p, 10, type) < 0) {
97 98 99
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Device type '%s' is not an integer"),
                       typestr);
100 101 102 103 104
        /* Hm, type wasn't an integer; seems strange */
        retval = -1;
        goto out;
    }

105
    VIR_DEBUG("Device type is %d", *type);
106

107
 out:
108 109 110 111
    VIR_FREE(type_path);
    return retval;
}

112 113 114 115
static char *
virStorageBackendSCSISerial(const char *dev)
{
    char *serial = NULL;
116
#ifdef WITH_UDEV
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
    virCommandPtr cmd = virCommandNewArgList(
        "/lib/udev/scsi_id",
        "--replace-whitespace",
        "--whitelisted",
        "--device", dev,
        NULL
        );

    /* Run the program and capture its output */
    virCommandSetOutputBuffer(cmd, &serial);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
#endif

    if (serial && STRNEQ(serial, "")) {
        char *nl = strchr(serial, '\n');
        if (nl)
            *nl = '\0';
    } else {
        VIR_FREE(serial);
137
        ignore_value(VIR_STRDUP(serial, dev));
138 139
    }

140
#ifdef WITH_UDEV
141
 cleanup:
142 143 144 145 146 147 148
    virCommandFree(cmd);
#endif

    return serial;
}


149 150 151 152 153 154 155 156 157 158
/*
 * Attempt to create a new LUN
 *
 * Returns:
 *
 *  0  => Success
 *  -1 => Failure due to some sort of OOM or other fatal issue found when
 *        attempting to get/update information about a found volume
 *  -2 => Failure to find a stable path, not fatal, caller can try another
 */
159
static int
160
virStorageBackendSCSINewLun(virStoragePoolObjPtr pool,
161
                            uint32_t host ATTRIBUTE_UNUSED,
162 163 164 165 166
                            uint32_t bus,
                            uint32_t target,
                            uint32_t lun,
                            const char *dev)
{
167
    virStorageVolDefPtr vol = NULL;
168
    char *devpath = NULL;
169
    int retval = -1;
170

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    /* Check if the pool is using a stable target path. The call to
     * virStorageBackendStablePath will fail if the pool target path
     * isn't stable and just return the strdup'd 'devpath' anyway.
     * This would be indistinguishable to failing to find the stable
     * path to the device if the virDirRead loop to search the
     * target pool path for our devpath had failed.
     */
    if (!virStorageBackendPoolPathIsStable(pool->def->target.path) &&
        !(STREQ(pool->def->target.path, "/dev") ||
          STREQ(pool->def->target.path, "/dev/"))) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("unable to use target path '%s' for dev '%s'"),
                       NULLSTR(pool->def->target.path), dev);
        goto cleanup;
    }

187 188
    if (VIR_ALLOC(vol) < 0)
        goto cleanup;
189 190 191

    vol->type = VIR_STORAGE_VOL_BLOCK;

192 193 194 195 196
    /* 'host' is dynamically allocated by the kernel, first come,
     * first served, per HBA. As such it isn't suitable for use
     * in the volume name. We only need uniqueness per-pool, so
     * just leave 'host' out
     */
197 198
    if (virAsprintf(&(vol->name), "unit:%u:%u:%u", bus, target, lun) < 0)
        goto cleanup;
199

200 201
    if (virAsprintf(&devpath, "/dev/%s", dev) < 0)
        goto cleanup;
202

203
    VIR_DEBUG("Trying to create volume for '%s'", devpath);
204 205 206 207 208 209 210

    /* Now figure out the stable path
     *
     * XXX this method is O(N) because it scans the pool target
     * dir every time its run. Should figure out a more efficient
     * way of doing this...
     */
211
    if ((vol->target.path = virStorageBackendStablePath(pool,
212
                                                        devpath,
213 214
                                                        true)) == NULL)
        goto cleanup;
215

216
    if (STREQ(devpath, vol->target.path) &&
217 218 219
        !(STREQ(pool->def->target.path, "/dev") ||
          STREQ(pool->def->target.path, "/dev/"))) {

220
        VIR_DEBUG("No stable path found for '%s' in '%s'",
221 222
                  devpath, pool->def->target.path);

223
        retval = -2;
224
        goto cleanup;
225 226
    }

227
    if (virStorageBackendUpdateVolInfo(vol, true,
228 229
                                       VIR_STORAGE_VOL_OPEN_DEFAULT) < 0)
        goto cleanup;
230

231 232
    if (!(vol->key = virStorageBackendSCSISerial(vol->target.path)))
        goto cleanup;
233

234 235
    pool->def->capacity += vol->target.capacity;
    pool->def->allocation += vol->target.allocation;
236

237 238
    if (VIR_APPEND_ELEMENT(pool->volumes.objs, pool->volumes.count, vol) < 0)
        goto cleanup;
239

240 241
    vol = NULL;
    retval = 0;
242

243
 cleanup:
244 245 246 247 248 249 250
    virStorageVolDefFree(vol);
    VIR_FREE(devpath);
    return retval;
}


static int
251
getNewStyleBlockDevice(const char *lun_path,
252 253 254 255 256 257
                       const char *block_name ATTRIBUTE_UNUSED,
                       char **block_device)
{
    char *block_path = NULL;
    DIR *block_dir = NULL;
    struct dirent *block_dirent = NULL;
258
    int retval = -1;
E
Eric Blake 已提交
259
    int direrr;
260

261
    if (virAsprintf(&block_path, "%s/block", lun_path) < 0)
262
        goto cleanup;
263

264
    VIR_DEBUG("Looking for block device in '%s'", block_path);
265

266
    if (!(block_dir = opendir(block_path))) {
267
        virReportSystemError(errno,
268 269
                             _("Failed to opendir sysfs path '%s'"),
                             block_path);
270
        goto cleanup;
271 272
    }

E
Eric Blake 已提交
273
    while ((direrr = virDirRead(block_dir, &block_dirent, block_path)) > 0) {
274
        if (STREQLEN(block_dirent->d_name, ".", 1))
275 276
            continue;

277 278
        if (VIR_STRDUP(*block_device, block_dirent->d_name) < 0)
            goto cleanup;
279

280
        VIR_DEBUG("Block device is '%s'", *block_device);
281 282 283

        break;
    }
284

E
Eric Blake 已提交
285
    if (direrr < 0)
286
        goto cleanup;
287

288
    retval = 0;
289

290 291 292
 cleanup:
    if (block_dir)
        closedir(block_dir);
293 294 295 296 297 298
    VIR_FREE(block_path);
    return retval;
}


static int
299
getOldStyleBlockDevice(const char *lun_path ATTRIBUTE_UNUSED,
300 301 302 303
                       const char *block_name,
                       char **block_device)
{
    char *blockp = NULL;
304
    int retval = -1;
305 306

    /* old-style; just parse out the sd */
307
    if (!(blockp = strrchr(block_name, ':'))) {
308
        /* Hm, wasn't what we were expecting; have to give up */
309 310 311
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to parse block name %s"),
                       block_name);
312
        goto cleanup;
313 314
    } else {
        blockp++;
315 316
        if (VIR_STRDUP(*block_device, blockp) < 0)
            goto cleanup;
317

318
        VIR_DEBUG("Block device is '%s'", *block_device);
319 320
    }

321 322
    retval = 0;
 cleanup:
323 324 325 326
    return retval;
}


327 328 329 330 331 332 333 334 335
/*
 * Search a device entry for the "block" file
 *
 * Returns
 *
 *   0 => Found it
 *   -1 => Fatal error
 *   -2 => Didn't find in lun_path directory
 */
336
static int
337
getBlockDevice(uint32_t host,
338 339 340 341 342 343 344 345
               uint32_t bus,
               uint32_t target,
               uint32_t lun,
               char **block_device)
{
    char *lun_path = NULL;
    DIR *lun_dir = NULL;
    struct dirent *lun_dirent = NULL;
346
    int retval = -1;
E
Eric Blake 已提交
347
    int direrr;
348

349 350
    *block_device = NULL;

351
    if (virAsprintf(&lun_path, "/sys/bus/scsi/devices/%u:%u:%u:%u",
352
                    host, bus, target, lun) < 0)
353
        goto cleanup;
354

355
    if (!(lun_dir = opendir(lun_path))) {
356
        virReportSystemError(errno,
357 358
                             _("Failed to opendir sysfs path '%s'"),
                             lun_path);
359
        goto cleanup;
360 361
    }

E
Eric Blake 已提交
362
    while ((direrr = virDirRead(lun_dir, &lun_dirent, lun_path)) > 0) {
363 364
        if (STREQLEN(lun_dirent->d_name, "block", 5)) {
            if (strlen(lun_dirent->d_name) == 5) {
365 366 367 368
                if (getNewStyleBlockDevice(lun_path,
                                           lun_dirent->d_name,
                                           block_device) < 0)
                    goto cleanup;
369
            } else {
370 371 372 373
                if (getOldStyleBlockDevice(lun_path,
                                           lun_dirent->d_name,
                                           block_device) < 0)
                    goto cleanup;
374 375 376 377
            }
            break;
        }
    }
378 379 380 381 382 383
    if (direrr < 0)
        goto cleanup;
    if (!*block_device) {
        retval = -2;
        goto cleanup;
    }
384

385
    retval = 0;
386

387 388 389
 cleanup:
    if (lun_dir)
        closedir(lun_dir);
390 391 392 393 394
    VIR_FREE(lun_path);
    return retval;
}


395 396 397 398 399 400 401 402 403
/*
 * Process a Logical Unit entry from the scsi host device directory
 *
 * Returns:
 *
 *  0  => Found a valid entry
 *  -1 => Some sort of fatal error
 *  -2 => non-fatal error or a non-disk entry
 */
404
static int
405
processLU(virStoragePoolObjPtr pool,
406 407 408 409 410
          uint32_t host,
          uint32_t bus,
          uint32_t target,
          uint32_t lun)
{
411
    int retval = -1;
412 413 414
    int device_type;
    char *block_device = NULL;

415
    VIR_DEBUG("Processing LU %u:%u:%u:%u",
416 417
              host, bus, target, lun);

418
    if (getDeviceType(host, bus, target, lun, &device_type) < 0) {
419 420 421
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"),
                       host, bus, target, lun);
422
        return -1;
423 424 425 426 427 428 429
    }

    /* We don't create volumes for devices other than disk and cdrom
     * devices, but finding a device that isn't one of those types
     * isn't an error, either. */
    if (!(device_type == VIR_STORAGE_DEVICE_TYPE_DISK ||
          device_type == VIR_STORAGE_DEVICE_TYPE_ROM))
430
        return -2;
431

432
    VIR_DEBUG("%u:%u:%u:%u is a Direct-Access LUN",
433 434
              host, bus, target, lun);

435
    if ((retval = getBlockDevice(host, bus, target, lun, &block_device)) < 0) {
436
        VIR_DEBUG("Failed to find block device for this LUN");
437
        return retval;
438
    }
439

440 441 442
    retval = virStorageBackendSCSINewLun(pool, host, bus, target, lun,
                                         block_device);
    if (retval < 0) {
443
        VIR_DEBUG("Failed to create new storage volume for %u:%u:%u:%u",
444
                  host, bus, target, lun);
445
        goto cleanup;
446 447
    }

448
    VIR_DEBUG("Created new storage volume for %u:%u:%u:%u successfully",
449 450
              host, bus, target, lun);

451
 cleanup:
452
    VIR_FREE(block_device);
453 454 455 456
    return retval;
}


457 458 459
int
virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
                              uint32_t scanhost)
460 461 462
{
    int retval = 0;
    uint32_t bus, target, lun;
463
    const char *device_path = "/sys/bus/scsi/devices";
464 465 466
    DIR *devicedir = NULL;
    struct dirent *lun_dirent = NULL;
    char devicepattern[64];
467
    int found = 0;
468

469
    VIR_DEBUG("Discovering LUs on host %u", scanhost);
470

471
    virFileWaitForDevices();
472 473 474 475

    devicedir = opendir(device_path);

    if (devicedir == NULL) {
476
        virReportSystemError(errno,
477
                             _("Failed to opendir path '%s'"), device_path);
478
        return -1;
479 480 481 482
    }

    snprintf(devicepattern, sizeof(devicepattern), "%u:%%u:%%u:%%u\n", scanhost);

E
Eric Blake 已提交
483
    while ((retval = virDirRead(devicedir, &lun_dirent, device_path)) > 0) {
484 485
        int rc;

486 487 488 489 490
        if (sscanf(lun_dirent->d_name, devicepattern,
                   &bus, &target, &lun) != 3) {
            continue;
        }

491
        VIR_DEBUG("Found possible LU '%s'", lun_dirent->d_name);
492

493 494 495 496 497 498
        rc = processLU(pool, scanhost, bus, target, lun);
        if (rc == -1) {
            retval = -1;
            break;
        }
        if (rc == 0)
499
            found++;
500 501 502 503
    }

    closedir(devicedir);

504 505
    if (retval < 0)
        return -1;
506

507 508 509
    VIR_DEBUG("Found %d LUs for pool %s", found, pool->def->name);

    return found;
510 511
}

512

513
static int
514
virStorageBackendSCSITriggerRescan(uint32_t host)
515 516 517 518 519
{
    int fd = -1;
    int retval = 0;
    char *path;

520
    VIR_DEBUG("Triggering rescan of host %d", host);
521

522 523
    if (virAsprintf(&path, "%s/host%u/scan",
                    LINUX_SYSFS_SCSI_HOST_PREFIX, host) < 0) {
524 525 526 527
        retval = -1;
        goto out;
    }

528
    VIR_DEBUG("Scan trigger path is '%s'", path);
529 530 531 532

    fd = open(path, O_WRONLY);

    if (fd < 0) {
533
        virReportSystemError(errno,
534 535 536 537 538 539 540 541 542
                             _("Could not open '%s' to trigger host scan"),
                             path);
        retval = -1;
        goto free_path;
    }

    if (safewrite(fd,
                  LINUX_SYSFS_SCSI_HOST_SCAN_STRING,
                  sizeof(LINUX_SYSFS_SCSI_HOST_SCAN_STRING)) < 0) {
543
        VIR_FORCE_CLOSE(fd);
544
        virReportSystemError(errno,
545 546 547 548 549
                             _("Write to '%s' to trigger host scan failed"),
                             path);
        retval = -1;
    }

550
    VIR_FORCE_CLOSE(fd);
551
 free_path:
552
    VIR_FREE(path);
553
 out:
554
    VIR_DEBUG("Rescan of host %d complete", host);
555 556 557
    return retval;
}

558 559 560 561 562 563 564 565 566 567
/**
 * Frees opaque data
 *
 * @opaque Data to be freed
 */
static void
virStoragePoolFCRefreshDataFree(void *opaque)
{
    virStoragePoolFCRefreshInfoPtr cbdata = opaque;

568
    VIR_FREE(cbdata->fchost_name);
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
    VIR_FREE(cbdata);
}

/**
 * Thread to handle the pool refresh after a VPORT_CREATE is done. In this
 * case the 'udevEventHandleCallback' will be executed asynchronously as a
 * result of the node device driver callback routine to handle when udev
 * notices some sort of device change (such as adding a new device). It takes
 * some amount of time (usually a few seconds) for udev to go through the
 * process of setting up the new device.  Unfortunately, there is nothing
 * that says "when" it's done. The immediate virStorageBackendSCSIRefreshPool
 * done after virStorageBackendSCSIStartPool (and createVport) occurs too
 * quickly to find any devices.
 *
 * So this thread is designed to wait a few seconds (5), then make the query
 * to find the LUs for the pool.  If none yet exist, we'll try once more
 * to find the LUs before giving up.
 *
 * Attempting to find devices prior to allowing udev to settle down may result
 * in finding devices that then get deleted.
 *
 * @opaque Pool's Refresh Info containing name and pool object pointer
 */
static void
virStoragePoolFCRefreshThread(void *opaque)
{
    virStoragePoolFCRefreshInfoPtr cbdata = opaque;
596
    const char *fchost_name = cbdata->fchost_name;
597 598
    const unsigned char *pool_uuid = cbdata->pool_uuid;
    virStoragePoolObjPtr pool = NULL;
599
    unsigned int host;
J
John Ferlan 已提交
600
    int found = 0;
601 602 603 604 605
    int tries = 2;

    do {
        sleep(5); /* Give it time */

606 607 608 609 610 611
        /* Let's see if the pool still exists -  */
        if (!(pool = virStoragePoolObjFindPoolByUUID(pool_uuid)))
            break;

        /* Return with pool lock, if active, we can get the host number,
         * successfully, rescan, and find LUN's, then we are happy
612 613
         */
        VIR_DEBUG("Attempt FC Refresh for pool='%s' name='%s' tries='%d'",
614
                  pool->def->name, fchost_name, tries);
615
        if (virStoragePoolObjIsActive(pool) &&
616
            virGetSCSIHostNumber(fchost_name, &host) == 0 &&
617 618
            virStorageBackendSCSITriggerRescan(host) == 0) {
            virStoragePoolObjClearVols(pool);
619
            found = virStorageBackendSCSIFindLUs(pool, host);
620 621 622 623
        }
        virStoragePoolObjUnlock(pool);
    } while (!found && --tries);

624
    if (pool && !found)
625 626 627 628 629
        VIR_DEBUG("FC Refresh Thread failed to find LU's");

    virStoragePoolFCRefreshDataFree(cbdata);
}

630 631 632 633
static char *
getAdapterName(virStoragePoolSourceAdapter adapter)
{
    char *name = NULL;
634
    char *parentaddr = NULL;
635

636
    if (adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) {
637
        if (adapter.data.scsi_host.has_parent) {
638
            virDevicePCIAddress addr = adapter.data.scsi_host.parentaddr;
639 640
            unsigned int unique_id = adapter.data.scsi_host.unique_id;

641 642 643 644 645
            if (!(name = virGetSCSIHostNameByParentaddr(addr.domain,
                                                        addr.bus,
                                                        addr.slot,
                                                        addr.function,
                                                        unique_id)))
646 647 648 649
                goto cleanup;
        } else {
            ignore_value(VIR_STRDUP(name, adapter.data.scsi_host.name));
        }
650 651 652 653 654 655 656 657 658
    } else if (adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) {
        if (!(name = virGetFCHostNameByWWN(NULL,
                                           adapter.data.fchost.wwnn,
                                           adapter.data.fchost.wwpn))) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("Failed to find SCSI host with wwnn='%s', "
                             "wwpn='%s'"), adapter.data.fchost.wwnn,
                           adapter.data.fchost.wwpn);
        }
659 660
    }

661 662
 cleanup:
    VIR_FREE(parentaddr);
663 664 665
    return name;
}

666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
/*
 * Using the host# name found via wwnn/wwpn lookup in the fc_host
 * sysfs tree to get the parent 'scsi_host#' to ensure it matches.
 */
static bool
checkVhbaSCSIHostParent(virConnectPtr conn,
                        const char *name,
                        const char *parent_name)
{
    char *vhba_parent = NULL;
    bool retval = false;

    VIR_DEBUG("conn=%p, name=%s, parent_name=%s", conn, name, parent_name);

    /* autostarted pool - assume we're OK */
    if (!conn)
        return true;

684
    if (!(vhba_parent = virStoragePoolGetVhbaSCSIHostParent(conn, name)))
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
        goto cleanup;

    if (STRNEQ(parent_name, vhba_parent)) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("Parent attribute '%s' does not match parent '%s' "
                         "determined for the '%s' wwnn/wwpn lookup."),
                       parent_name, vhba_parent, name);
        goto cleanup;
    }

    retval = true;

 cleanup:
    VIR_FREE(vhba_parent);
    return retval;
}

702
static int
703
createVport(virConnectPtr conn,
704
            virStoragePoolObjPtr pool)
705
{
706 707
    const char *configFile = pool->configFile;
    virStoragePoolSourceAdapterPtr adapter = &pool->def->source.adapter;
708 709
    unsigned int parent_host;
    char *name = NULL;
710
    char *parent_hoststr = NULL;
711 712
    virStoragePoolFCRefreshInfoPtr cbdata = NULL;
    virThread thread;
713

714
    if (adapter->type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST)
715 716
        return 0;

717 718 719 720
    VIR_DEBUG("conn=%p, configFile='%s' parent='%s', wwnn='%s' wwpn='%s'",
              conn, NULLSTR(configFile), NULLSTR(adapter->data.fchost.parent),
              adapter->data.fchost.wwnn, adapter->data.fchost.wwpn);

721
    /* If a parent was provided, then let's make sure it's vhost capable */
722 723
    if (adapter->data.fchost.parent) {
        if (virGetSCSIHostNumber(adapter->data.fchost.parent, &parent_host) < 0)
724 725 726 727 728 729
            return -1;

        if (!virIsCapableFCHost(NULL, parent_host)) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("parent '%s' specified for vHBA "
                             "is not vport capable"),
730
                           adapter->data.fchost.parent);
731 732 733 734
            return -1;
        }
    }

735 736 737 738
    /* If we find an existing HBA/vHBA within the fc_host sysfs
     * using the wwnn/wwpn, then a nodedev is already created for
     * this pool and we don't have to create the vHBA
     */
739 740
    if ((name = virGetFCHostNameByWWN(NULL, adapter->data.fchost.wwnn,
                                      adapter->data.fchost.wwpn))) {
741 742 743 744 745
        int retval = 0;

        /* If a parent was provided, let's make sure the 'name' we've
         * retrieved has the same parent
         */
746 747
        if (adapter->data.fchost.parent &&
            !checkVhbaSCSIHostParent(conn, name, adapter->data.fchost.parent))
748 749
            retval = -1;

750
        VIR_FREE(name);
751
        return retval;
752 753
    }

754
    if (!adapter->data.fchost.parent) {
755
        if (!(parent_hoststr = virFindFCHostCapableVport(NULL))) {
756 757 758 759 760
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("'parent' for vHBA not specified, and "
                             "cannot find one on this host"));
            return -1;
        }
761

762 763
        if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) {
            VIR_FREE(parent_hoststr);
764
            return -1;
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
        }

        /* NOTE:
         * We do not save the parent_hoststr in adapter->data.fchost.parent
         * since we could be writing out the 'def' to the saved XML config.
         * If we wrote out the name in the XML, then future starts would
         * always use the same parent rather than finding the "best available"
         * parent. Besides we have a way to determine the parent based on
         * the 'name' field.
         */
        VIR_FREE(parent_hoststr);
    }

    /* Since we're creating the vHBA, then we need to manage removing it
     * as well. Since we need this setting to "live" through a libvirtd
     * restart, we need to save the persistent configuration. So if not
     * already defined as YES, then force the issue.
     */
    if (adapter->data.fchost.managed != VIR_TRISTATE_BOOL_YES) {
        adapter->data.fchost.managed = VIR_TRISTATE_BOOL_YES;
        if (configFile) {
786
            if (virStoragePoolSaveConfig(configFile, pool->def) < 0)
787 788
                return -1;
        }
789
    }
790

791 792
    if (virManageVport(parent_host, adapter->data.fchost.wwpn,
                       adapter->data.fchost.wwnn, VPORT_CREATE) < 0)
793 794 795
        return -1;

    virFileWaitForDevices();
796 797 798 799 800 801 802 803 804

    /* Creating our own VPORT didn't leave enough time to find any LUN's,
     * so, let's create a thread whose job it is to call the FindLU's with
     * retry logic set to true. If the thread isn't created, then no big
     * deal since it's still possible to refresh the pool later.
     */
    if ((name = virGetFCHostNameByWWN(NULL, adapter->data.fchost.wwnn,
                                      adapter->data.fchost.wwpn))) {
        if (VIR_ALLOC(cbdata) == 0) {
805
            memcpy(cbdata->pool_uuid, pool->def->uuid, VIR_UUID_BUFLEN);
806
            cbdata->fchost_name = name;
807 808 809 810 811 812 813 814 815 816 817 818
            name = NULL;

            if (virThreadCreate(&thread, false, virStoragePoolFCRefreshThread,
                                cbdata) < 0) {
                /* Oh well - at least someone can still refresh afterwards */
                VIR_DEBUG("Failed to create FC Pool Refresh Thread");
                virStoragePoolFCRefreshDataFree(cbdata);
            }
        }
        VIR_FREE(name);
    }

819 820 821 822
    return 0;
}

static int
823 824
deleteVport(virConnectPtr conn,
            virStoragePoolSourceAdapter adapter)
825 826
{
    unsigned int parent_host;
O
Osier Yang 已提交
827
    char *name = NULL;
828
    char *vhba_parent = NULL;
O
Osier Yang 已提交
829
    int ret = -1;
830 831 832 833

    if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST)
        return 0;

834 835 836 837 838 839 840 841
    VIR_DEBUG("conn=%p parent='%s', managed='%d' wwnn='%s' wwpn='%s'",
              conn, NULLSTR(adapter.data.fchost.parent),
              adapter.data.fchost.managed,
              adapter.data.fchost.wwnn,
              adapter.data.fchost.wwpn);

    /* If we're not managing the deletion of the vHBA, then just return */
    if (adapter.data.fchost.managed != VIR_TRISTATE_BOOL_YES)
842 843
        return 0;

844
    /* Find our vHBA by searching the fc_host sysfs tree for our wwnn/wwpn */
O
Osier Yang 已提交
845
    if (!(name = virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn,
846 847 848 849
                                       adapter.data.fchost.wwpn))) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to find fc_host for wwnn='%s' and wwpn='%s'"),
                       adapter.data.fchost.wwnn, adapter.data.fchost.wwpn);
O
Osier Yang 已提交
850
        goto cleanup;
851 852 853 854 855 856 857 858 859 860
    }

    /* If at startup time we provided a parent, then use that to
     * get the parent_host value; otherwise, we have to determine
     * the parent scsi_host which we did not save at startup time
     */
    if (adapter.data.fchost.parent) {
        if (virGetSCSIHostNumber(adapter.data.fchost.parent, &parent_host) < 0)
            goto cleanup;
    } else {
861
        if (!(vhba_parent = virStoragePoolGetVhbaSCSIHostParent(conn, name)))
862 863 864 865 866
            goto cleanup;

        if (virGetSCSIHostNumber(vhba_parent, &parent_host) < 0)
            goto cleanup;
    }
867

868 869
    if (virManageVport(parent_host, adapter.data.fchost.wwpn,
                       adapter.data.fchost.wwnn, VPORT_DELETE) < 0)
O
Osier Yang 已提交
870
        goto cleanup;
871

O
Osier Yang 已提交
872
    ret = 0;
873
 cleanup:
O
Osier Yang 已提交
874
    VIR_FREE(name);
875
    VIR_FREE(vhba_parent);
O
Osier Yang 已提交
876
    return ret;
877 878 879
}


880
static int
881
virStorageBackendSCSICheckPool(virStoragePoolObjPtr pool,
882 883
                               bool *isActive)
{
884 885
    char *path = NULL;
    char *name = NULL;
886
    unsigned int host;
887
    int ret = -1;
888 889

    *isActive = false;
890

891 892 893 894 895 896 897 898 899 900 901 902 903
    if (!(name = getAdapterName(pool->def->source.adapter))) {
        /* It's normal for the pool with "fc_host" type source
         * adapter fails to get the adapter name, since the vHBA
         * the adapter based on might be not created yet.
         */
        if (pool->def->source.adapter.type ==
            VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) {
            virResetLastError();
            return 0;
        } else {
            return -1;
        }
    }
904

905
    if (virGetSCSIHostNumber(name, &host) < 0)
906 907
        goto cleanup;

908 909
    if (virAsprintf(&path, "%s/host%d",
                    LINUX_SYSFS_SCSI_HOST_PREFIX, host) < 0)
910
        goto cleanup;
911

912
    *isActive = virFileExists(path);
913

914
    ret = 0;
915
 cleanup:
916
    VIR_FREE(path);
917 918
    VIR_FREE(name);
    return ret;
919
}
920

921
static int
922
virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
923 924
                                 virStoragePoolObjPtr pool)
{
925
    char *name = NULL;
926
    unsigned int host;
927
    int ret = -1;
928 929 930

    pool->def->allocation = pool->def->capacity = pool->def->available = 0;

931 932 933
    if (!(name = getAdapterName(pool->def->source.adapter)))
        return -1;

934
    if (virGetSCSIHostNumber(name, &host) < 0)
935 936
        goto out;

937
    VIR_DEBUG("Scanning host%u", host);
938

939
    if (virStorageBackendSCSITriggerRescan(host) < 0)
940 941
        goto out;

942 943
    if (virStorageBackendSCSIFindLUs(pool, host) < 0)
        goto out;
944

945
    ret = 0;
946
 out:
947
    VIR_FREE(name);
948
    return ret;
949 950
}

951
static int
952
virStorageBackendSCSIStartPool(virConnectPtr conn,
953 954
                               virStoragePoolObjPtr pool)
{
955
    return createVport(conn, pool);
956 957 958
}

static int
959
virStorageBackendSCSIStopPool(virConnectPtr conn,
960 961 962
                              virStoragePoolObjPtr pool)
{
    virStoragePoolSourceAdapter adapter = pool->def->source.adapter;
963
    return deleteVport(conn, adapter);
964
}
965 966 967 968

virStorageBackend virStorageBackendSCSI = {
    .type = VIR_STORAGE_POOL_SCSI,

969
    .checkPool = virStorageBackendSCSICheckPool,
970
    .refreshPool = virStorageBackendSCSIRefreshPool,
971 972
    .startPool = virStorageBackendSCSIStartPool,
    .stopPool = virStorageBackendSCSIStopPool,
973 974
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
975
    .wipeVol = virStorageBackendVolWipeLocal,
976
};