Beispiel #1
0
/* ARGSUSED */
static void
vdev_disk_off_finalize(ldi_handle_t lh, ldi_ev_cookie_t ecookie,
    int ldi_result, void *arg, void *ev_data)
{
	vdev_t *vd = (vdev_t *)arg;

	/*
	 * Ignore events other than offline.
	 */
	if (strcmp(ldi_ev_get_type(ecookie), LDI_EV_OFFLINE) != 0)
		return;

	/*
	 * We have already closed the LDI handle in notify.
	 * Clean up the LDI event callbacks and free vd->vdev_tsd.
	 */
	vdev_disk_free(vd);
	/*
	 * Request that the vdev be reopened if the offline state change was
	 * unsuccessful.
	 */
	if (ldi_result != LDI_EV_SUCCESS) {
		vd->vdev_probe_wanted = B_TRUE;
		spa_async_request(vd->vdev_spa, SPA_ASYNC_PROBE);
	}
}
Beispiel #2
0
static int
vdev_disk_off_notify(ldi_handle_t lh, ldi_ev_cookie_t ecookie, void *arg,
    void *ev_data)
{
	vdev_t *vd = (vdev_t *)arg;
	vdev_disk_t *dvd = vd->vdev_tsd;

	/*
	 * Ignore events other than offline.
	 */
	if (strcmp(ldi_ev_get_type(ecookie), LDI_EV_OFFLINE) != 0)
		return (LDI_EV_SUCCESS);

	/*
	 * All LDI handles must be closed for the state change to succeed, so
	 * call on vdev_disk_close() to do this.
	 *
	 * We inform vdev_disk_close that it is being called from offline
	 * notify context so it will defer cleanup of LDI event callbacks and
	 * freeing of vd->vdev_tsd to the offline finalize or a reopen.
	 */
	dvd->vd_ldi_offline = B_TRUE;
	vdev_disk_close(vd);

	/*
	 * Now that the device is closed, request that the spa_async_thread
	 * mark the device as REMOVED and notify FMA of the removal.
	 */
	zfs_post_remove(vd->vdev_spa, vd);
	vd->vdev_remove_wanted = B_TRUE;
	spa_async_request(vd->vdev_spa, SPA_ASYNC_REMOVE);

	return (LDI_EV_SUCCESS);
}
Beispiel #3
0
static void
vdev_disk_io_done(zio_t *zio)
{
	vdev_t *vd = zio->io_vd;

	zfs_zone_zio_done(zio);

	/*
	 * If the device returned EIO, then attempt a DKIOCSTATE ioctl to see if
	 * the device has been removed.  If this is the case, then we trigger an
	 * asynchronous removal of the device. Otherwise, probe the device and
	 * make sure it's still accessible.
	 */
	if (zio->io_error == EIO && !vd->vdev_remove_wanted) {
		vdev_disk_t *dvd = vd->vdev_tsd;
		int state = DKIO_NONE;

		if (ldi_ioctl(dvd->vd_lh, DKIOCSTATE, (intptr_t)&state,
		    FKIOCTL, kcred, NULL) == 0 && state != DKIO_INSERTED) {
			/*
			 * We post the resource as soon as possible, instead of
			 * when the async removal actually happens, because the
			 * DE is using this information to discard previous I/O
			 * errors.
			 */
			zfs_post_remove(zio->io_spa, vd);
			vd->vdev_remove_wanted = B_TRUE;
			spa_async_request(zio->io_spa, SPA_ASYNC_REMOVE);
		} else if (!vd->vdev_delayed_close) {
			vd->vdev_delayed_close = B_TRUE;
		}
	}
}
Beispiel #4
0
/*
 * Callback for device termination events, ie, disks removed.
 */
