Example #1
0
/*
 * Determine if the underlying device is accessible by reading and writing
 * to a known location. We must be able to do this during syncing context
 * and thus we cannot set the vdev state directly.
 */
static int
vdev_file_probe(vdev_t *vd)
{
	vdev_t *nvd;
	char *vl_boot;
	uint64_t offset;
	int l, error = 0, retries = 0;

	if (vd == NULL)
		return (EINVAL);

	/* Hijack the current vdev */
	nvd = vd;

	/*
	 * Pick a random label to rewrite.
	 */
	l = spa_get_random(VDEV_LABELS);
	ASSERT(l < VDEV_LABELS);

	offset = vdev_label_offset(vd->vdev_psize, l,
	    offsetof(vdev_label_t, vl_boot_header));

	vl_boot = kmem_alloc(VDEV_BOOT_HEADER_SIZE, KM_SLEEP);

	while ((error = vdev_file_probe_io(nvd, vl_boot, VDEV_BOOT_HEADER_SIZE,
	    offset, UIO_READ)) != 0 && retries == 0) {

		/*
		 * If we failed with the vdev that was passed in then
		 * try allocating a new one and try again.
		 */
		nvd = kmem_zalloc(sizeof (vdev_t), KM_SLEEP);
		if (vd->vdev_path)
			nvd->vdev_path = spa_strdup(vd->vdev_path);
		nvd->vdev_guid = vd->vdev_guid;
		retries++;

		if (vdev_file_open_common(nvd) != 0)
			break;
	}

	if ((spa_mode & FWRITE) && !error) {
		error = vdev_file_probe_io(nvd, vl_boot, VDEV_BOOT_HEADER_SIZE,
		    offset, UIO_WRITE);
	}

	if (retries) {
		vdev_file_close(nvd);
		if (nvd->vdev_path)
			spa_strfree(nvd->vdev_path);
		kmem_free(nvd, sizeof (vdev_t));
	}
	kmem_free(vl_boot, VDEV_BOOT_HEADER_SIZE);

	if (!error)
		vd->vdev_is_failing = B_FALSE;

	return (error);
}
Example #2
0
/*
 * Given the root disk device name, read the label from
 * the device, and construct a configuration nvlist.
 */
int
vdev_disk_read_rootlabel(char *devname, nvlist_t **config)
{
	vdev_label_t *label;
	struct device *dev;
	uint64_t size;
	int l;
	int error = -1;
	char *minor_name;

	error = device_open(devname + 5, DO_RDWR, &dev);
	if (error)
		return error;

	size = P2ALIGN_TYPED(dev->size, sizeof (vdev_label_t), uint64_t);
	label = kmem_alloc(sizeof (vdev_label_t), KM_SLEEP);

	*config = NULL;
	for (l = 0; l < VDEV_LABELS; l++) {
		uint64_t offset, state, txg = 0;

		/* read vdev label */
		offset = vdev_label_offset(size, l, 0);
		if (vdev_disk_physio(dev, (caddr_t)label,
		    VDEV_SKIP_SIZE + VDEV_PHYS_SIZE, offset, 0) != 0)
			continue;

		if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist,
		    sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0) {
			*config = NULL;
			continue;
		}

		if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE,
		    &state) != 0 || state >= POOL_STATE_DESTROYED) {
			nvlist_free(*config);
			*config = NULL;
			continue;
		}

		if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG,
		    &txg) != 0 || txg == 0) {
			nvlist_free(*config);
			*config = NULL;
			continue;
		}

		break;
	}

	kmem_free(label, sizeof (vdev_label_t));
	device_close(dev);
	if (*config == NULL)
		error = EIDRM;

	return (error);
}
Example #3
0
/*
 * Determine if the zio is part of a label update and has an injection
 * handler associated with that portion of the label. Currently, we
 * allow error injection in either the nvlist or the uberblock region of
 * of the vdev label.
 */
