storage_backend_scsi.c 27.2 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 46 47 48 49
typedef struct _virStoragePoolFCRefreshInfo virStoragePoolFCRefreshInfo;
typedef virStoragePoolFCRefreshInfo *virStoragePoolFCRefreshInfoPtr;
struct _virStoragePoolFCRefreshInfo {
    char *name;
    virStoragePoolObjPtr pool;
};

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
static int
150
virStorageBackendSCSINewLun(virStoragePoolObjPtr pool,
151
                            uint32_t host ATTRIBUTE_UNUSED,
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
                            uint32_t bus,
                            uint32_t target,
                            uint32_t lun,
                            const char *dev)
{
    virStorageVolDefPtr vol;
    char *devpath = NULL;
    int retval = 0;

    if (VIR_ALLOC(vol) < 0) {
        retval = -1;
        goto out;
    }

    vol->type = VIR_STORAGE_VOL_BLOCK;

168 169 170 171 172 173
    /* '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
     */
    if (virAsprintf(&(vol->name), "unit:%u:%u:%u", bus, target, lun) < 0) {
174 175 176 177 178 179 180 181 182
        retval = -1;
        goto free_vol;
    }

    if (virAsprintf(&devpath, "/dev/%s", dev) < 0) {
        retval = -1;
        goto free_vol;
    }

183
    VIR_DEBUG("Trying to create volume for '%s'", devpath);
184 185 186 187 188 189 190

    /* 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...
     */
191
    if ((vol->target.path = virStorageBackendStablePath(pool,
192 193
                                                        devpath,
                                                        true)) == NULL) {
194 195 196 197
        retval = -1;
        goto free_vol;
    }

198
    if (STREQ(devpath, vol->target.path) &&
199 200 201
        !(STREQ(pool->def->target.path, "/dev") ||
          STREQ(pool->def->target.path, "/dev/"))) {

202
        VIR_DEBUG("No stable path found for '%s' in '%s'",
203 204 205 206 207 208
                  devpath, pool->def->target.path);

        retval = -1;
        goto free_vol;
    }

209
    if (virStorageBackendUpdateVolInfo(vol, true,
210
                                       VIR_STORAGE_VOL_OPEN_DEFAULT) < 0) {
211 212 213 214
        retval = -1;
        goto free_vol;
    }

215
    if (!(vol->key = virStorageBackendSCSISerial(vol->target.path))) {
216 217 218 219
        retval = -1;
        goto free_vol;
    }

220 221
    pool->def->capacity += vol->target.capacity;
    pool->def->allocation += vol->target.allocation;
222

223
    if (VIR_APPEND_ELEMENT(pool->volumes.objs, pool->volumes.count, vol) < 0) {
224 225 226 227 228 229
        retval = -1;
        goto free_vol;
    }

    goto out;

230
 free_vol:
231
    virStorageVolDefFree(vol);
232
 out:
233 234 235 236 237 238
    VIR_FREE(devpath);
    return retval;
}


static int
239
getNewStyleBlockDevice(const char *lun_path,
240 241 242 243 244 245 246
                       const char *block_name ATTRIBUTE_UNUSED,
                       char **block_device)
{
    char *block_path = NULL;
    DIR *block_dir = NULL;
    struct dirent *block_dirent = NULL;
    int retval = 0;
E
Eric Blake 已提交
247
    int direrr;
248

249
    if (virAsprintf(&block_path, "%s/block", lun_path) < 0)
250 251
        goto out;

252
    VIR_DEBUG("Looking for block device in '%s'", block_path);
253 254 255

    block_dir = opendir(block_path);
    if (block_dir == NULL) {
256
        virReportSystemError(errno,
257 258 259 260 261 262
                             _("Failed to opendir sysfs path '%s'"),
                             block_path);
        retval = -1;
        goto out;
    }

E
Eric Blake 已提交
263
    while ((direrr = virDirRead(block_dir, &block_dirent, block_path)) > 0) {
264

265
        if (STREQLEN(block_dirent->d_name, ".", 1))
266 267
            continue;

268
        if (VIR_STRDUP(*block_device, block_dirent->d_name) < 0) {
269 270 271 272 273
            closedir(block_dir);
            retval = -1;
            goto out;
        }

274
        VIR_DEBUG("Block device is '%s'", *block_device);
275 276 277

        break;
    }
E
Eric Blake 已提交
278 279
    if (direrr < 0)
        retval = -1;
280 281 282

    closedir(block_dir);

283
 out:
284 285 286 287 288 289
    VIR_FREE(block_path);
    return retval;
}