bool IOkit_disk_removed_callback(void* target,
								 void* refCon,
								 IOService* newService,
								 IONotifier* notifier)
{
	OSObject *prop = 0;
	OSString* bsdnameosstr = 0;

	prop = newService->getProperty(kIOBSDNameKey, gIOServicePlane,
								   kIORegistryIterateRecursively);
	if (prop) {
		spa_t *spa = NULL;

		bsdnameosstr = OSDynamicCast(OSString, prop);
		printf("ZFS: Device removal detected: '%s'\n",
			   bsdnameosstr->getCStringNoCopy());

		for (spa = spa_next(NULL);
			 spa != NULL; spa = spa_next(spa)) {
		  vdev_t *vd;

		  dprintf("ZFS: Scanning pool '%s'\n", spa_name(spa));

		  vd = vdev_lookup_by_path(spa->spa_root_vdev,
								   bsdnameosstr->getCStringNoCopy());

		  if (vd && vd->vdev_path) {

			printf("ZFS: Device '%s' removal requested\n",
				   vd->vdev_path);
			(void) thread_create(NULL, 0, vdev_close_thread,
								 vd, 0, &p0,
								 TS_RUN, minclsyspri);

			vd->vdev_remove_wanted = B_TRUE;
			spa_async_request(spa, SPA_ASYNC_REMOVE);

			break;
		  }

		} // for all spa

	} // if has BSDname

	return true;
}
Beispiel #5
0
static void
vdev_disk_io_done(zio_t *zio)
{
	vdev_t *vd = zio->io_vd;
	/*
	 * If the device returned EIO, then attempt a DKIOCSTATE ioctl to see if
	 * the device has been removed. If this is the case, then we trigger an
	 * asynchronous removal of the device.
	 */
	if (zio->io_error == EIO && !vd->vdev_remove_wanted) {

		/* Apple handle device removal in zfs_osx.cpp - read errors etc
		 * should be retried by zio
		 */
#ifdef __APPLE__
		return;
#else
		state = DKIO_NONE;
		if (ldi_ioctl(dvd->vd_lh, DKIOCSTATE, (intptr_t)&state,
					  FKIOCTL, kcred, NULL) == 0 &&
			state != DKIO_INSERTED)
			{
				/*
				 * We post the resource as soon as possible, instead of
				 * when the async removal actually happens, because the
				 * DE is using this information to discard previous I/O
				 * errors.
				 */
				zfs_post_remove(zio->io_spa, vd);
				vd->vdev_remove_wanted = B_TRUE;
				spa_async_request(zio->io_spa, SPA_ASYNC_REMOVE);
				spa_async_dispatch(zio->io_spa);
			} else if (!vd->vdev_delayed_close) {
			vd->vdev_delayed_close = B_TRUE;
		}
#endif
	}
}
Beispiel #6
0
/* ARGSUSED */
static void
dsl_scan_done(dsl_scan_t *scn, boolean_t complete, dmu_tx_t *tx)
{
	static const char *old_names[] = {
		"scrub_bookmark",
		"scrub_ddt_bookmark",
		"scrub_ddt_class_max",
		"scrub_queue",
		"scrub_min_txg",
		"scrub_max_txg",
		"scrub_func",
		"scrub_errors",
		NULL
	};

	dsl_pool_t *dp = scn->scn_dp;
	spa_t *spa = dp->dp_spa;
	int i;

	/* Remove any remnants of an old-style scrub. */
	for (i = 0; old_names[i]; i++) {
		(void) zap_remove(dp->dp_meta_objset,
		    DMU_POOL_DIRECTORY_OBJECT, old_names[i], tx);
	}

	if (scn->scn_phys.scn_queue_obj != 0) {
		VERIFY(0 == dmu_object_free(dp->dp_meta_objset,
		    scn->scn_phys.scn_queue_obj, tx));
		scn->scn_phys.scn_queue_obj = 0;
	}

	/*
	 * If we were "restarted" from a stopped state, don't bother
	 * with anything else.
	 */
	if (scn->scn_phys.scn_state != DSS_SCANNING)
		return;

	if (complete)
		scn->scn_phys.scn_state = DSS_FINISHED;
	else
		scn->scn_phys.scn_state = DSS_CANCELED;

	spa_history_log_internal(spa, "scan done", tx,
	    "complete=%u", complete);

	if (DSL_SCAN_IS_SCRUB_RESILVER(scn)) {
		mutex_enter(&spa->spa_scrub_lock);
		while (spa->spa_scrub_inflight > 0) {
			cv_wait(&spa->spa_scrub_io_cv,
			    &spa->spa_scrub_lock);
		}
		mutex_exit(&spa->spa_scrub_lock);
		spa->spa_scrub_started = B_FALSE;
		spa->spa_scrub_active = B_FALSE;

		/*
		 * If the scrub/resilver completed, update all DTLs to
		 * reflect this.  Whether it succeeded or not, vacate
		 * all temporary scrub DTLs.
		 */
		vdev_dtl_reassess(spa->spa_root_vdev, tx->tx_txg,
		    complete ? scn->scn_phys.scn_max_txg : 0, B_TRUE);
		if (complete) {
			spa_event_notify(spa, NULL, scn->scn_phys.scn_min_txg ?
			    ESC_ZFS_RESILVER_FINISH : ESC_ZFS_SCRUB_FINISH);
		}
		spa_errlog_rotate(spa);

		/*
		 * We may have finished replacing a device.
		 * Let the async thread assess this and handle the detach.
		 */
		spa_async_request(spa, SPA_ASYNC_RESILVER_DONE);
	}

	scn->scn_phys.scn_end_time = gethrestime_sec();
}
Beispiel #7
0
/*
 * Synchronize pool configuration to disk.  This must be called with the
 * namespace lock held. Synchronizing the pool cache is typically done after
 * the configuration has been synced to the MOS. This exposes a window where
 * the MOS config will have been updated but the cache file has not. If
 * the system were to crash at that instant then the cached config may not
 * contain the correct information to open the pool and an explicity import
 * would be required.
 */