int
zio_handle_label_injection(zio_t *zio, int error)
{
	inject_handler_t *handler;
	vdev_t *vd = zio->io_vd;
	uint64_t offset = zio->io_offset;
	int label;
	int ret = 0;

	if (offset >= VDEV_LABEL_START_SIZE &&
	    offset < vd->vdev_psize - VDEV_LABEL_END_SIZE)
		return (0);

	rw_enter(&inject_lock, RW_READER);

	for (handler = list_head(&inject_handlers); handler != NULL;
	    handler = list_next(&inject_handlers, handler)) {
		uint64_t start = handler->zi_record.zi_start;
		uint64_t end = handler->zi_record.zi_end;

		if (handler->zi_record.zi_cmd != ZINJECT_LABEL_FAULT)
			continue;

		/*
		 * The injection region is the relative offsets within a
		 * vdev label. We must determine the label which is being
		 * updated and adjust our region accordingly.
		 */
		label = vdev_label_number(vd->vdev_psize, offset);
		start = vdev_label_offset(vd->vdev_psize, label, start);
		end = vdev_label_offset(vd->vdev_psize, label, end);

		if (zio->io_vd->vdev_guid == handler->zi_record.zi_guid &&
		    (offset >= start && offset <= end)) {
			ret = error;
			break;
		}
	}
	rw_exit(&inject_lock);
	return (ret);
}
Example #4
0
        offset -= psize - VDEV_LABEL_END_SIZE;
        offset += (VDEV_LABELS / 2) * sizeof (vdev_label_t);
    }
    l = offset / sizeof (vdev_label_t);
    return (l < VDEV_LABELS ? l : -1);
}

static void
vdev_label_read(zio_t *zio, vdev_t *vd, int l, void *buf, uint64_t offset,
                uint64_t size, zio_done_func_t *done, void *private, int flags)
{
    ASSERT(spa_config_held(zio->io_spa, SCL_STATE_ALL, RW_WRITER) ==
           SCL_STATE_ALL);
    ASSERT(flags & ZIO_FLAG_CONFIG_WRITER);
    zio_nowait(zio_read_phys(zio, vd,
                             vdev_label_offset(vd->vdev_psize, l, offset),
                             size, buf, ZIO_CHECKSUM_LABEL, done, private,
                             ZIO_PRIORITY_SYNC_READ, flags, B_TRUE));
}

static void
vdev_label_write(zio_t *zio, vdev_t *vd, int l, void *buf, uint64_t offset,
                 uint64_t size, zio_done_func_t *done, void *private, int flags)
{
    ASSERT(spa_config_held(zio->io_spa, SCL_ALL, RW_WRITER) == SCL_ALL ||
           (spa_config_held(zio->io_spa, SCL_CONFIG | SCL_STATE, RW_READER) ==
            (SCL_CONFIG | SCL_STATE) &&
            dsl_pool_sync_context(spa_get_dsl(zio->io_spa))));
    ASSERT(flags & ZIO_FLAG_CONFIG_WRITER);

    zio_nowait(zio_write_phys(zio, vd,
Example #5
0
/*
 * Given the root disk device devid or pathname, read the label from
 * the device, and construct a configuration nvlist.
 */
int
vdev_disk_read_rootlabel(char *devpath, char *devid, nvlist_t **config)
{
	ldi_handle_t vd_lh;
	vdev_label_t *label;
	uint64_t s, size;
	int l;
	ddi_devid_t tmpdevid;
	int error = -1;
	char *minor_name;

	/*
	 * Read the device label and build the nvlist.
	 */
	if (devid != NULL && ddi_devid_str_decode(devid, &tmpdevid,
	    &minor_name) == 0) {
		error = ldi_open_by_devid(tmpdevid, minor_name,
		    FREAD, kcred, &vd_lh, zfs_li);
		ddi_devid_free(tmpdevid);
		ddi_devid_str_free(minor_name);
	}

	if (error && (error = ldi_open_by_name(devpath, FREAD, kcred, &vd_lh,
	    zfs_li)))
		return (error);

	if (ldi_get_size(vd_lh, &s)) {
		(void) ldi_close(vd_lh, FREAD, kcred);
		return (SET_ERROR(EIO));
	}

	size = P2ALIGN_TYPED(s, sizeof (vdev_label_t), uint64_t);
	label = kmem_alloc(sizeof (vdev_label_t), KM_SLEEP);

	*config = NULL;
	for (l = 0; l < VDEV_LABELS; l++) {
		uint64_t offset, state, txg = 0;

		/* read vdev label */
		offset = vdev_label_offset(size, l, 0);
		if (vdev_disk_ldi_physio(vd_lh, (caddr_t)label,
		    VDEV_SKIP_SIZE + VDEV_PHYS_SIZE, offset, B_READ) != 0)
			continue;

		if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist,
		    sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0) {
			*config = NULL;
			continue;
		}

		if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE,
		    &state) != 0 || state >= POOL_STATE_DESTROYED) {
			nvlist_free(*config);
			*config = NULL;
			continue;
		}

		if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG,
		    &txg) != 0 || txg == 0) {
			nvlist_free(*config);
			*config = NULL;
			continue;
		}

		break;
	}

	kmem_free(label, sizeof (vdev_label_t));
	(void) ldi_close(vd_lh, FREAD, kcred);
	if (*config == NULL)
		error = SET_ERROR(EIDRM);

	return (error);
}