static int
290
getOldStyleBlockDevice(const char *lun_path ATTRIBUTE_UNUSED,
291 292 293 294 295 296 297 298 299 300
                       const char *block_name,
                       char **block_device)
{
    char *blockp = NULL;
    int retval = 0;

    /* old-style; just parse out the sd */
    blockp = strrchr(block_name, ':');
    if (blockp == NULL) {
        /* Hm, wasn't what we were expecting; have to give up */
301 302 303
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to parse block name %s"),
                       block_name);
304 305 306
        retval = -1;
    } else {
        blockp++;
307
        if (VIR_STRDUP(*block_device, blockp) < 0) {
308 309 310 311
            retval = -1;
            goto out;
        }

312
        VIR_DEBUG("Block device is '%s'", *block_device);
313 314
    }

315
 out:
316 317 318 319 320
    return retval;
}


static int
321
getBlockDevice(uint32_t host,
322 323 324 325 326 327 328 329
               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;
330
    int retval = -1;
E
Eric Blake 已提交
331
    int direrr;
332 333

    if (virAsprintf(&lun_path, "/sys/bus/scsi/devices/%u:%u:%u:%u",
334
                    host, bus, target, lun) < 0)
335 336 337 338
        goto out;

    lun_dir = opendir(lun_path);
    if (lun_dir == NULL) {
339
        virReportSystemError(errno,
340 341 342 343 344
                             _("Failed to opendir sysfs path '%s'"),
                             lun_path);
        goto out;
    }

E
Eric Blake 已提交
345
    while ((direrr = virDirRead(lun_dir, &lun_dirent, lun_path)) > 0) {
346 347
        if (STREQLEN(lun_dirent->d_name, "block", 5)) {
            if (strlen(lun_dirent->d_name) == 5) {
348
                retval = getNewStyleBlockDevice(lun_path,
349 350 351
                                                lun_dirent->d_name,
                                                block_device);
            } else {
352
                retval = getOldStyleBlockDevice(lun_path,
353 354 355 356 357 358 359 360 361
                                                lun_dirent->d_name,
                                                block_device);
            }
            break;
        }
    }

    closedir(lun_dir);

362
 out:
363 364 365 366 367 368
    VIR_FREE(lun_path);
    return retval;
}