void
spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent)
{
	spa_config_dirent_t *dp, *tdp;
	nvlist_t *nvl;
	boolean_t ccw_failure;
	int error;

	ASSERT(MUTEX_HELD(&spa_namespace_lock));

	if (rootdir == NULL || !(spa_mode_global & FWRITE))
		return;

	/*
	 * Iterate over all cachefiles for the pool, past or present.  When the
	 * cachefile is changed, the new one is pushed onto this list, allowing
	 * us to update previous cachefiles that no longer contain this pool.
	 */
	ccw_failure = B_FALSE;
	for (dp = list_head(&target->spa_config_list); dp != NULL;
	    dp = list_next(&target->spa_config_list, dp)) {
		spa_t *spa = NULL;
		if (dp->scd_path == NULL)
			continue;

		/*
		 * Iterate over all pools, adding any matching pools to 'nvl'.
		 */
		nvl = NULL;
		while ((spa = spa_next(spa)) != NULL) {
			nvlist_t *nvroot = NULL;
			/*
			 * Skip over our own pool if we're about to remove
			 * ourselves from the spa namespace or any pool that
			 * is readonly. Since we cannot guarantee that a
			 * readonly pool would successfully import upon reboot,
			 * we don't allow them to be written to the cache file.
			 */
			if ((spa == target && removing) ||
			    (spa_state(spa) == POOL_STATE_ACTIVE &&
			    !spa_writeable(spa)))
				continue;

			mutex_enter(&spa->spa_props_lock);
			tdp = list_head(&spa->spa_config_list);
			if (spa->spa_config == NULL ||
			    tdp->scd_path == NULL ||
			    strcmp(tdp->scd_path, dp->scd_path) != 0) {
				mutex_exit(&spa->spa_props_lock);
				continue;
			}

			if (nvl == NULL)
				VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME,
				    KM_SLEEP) == 0);

			VERIFY(nvlist_add_nvlist(nvl, spa->spa_name,
			    spa->spa_config) == 0);
			mutex_exit(&spa->spa_props_lock);

			if (nvlist_lookup_nvlist(nvl, spa->spa_name, &nvroot) == 0)
				spa_config_clean(nvroot);
		}

		error = spa_config_write(dp, nvl);
		if (error != 0)
			ccw_failure = B_TRUE;
		nvlist_free(nvl);
	}

	if (ccw_failure) {
		/*
		 * Keep trying so that configuration data is
		 * written if/when any temporary filesystem
		 * resource issues are resolved.
		 */
		if (target->spa_ccw_fail_time == 0) {
			zfs_ereport_post(FM_EREPORT_ZFS_CONFIG_CACHE_WRITE,
			    target, NULL, NULL, 0, 0);
		}
		target->spa_ccw_fail_time = gethrtime();
		spa_async_request(target, SPA_ASYNC_CONFIG_UPDATE);
	} else {
		/*
		 * Do not rate limit future attempts to update
		 * the config cache.
		 */
		target->spa_ccw_fail_time = 0;
	}

	/*
	 * Remove any config entries older than the current one.
	 */
	dp = list_head(&target->spa_config_list);
	while ((tdp = list_next(&target->spa_config_list, dp)) != NULL) {
		list_remove(&target->spa_config_list, tdp);
		if (tdp->scd_path != NULL)
			spa_strfree(tdp->scd_path);
		kmem_free(tdp, sizeof (spa_config_dirent_t));
	}

	spa_config_generation++;

	if (postsysevent)
		spa_event_notify(target, NULL, ESC_ZFS_CONFIG_SYNC);
}
Beispiel #8
0
/* ARGSUSED */
static void
dsl_pool_scrub_cancel_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
	dsl_pool_t *dp = arg1;
	boolean_t *completep = arg2;

	if (dp->dp_scrub_func == SCRUB_FUNC_NONE)
		return;

	mutex_enter(&dp->dp_scrub_cancel_lock);

	if (dp->dp_scrub_restart) {
		dp->dp_scrub_restart = B_FALSE;
		*completep = B_FALSE;
	}

	/* XXX this is scrub-clean specific */
	mutex_enter(&dp->dp_spa->spa_scrub_lock);
	while (dp->dp_spa->spa_scrub_inflight > 0) {
		cv_wait(&dp->dp_spa->spa_scrub_io_cv,
		    &dp->dp_spa->spa_scrub_lock);
	}
	mutex_exit(&dp->dp_spa->spa_scrub_lock);
	dp->dp_spa->spa_scrub_active = B_FALSE;

	dp->dp_scrub_func = SCRUB_FUNC_NONE;
	VERIFY(0 == dmu_object_free(dp->dp_meta_objset,
	    dp->dp_scrub_queue_obj, tx));
	dp->dp_scrub_queue_obj = 0;
	bzero(&dp->dp_scrub_bookmark, sizeof (zbookmark_t));
	bzero(&dp->dp_scrub_ddt_bookmark, sizeof (ddt_bookmark_t));

	VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
	    DMU_POOL_SCRUB_QUEUE, tx));
	VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
	    DMU_POOL_SCRUB_MIN_TXG, tx));
	VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
	    DMU_POOL_SCRUB_MAX_TXG, tx));
	VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
	    DMU_POOL_SCRUB_BOOKMARK, tx));
	VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
	    DMU_POOL_SCRUB_FUNC, tx));
	VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
	    DMU_POOL_SCRUB_ERRORS, tx));

	(void) zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
	    DMU_POOL_SCRUB_DDT_BOOKMARK, tx);
	(void) zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
	    DMU_POOL_SCRUB_DDT_CLASS_MAX, tx);

	spa_history_internal_log(LOG_POOL_SCRUB_DONE, dp->dp_spa, tx, cr,
	    "complete=%u", *completep);

	/* below is scrub-clean specific */
	vdev_scrub_stat_update(dp->dp_spa->spa_root_vdev, POOL_SCRUB_NONE,
	    *completep);
	/*
	 * If the scrub/resilver completed, update all DTLs to reflect this.
	 * Whether it succeeded or not, vacate all temporary scrub DTLs.
	 */
	vdev_dtl_reassess(dp->dp_spa->spa_root_vdev, tx->tx_txg,
	    *completep ? dp->dp_scrub_max_txg : 0, B_TRUE);
	dp->dp_spa->spa_scrub_started = B_FALSE;
	if (*completep)
		spa_event_notify(dp->dp_spa, NULL, dp->dp_scrub_min_txg ?
		    ESC_ZFS_RESILVER_FINISH : ESC_ZFS_SCRUB_FINISH);
	spa_errlog_rotate(dp->dp_spa);

	/*
	 * We may have finished replacing a device.
	 * Let the async thread assess this and handle the detach.
	 */
	spa_async_request(dp->dp_spa, SPA_ASYNC_RESILVER_DONE);

	dp->dp_scrub_min_txg = dp->dp_scrub_max_txg = 0;
	mutex_exit(&dp->dp_scrub_cancel_lock);
}