static int
369
processLU(virStoragePoolObjPtr pool,
370 371 372 373 374
          uint32_t host,
          uint32_t bus,
          uint32_t target,
          uint32_t lun)
{
375
    int retval = -1;
376 377 378
    int device_type;
    char *block_device = NULL;

379
    VIR_DEBUG("Processing LU %u:%u:%u:%u",
380 381
              host, bus, target, lun);

382
    if (getDeviceType(host, bus, target, lun, &device_type) < 0) {
383 384 385
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"),
                       host, bus, target, lun);
386 387 388 389 390 391 392 393 394 395 396 397 398
        goto out;
    }

    /* 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))
    {
        retval = 0;
        goto out;
    }

399
    VIR_DEBUG("%u:%u:%u:%u is a Direct-Access LUN",
400 401
              host, bus, target, lun);

402 403
    if (getBlockDevice(host, bus, target, lun, &block_device) < 0) {
        VIR_DEBUG("Failed to find block device for this LUN");
404
        goto out;
405
    }
406

407
    if (virStorageBackendSCSINewLun(pool,
408 409
                                    host, bus, target, lun,
                                    block_device) < 0) {
410
        VIR_DEBUG("Failed to create new storage volume for %u:%u:%u:%u",
411 412 413
                  host, bus, target, lun);
        goto out;
    }
414
    retval = 0;
415

416
    VIR_DEBUG("Created new storage volume for %u:%u:%u:%u successfully",
417 418
              host, bus, target, lun);

419
 out:
420
    VIR_FREE(block_device);
421 422 423 424
    return retval;
}


425 426 427 428
static int
virStorageBackendSCSIFindLUsInternal(virStoragePoolObjPtr pool,
                                     uint32_t scanhost,
                                     bool *found)
429 430 431
{
    int retval = 0;
    uint32_t bus, target, lun;
432
    const char *device_path = "/sys/bus/scsi/devices";
433 434 435 436
    DIR *devicedir = NULL;
    struct dirent *lun_dirent = NULL;
    char devicepattern[64];

437
    VIR_DEBUG("Discovering LUs on host %u", scanhost);
438

439
    virFileWaitForDevices();
440 441 442 443

    devicedir = opendir(device_path);

    if (devicedir == NULL) {
444
        virReportSystemError(errno,
445
                             _("Failed to opendir path '%s'"), device_path);
446
        return -1;
447 448 449 450
    }

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

451
    *found = false;
E
Eric Blake 已提交
452
    while ((retval = virDirRead(devicedir, &lun_dirent, device_path)) > 0) {
453 454 455 456 457
        if (sscanf(lun_dirent->d_name, devicepattern,
                   &bus, &target, &lun) != 3) {
            continue;
        }

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

460
        if (processLU(pool, scanhost, bus, target, lun) == 0)
461
            *found = true;
462 463
    }

464
    if (!*found)
465 466
        VIR_DEBUG("No LU found for pool %s", pool->def->name);

467 468 469 470 471
    closedir(devicedir);

    return retval;
}

472 473 474 475 476 477 478 479
int
virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
                             uint32_t scanhost)
{
    bool found;  /* This path doesn't care whether found or not */
    return virStorageBackendSCSIFindLUsInternal(pool, scanhost, &found);
}

480
static int
481
virStorageBackendSCSITriggerRescan(uint32_t host)
482 483 484 485 486
{
    int fd = -1;
    int retval = 0;
    char *path;

487
    VIR_DEBUG("Triggering rescan of host %d", host);
488

489 490
    if (virAsprintf(&path, "%s/host%u/scan",
                    LINUX_SYSFS_SCSI_HOST_PREFIX, host) < 0) {
491 492 493 494
        retval = -1;
        goto out;
    }

495
    VIR_DEBUG("Scan trigger path is '%s'", path);
496 497 498 499

    fd = open(path, O_WRONLY);

    if (fd < 0) {
500
        virReportSystemError(errno,
501 502 503 504 505 506 507 508 509
                             _("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) {
510
        VIR_FORCE_CLOSE(fd);
511
        virReportSystemError(errno,
512 513 514 515 516
                             _("Write to '%s' to trigger host scan failed"),
                             path);
        retval = -1;
    }

517
    VIR_FORCE_CLOSE(fd);
518
 free_path:
519
    VIR_FREE(path);
520
 out:
521
    VIR_DEBUG("Rescan of host %d complete", host);
522 523 524
    return retval;
}

525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
/**
 * Frees opaque data
 *
 * @opaque Data to be freed
 */
static void
virStoragePoolFCRefreshDataFree(void *opaque)
{
    virStoragePoolFCRefreshInfoPtr cbdata = opaque;

    VIR_FREE(cbdata->name);
    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;
    const char *name = cbdata->name;
    virStoragePoolObjPtr pool = cbdata->pool;
    unsigned int host;
    bool found = false;
    int tries = 2;

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

        /* Lock the pool, if active, we can get the host number, successfully
         * rescan, and find LUN's, then we are happy
         */
        VIR_DEBUG("Attempt FC Refresh for pool='%s' name='%s' tries='%d'",
                  pool->def->name, name, tries);
        virStoragePoolObjLock(pool);
        if (virStoragePoolObjIsActive(pool) &&
            virGetSCSIHostNumber(name, &host) == 0 &&
            virStorageBackendSCSITriggerRescan(host) == 0) {
            virStoragePoolObjClearVols(pool);
            virStorageBackendSCSIFindLUsInternal(pool, host, &found);
        }
        virStoragePoolObjUnlock(pool);
    } while (!found && --tries);

    if (!found)
        VIR_DEBUG("FC Refresh Thread failed to find LU's");

    virStoragePoolFCRefreshDataFree(cbdata);
}

593 594 595 596
static char *
getAdapterName(virStoragePoolSourceAdapter adapter)
{
    char *name = NULL;
597
    char *parentaddr = NULL;
598

599
    if (adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) {
600
        if (adapter.data.scsi_host.has_parent) {
601
            virDevicePCIAddress addr = adapter.data.scsi_host.parentaddr;
602 603
            unsigned int unique_id = adapter.data.scsi_host.unique_id;

604 605 606 607 608
            if (!(name = virGetSCSIHostNameByParentaddr(addr.domain,
                                                        addr.bus,
                                                        addr.slot,
                                                        addr.function,
                                                        unique_id)))
609 610 611 612
                goto cleanup;
        } else {
            ignore_value(VIR_STRDUP(name, adapter.data.scsi_host.name));
        }
613 614 615 616 617 618 619 620 621
    } 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);
        }
622 623
    }

624 625
 cleanup:
    VIR_FREE(parentaddr);
626 627 628
    return name;
}

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
/*
 * 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;

647
    if (!(vhba_parent = virStoragePoolGetVhbaSCSIHostParent(conn, name)))
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
        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;
}

665
static int
666
createVport(virConnectPtr conn,
667
            virStoragePoolObjPtr pool)
668
{
669 670
    const char *configFile = pool->configFile;
    virStoragePoolSourceAdapterPtr adapter = &pool->def->source.adapter;
671 672
    unsigned int parent_host;
    char *name = NULL;
673
    char *parent_hoststr = NULL;
674 675
    virStoragePoolFCRefreshInfoPtr cbdata = NULL;
    virThread thread;
676

677
    if (adapter->type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST)
678 679
        return 0;

680 681 682 683
    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);

684
    /* If a parent was provided, then let's make sure it's vhost capable */
685 686
    if (adapter->data.fchost.parent) {
        if (virGetSCSIHostNumber(adapter->data.fchost.parent, &parent_host) < 0)
687 688 689 690 691 692
            return -1;

        if (!virIsCapableFCHost(NULL, parent_host)) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("parent '%s' specified for vHBA "
                             "is not vport capable"),
693
                           adapter->data.fchost.parent);
694 695 696 697
            return -1;
        }
    }

698 699 700 701
    /* 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
     */
702 703
    if ((name = virGetFCHostNameByWWN(NULL, adapter->data.fchost.wwnn,
                                      adapter->data.fchost.wwpn))) {
704 705 706 707 708
        int retval = 0;

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

713
        VIR_FREE(name);
714
        return retval;
715 716
    }

717
    if (!adapter->data.fchost.parent) {
718
        if (!(parent_hoststr = virFindFCHostCapableVport(NULL))) {
719 720 721 722 723
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("'parent' for vHBA not specified, and "
                             "cannot find one on this host"));
            return -1;
        }
724

725 726
        if (virGetSCSIHostNumber(parent_hoststr, &parent_host) < 0) {
            VIR_FREE(parent_hoststr);
727
            return -1;
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
        }

        /* 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) {
749
            if (virStoragePoolSaveConfig(configFile, pool->def) < 0)
750 751
                return -1;
        }
752
    }
753

754 755
    if (virManageVport(parent_host, adapter->data.fchost.wwpn,
                       adapter->data.fchost.wwnn, VPORT_CREATE) < 0)
756 757 758
        return -1;

    virFileWaitForDevices();
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781

    /* 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) {
            cbdata->pool = pool;
            cbdata->name = name;
            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);
    }

782 783 784 785
    return 0;
}

static int
786 787
deleteVport(virConnectPtr conn,
            virStoragePoolSourceAdapter adapter)
788 789
{
    unsigned int parent_host;
O
Osier Yang 已提交
790
    char *name = NULL;
791
    char *vhba_parent = NULL;
O
Osier Yang 已提交
792
    int ret = -1;
793 794 795 796

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

797 798 799 800 801 802 803 804
    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)
805 806
        return 0;

807
    /* Find our vHBA by searching the fc_host sysfs tree for our wwnn/wwpn */
O
Osier Yang 已提交
808
    if (!(name = virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn,
809 810 811 812
                                       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 已提交
813
        goto cleanup;
814 815 816 817 818 819 820 821 822 823
    }

    /* 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 {
824
        if (!(vhba_parent = virStoragePoolGetVhbaSCSIHostParent(conn, name)))
825 826 827 828 829
            goto cleanup;

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

831 832
    if (virManageVport(parent_host, adapter.data.fchost.wwpn,
                       adapter.data.fchost.wwnn, VPORT_DELETE) < 0)
O
Osier Yang 已提交
833
        goto cleanup;
834

O
Osier Yang 已提交
835
    ret = 0;
836
 cleanup:
O
Osier Yang 已提交
837
    VIR_FREE(name);
838
    VIR_FREE(vhba_parent);
O
Osier Yang 已提交
839
    return ret;
840 841 842
}


843
static int
844
virStorageBackendSCSICheckPool(virStoragePoolObjPtr pool,
845 846
                               bool *isActive)
{
847 848
    char *path = NULL;
    char *name = NULL;
849
    unsigned int host;
850
    int ret = -1;
851 852

    *isActive = false;
853

854 855 856 857 858 859 860 861 862 863 864 865 866
    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;
        }
    }
867

868
    if (virGetSCSIHostNumber(name, &host) < 0)
869 870
        goto cleanup;

871 872
    if (virAsprintf(&path, "%s/host%d",
                    LINUX_SYSFS_SCSI_HOST_PREFIX, host) < 0)
873
        goto cleanup;
874

875
    *isActive = virFileExists(path);
876

877
    ret = 0;
878
 cleanup:
879
    VIR_FREE(path);
880 881
    VIR_FREE(name);
    return ret;
882
}
883

884
static int
885
virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
886 887
                                 virStoragePoolObjPtr pool)
{
888
    char *name = NULL;
889
    unsigned int host;
890
    int ret = -1;
891 892 893

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

894 895 896
    if (!(name = getAdapterName(pool->def->source.adapter)))
        return -1;

897
    if (virGetSCSIHostNumber(name, &host) < 0)
898 899
        goto out;

900
    VIR_DEBUG("Scanning host%u", host);
901

902
    if (virStorageBackendSCSITriggerRescan(host) < 0)
903 904
        goto out;

905
    virStorageBackendSCSIFindLUs(pool, host);
906

907
    ret = 0;
908
 out:
909
    VIR_FREE(name);
910
    return ret;
911 912
}

913
static int
914
virStorageBackendSCSIStartPool(virConnectPtr conn,
915 916
                               virStoragePoolObjPtr pool)
{
917
    return createVport(conn, pool);
918 919 920
}

static int
921
virStorageBackendSCSIStopPool(virConnectPtr conn,
922 923 924
                              virStoragePoolObjPtr pool)
{
    virStoragePoolSourceAdapter adapter = pool->def->source.adapter;
925
    return deleteVport(conn, adapter);
926
}
927 928 929 930

virStorageBackend virStorageBackendSCSI = {
    .type = VIR_STORAGE_POOL_SCSI,

931
    .checkPool = virStorageBackendSCSICheckPool,
932
    .refreshPool = virStorageBackendSCSIRefreshPool,
933 934
    .startPool = virStorageBackendSCSIStartPool,
    .stopPool = virStorageBackendSCSIStopPool,
935 936
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
937
    .wipeVol = virStorageBackendVolWipeLocal,
938
};