Example #1
0
void
taskq_destroy(taskq_t *tq)
{
	int t;
	int nthreads = tq->tq_nthreads;

	taskq_wait(tq);

	mutex_enter(&tq->tq_lock);

	tq->tq_flags &= ~TASKQ_ACTIVE;
	cv_broadcast(&tq->tq_dispatch_cv);

	while (tq->tq_nthreads != 0)
		cv_wait(&tq->tq_wait_cv, &tq->tq_lock);

	tq->tq_minalloc = 0;
	while (tq->tq_nalloc != 0) {
		ASSERT(tq->tq_freelist != NULL);
		task_free(tq, task_alloc(tq, KM_SLEEP));
	}

	mutex_exit(&tq->tq_lock);

	for (t = 0; t < nthreads; t++)
		(void) thr_join(tq->tq_threadlist[t], NULL, NULL);

	kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t));

	rw_destroy(&tq->tq_threadlock);

	kmem_free(tq, sizeof (taskq_t));
}
Example #2
0
/*
 * Wait for pending commit callbacks of already-synced transactions to finish
 * processing.
 * Calling this function from within a commit callback will deadlock.
 */
void
txg_wait_callbacks(dsl_pool_t *dp)
{
	tx_state_t *tx = &dp->dp_tx;

	if (tx->tx_commit_cb_taskq != NULL)
		taskq_wait(tx->tx_commit_cb_taskq);
}
Example #3
0
static int
splat_rwlock_test2(struct file *file, void *arg)
{
	rw_priv_t *rwp;
	taskq_t *tq;
	int i, rc = 0, tq_count = 256;

	rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
	if (rwp == NULL)
		return -ENOMEM;

	splat_init_rw_priv(rwp, file);

	/* Create several threads allowing tasks to race with each other */
	tq = taskq_create(SPLAT_RWLOCK_TEST_TASKQ, num_online_cpus(),
			  maxclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE);
	if (tq == NULL) {
		rc = -ENOMEM;
		goto out;
	}

	/*
	 * Schedule N work items to the work queue each of which enters the
	 * writer rwlock, sleeps briefly, then exits the writer rwlock.  On a
	 * multiprocessor box these work items will be handled by all available
	 * CPUs.  The task function checks to ensure the tracked shared variable
	 * is always only incremented by one.  Additionally, the rwlock itself
	 * is instrumented such that if any two processors are in the
	 * critical region at the same time the system will panic.  If the
	 * rwlock is implemented right this will never happy, that's a pass.
	 */
	for (i = 0; i < tq_count; i++) {
		if (!taskq_dispatch(tq,splat_rwlock_test2_func,rwp,TQ_SLEEP)) {
			splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME,
				     "Failed to queue task %d\n", i);
			rc = -EINVAL;
		}
	}

	taskq_wait(tq);

	if (rwp->rw_rc == tq_count) {
		splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads "
			     "correctly entered/exited the rwlock %d times\n",
			     num_online_cpus(), rwp->rw_rc);
	} else {
		splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads "
			     "only processed %d/%d w rwlock work items\n",
			     num_online_cpus(), rwp->rw_rc, tq_count);
		rc = -EINVAL;
	}

	taskq_destroy(tq);
	rw_destroy(&(rwp->rw_rwlock));
out:
	kfree(rwp);
	return rc;
}
Example #4
0
static int
splat_taskq_test1_impl(struct file *file, void *arg, boolean_t prealloc)
{
	taskq_t *tq;
	taskqid_t id;
	splat_taskq_arg_t tq_arg;
	taskq_ent_t tqe;

	taskq_init_ent(&tqe);

	splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
		     "Taskq '%s' creating (%s dispatch)\n",
	             SPLAT_TASKQ_TEST1_NAME,
		     prealloc ? "prealloc" : "dynamic");
	if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, maxclsyspri,
			       50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
		splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
		           "Taskq '%s' create failed\n",
		           SPLAT_TASKQ_TEST1_NAME);
		return -EINVAL;
	}

	tq_arg.flag = 0;
	tq_arg.id   = 0;
	tq_arg.file = file;
	tq_arg.name = SPLAT_TASKQ_TEST1_NAME;

	splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
	           "Taskq '%s' function '%s' dispatching\n",
	           tq_arg.name, sym2str(splat_taskq_test13_func));
	if (prealloc) {
		taskq_dispatch_ent(tq, splat_taskq_test13_func,
		                   &tq_arg, TQ_SLEEP, &tqe);
		id = tqe.tqent_id;
	} else {
		id = taskq_dispatch(tq, splat_taskq_test13_func,
				    &tq_arg, TQ_SLEEP);
	}

	if (id == 0) {
		splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
		             "Taskq '%s' function '%s' dispatch failed\n",
		             tq_arg.name, sym2str(splat_taskq_test13_func));
		taskq_destroy(tq);
		return -EINVAL;
	}

	splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n",
	           tq_arg.name);
	taskq_wait(tq);
	splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n",
	           tq_arg.name);

	taskq_destroy(tq);

	return (tq_arg.flag) ? 0 : -EINVAL;
}
Example #5
0
/*
 * Simple algorithm for now, grab the global lock and let all
 * the clients update themselves in parallel. There is a lot of
 * room for improvement here. We could eliminate some scans of
 * the DAG by incrementally scanning at lower levels of the DAG
 * rather than having each client start its own scan from the root.
 */
void
mdeg_notify_clients(void)
{
	md_t		*md_new;
	mdeg_clnt_t	*clnt;
	int		idx;
	int		nclnt;

	rw_enter(&mdeg.rwlock, RW_READER);
	mutex_enter(&mdeg.lock);

	/*
	 * Rotate the MDs
	 */
	if ((md_new = md_get_handle()) == NULL) {
		cmn_err(CE_WARN, "unable to retrieve new MD");
		goto done;
	}

	if (mdeg.md_prev) {
		(void) md_fini_handle(mdeg.md_prev);
	}

	mdeg.md_prev = mdeg.md_curr;
	mdeg.md_curr = md_new;

	if (mdeg.nclnts == 0) {
		MDEG_DBG("mdeg_notify_clients: no clients registered\n");
		goto done;
	}

	/* dispatch the update notification to all clients */
	for (idx = 0, nclnt = 0; idx < mdeg.maxclnts; idx++) {
		clnt = &mdeg.tbl[idx];

		if (!clnt->valid)
			continue;

		MDEG_DBG("notifying client 0x%lx (%d/%d)\n", clnt->hdl,
		    ++nclnt, mdeg.nclnts);

		(void) taskq_dispatch(mdeg.taskq, mdeg_notify_client,
		    (void *)clnt, TQ_SLEEP);
	}

	/*
	 * Wait for all mdeg_notify_client notifications to
	 * finish while we are still holding mdeg.rwlock.
	 */
	taskq_wait(mdeg.taskq);

done:
	mutex_exit(&mdeg.lock);
	rw_exit(&mdeg.rwlock);
}
Example #6
0
/*
 * Use the global system task queue with a single task, wait until task
 * completes, ensure task ran properly.
 */
static int
splat_taskq_test3_impl(struct file *file, void *arg, boolean_t prealloc)
{
	taskqid_t id;
	splat_taskq_arg_t *tq_arg;
	taskq_ent_t *tqe;
	int error;

	tq_arg = kmem_alloc(sizeof (splat_taskq_arg_t), KM_SLEEP);
	tqe = kmem_alloc(sizeof (taskq_ent_t), KM_SLEEP);
	taskq_init_ent(tqe);

	tq_arg->flag = 0;
	tq_arg->id   = 0;
	tq_arg->file = file;
	tq_arg->name = SPLAT_TASKQ_TEST3_NAME;

	splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
	           "Taskq '%s' function '%s' %s dispatch\n",
	           tq_arg->name, sym2str(splat_taskq_test13_func),
		   prealloc ? "prealloc" : "dynamic");
	if (prealloc) {
		taskq_dispatch_ent(system_taskq, splat_taskq_test13_func,
		                   tq_arg, TQ_SLEEP, tqe);
		id = tqe->tqent_id;
	} else {
		id = taskq_dispatch(system_taskq, splat_taskq_test13_func,
				    tq_arg, TQ_SLEEP);
	}

	if (id == 0) {
		splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
		           "Taskq '%s' function '%s' dispatch failed\n",
		           tq_arg->name, sym2str(splat_taskq_test13_func));
		kmem_free(tqe, sizeof (taskq_ent_t));
		kmem_free(tq_arg, sizeof (splat_taskq_arg_t));
		return -EINVAL;
	}

	splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n",
	           tq_arg->name);
	taskq_wait(system_taskq);

	error = (tq_arg->flag) ? 0 : -EINVAL;

	kmem_free(tqe, sizeof (taskq_ent_t));
	kmem_free(tq_arg, sizeof (splat_taskq_arg_t));

	return (error);
}
Example #7
0
/*
 * Use the global system task queue with a single task, wait until task
 * completes, ensure task ran properly.
 */
static int
splat_taskq_test3_impl(struct file *file, void *arg, boolean_t prealloc)
{
	taskqid_t id;
	splat_taskq_arg_t tq_arg;
	taskq_ent_t tqe;

	taskq_init_ent(&tqe);

	tq_arg.flag = 0;
	tq_arg.id   = 0;
	tq_arg.file = file;
	tq_arg.name = SPLAT_TASKQ_TEST3_NAME;

	splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
	           "Taskq '%s' function '%s' %s dispatch\n",
	           tq_arg.name, sym2str(splat_taskq_test13_func),
		   prealloc ? "prealloc" : "dynamic");
	if (prealloc) {
		taskq_dispatch_ent(system_taskq, splat_taskq_test13_func,
		                   &tq_arg, TQ_SLEEP, &tqe);
		id = tqe.tqent_id;
	} else {
		id = taskq_dispatch(system_taskq, splat_taskq_test13_func,
				    &tq_arg, TQ_SLEEP);
	}

	if (id == 0) {
		splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
		           "Taskq '%s' function '%s' dispatch failed\n",
		           tq_arg.name, sym2str(splat_taskq_test13_func));
		return -EINVAL;
	}

	splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n",
	           tq_arg.name);
	taskq_wait(system_taskq);

	return (tq_arg.flag) ? 0 : -EINVAL;
}
Example #8
0
void
taskq_destroy(taskq_t *tq)
{
	int t;
	int nthreads = tq->tq_nthreads;

	taskq_wait(tq);

	mxlock(&tq->tq_lock);

	tq->tq_flags &= ~TASKQ_ACTIVE;
	condbcast(&tq->tq_dispatch_cv);

	while (tq->tq_nthreads != 0)
		condwait(&tq->tq_wait_cv, &tq->tq_lock);

	tq->tq_minalloc = 0;
	while (tq->tq_nalloc != 0) {
		assert(tq->tq_freelist != NULL);
		task_free(tq, task_alloc(tq, UMEM_NOFAIL));
	}

	mxunlock(&tq->tq_lock);

	for (t = 0; t < nthreads; t++)
		pthread_join(tq->tq_threadlist[t], NULL);

	umem_free(tq->tq_threadlist, nthreads * sizeof (pthread_t));

	rwdestroy(&tq->tq_threadlock);
	mxdestroy(&tq->tq_lock);
	conddestroy(&tq->tq_dispatch_cv);
	conddestroy(&tq->tq_wait_cv);
	conddestroy(&tq->tq_maxalloc_cv);

	umem_free(tq, sizeof (taskq_t));
}
Example #9
0
/*
 * Teardown the zfs_sb_t.
 *
 * Note, if 'unmounting' if FALSE, we return with the 'z_teardown_lock'
 * and 'z_teardown_inactive_lock' held.
 */
int
zfs_sb_teardown(zfs_sb_t *zsb, boolean_t unmounting)
{
	znode_t	*zp;

	rrw_enter(&zsb->z_teardown_lock, RW_WRITER, FTAG);

	if (!unmounting) {
		/*
		 * We purge the parent filesystem's super block as the
		 * parent filesystem and all of its snapshots have their
		 * inode's super block set to the parent's filesystem's
		 * super block.  Note,  'z_parent' is self referential
		 * for non-snapshots.
		 */
		shrink_dcache_sb(zsb->z_parent->z_sb);
	}

	/*
	 * If someone has not already unmounted this file system,
	 * drain the iput_taskq to ensure all active references to the
	 * zfs_sb_t have been handled only then can it be safely destroyed.
	 */
	if (zsb->z_os)
		taskq_wait(dsl_pool_iput_taskq(dmu_objset_pool(zsb->z_os)));

	/*
	 * Close the zil. NB: Can't close the zil while zfs_inactive
	 * threads are blocked as zil_close can call zfs_inactive.
	 */
	if (zsb->z_log) {
		zil_close(zsb->z_log);
		zsb->z_log = NULL;
	}

	rw_enter(&zsb->z_teardown_inactive_lock, RW_WRITER);

	/*
	 * If we are not unmounting (ie: online recv) and someone already
	 * unmounted this file system while we were doing the switcheroo,
	 * or a reopen of z_os failed then just bail out now.
	 */
	if (!unmounting && (zsb->z_unmounted || zsb->z_os == NULL)) {
		rw_exit(&zsb->z_teardown_inactive_lock);
		rrw_exit(&zsb->z_teardown_lock, FTAG);
		return (EIO);
	}

	/*
	 * At this point there are no VFS ops active, and any new VFS ops
	 * will fail with EIO since we have z_teardown_lock for writer (only
	 * relevant for forced unmount).
	 *
	 * Release all holds on dbufs.
	 */
	mutex_enter(&zsb->z_znodes_lock);
	for (zp = list_head(&zsb->z_all_znodes); zp != NULL;
	    zp = list_next(&zsb->z_all_znodes, zp)) {
		if (zp->z_sa_hdl) {
			ASSERT(atomic_read(&ZTOI(zp)->i_count) > 0);
			zfs_znode_dmu_fini(zp);
		}
	}
	mutex_exit(&zsb->z_znodes_lock);

	/*
	 * If we are unmounting, set the unmounted flag and let new VFS ops
	 * unblock.  zfs_inactive will have the unmounted behavior, and all
	 * other VFS ops will fail with EIO.
	 */
	if (unmounting) {
		zsb->z_unmounted = B_TRUE;
		rrw_exit(&zsb->z_teardown_lock, FTAG);
		rw_exit(&zsb->z_teardown_inactive_lock);
	}

	/*
	 * z_os will be NULL if there was an error in attempting to reopen
	 * zsb, so just return as the properties had already been
	 *
	 * unregistered and cached data had been evicted before.
	 */
	if (zsb->z_os == NULL)
		return (0);

	/*
	 * Unregister properties.
	 */
	zfs_unregister_callbacks(zsb);

	/*
	 * Evict cached data
	 */
	if (dsl_dataset_is_dirty(dmu_objset_ds(zsb->z_os)) &&
	    !zfs_is_readonly(zsb))
		txg_wait_synced(dmu_objset_pool(zsb->z_os), 0);
	dmu_objset_evict_dbufs(zsb->z_os);

	return (0);
}
Example #10
0
static int
splat_taskq_test4_common(struct file *file, void *arg, int minalloc,
                         int maxalloc, int nr_tasks, boolean_t prealloc)
{
	taskq_t *tq;
	taskqid_t id;
	splat_taskq_arg_t tq_arg;
	taskq_ent_t *tqes;
	int i, j, rc = 0;

	tqes = kmalloc(sizeof(*tqes) * nr_tasks, GFP_KERNEL);
	if (tqes == NULL)
		return -ENOMEM;

	splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
		     "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n",
		     SPLAT_TASKQ_TEST4_NAME,
		     prealloc ? "prealloc" : "dynamic",
		     minalloc, maxalloc, nr_tasks);
	if ((tq = taskq_create(SPLAT_TASKQ_TEST4_NAME, 1, maxclsyspri,
		               minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
		splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
		             "Taskq '%s' create failed\n",
		             SPLAT_TASKQ_TEST4_NAME);
		rc = -EINVAL;
		goto out_free;
	}

	tq_arg.file = file;
	tq_arg.name = SPLAT_TASKQ_TEST4_NAME;

	for (i = 1; i <= nr_tasks; i *= 2) {
		atomic_set(&tq_arg.count, 0);
		splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
		             "Taskq '%s' function '%s' dispatched %d times\n",
		             tq_arg.name, sym2str(splat_taskq_test4_func), i);

		for (j = 0; j < i; j++) {
			taskq_init_ent(&tqes[j]);

			if (prealloc) {
				taskq_dispatch_ent(tq, splat_taskq_test4_func,
				                   &tq_arg, TQ_SLEEP, &tqes[j]);
				id = tqes[j].tqent_id;
			} else {
				id = taskq_dispatch(tq, splat_taskq_test4_func,
						    &tq_arg, TQ_SLEEP);
			}

			if (id == 0) {
				splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
				        "Taskq '%s' function '%s' dispatch "
					"%d failed\n", tq_arg.name,
					sym2str(splat_taskq_test4_func), j);
					rc = -EINVAL;
					goto out;
			}
		}

		splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
			     "waiting for %d dispatches\n", tq_arg.name, i);
		taskq_wait(tq);
		splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
			     "%d/%d dispatches finished\n", tq_arg.name,
			     atomic_read(&tq_arg.count), i);
		if (atomic_read(&tq_arg.count) != i) {
			rc = -ERANGE;
			goto out;

		}
	}
out:
	splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' destroying\n",
	           tq_arg.name);
	taskq_destroy(tq);

out_free:
	kfree(tqes);

	return rc;
}
Example #11
0
static int
splat_taskq_test2_impl(struct file *file, void *arg, boolean_t prealloc) {
	taskq_t *tq[TEST2_TASKQS] = { NULL };
	taskqid_t id;
	splat_taskq_arg_t tq_args[TEST2_TASKQS];
	taskq_ent_t *func1_tqes = NULL;
	taskq_ent_t *func2_tqes = NULL;
	int i, rc = 0;

	func1_tqes = kmalloc(sizeof(*func1_tqes) * TEST2_TASKQS, GFP_KERNEL);
	if (func1_tqes == NULL) {
		rc = -ENOMEM;
		goto out;
	}

	func2_tqes = kmalloc(sizeof(*func2_tqes) * TEST2_TASKQS, GFP_KERNEL);
	if (func2_tqes == NULL) {
		rc = -ENOMEM;
		goto out;
	}

	for (i = 0; i < TEST2_TASKQS; i++) {
		taskq_init_ent(&func1_tqes[i]);
		taskq_init_ent(&func2_tqes[i]);

		splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
			     "Taskq '%s/%d' creating (%s dispatch)\n",
			     SPLAT_TASKQ_TEST2_NAME, i,
			     prealloc ? "prealloc" : "dynamic");
		if ((tq[i] = taskq_create(SPLAT_TASKQ_TEST2_NAME,
			                  TEST2_THREADS_PER_TASKQ,
					  maxclsyspri, 50, INT_MAX,
					  TASKQ_PREPOPULATE)) == NULL) {
			splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
			           "Taskq '%s/%d' create failed\n",
				   SPLAT_TASKQ_TEST2_NAME, i);
			rc = -EINVAL;
			break;
		}

		tq_args[i].flag = i;
		tq_args[i].id   = i;
		tq_args[i].file = file;
		tq_args[i].name = SPLAT_TASKQ_TEST2_NAME;

		splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
		           "Taskq '%s/%d' function '%s' dispatching\n",
			   tq_args[i].name, tq_args[i].id,
		           sym2str(splat_taskq_test2_func1));
		if (prealloc) {
			taskq_dispatch_ent(tq[i], splat_taskq_test2_func1,
			                 &tq_args[i], TQ_SLEEP, &func1_tqes[i]);
			id = func1_tqes[i].tqent_id;
		} else {
			id = taskq_dispatch(tq[i], splat_taskq_test2_func1,
					    &tq_args[i], TQ_SLEEP);
		}

		if (id == 0) {
			splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
			           "Taskq '%s/%d' function '%s' dispatch "
			           "failed\n", tq_args[i].name, tq_args[i].id,
			           sym2str(splat_taskq_test2_func1));
			rc = -EINVAL;
			break;
		}

		splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
		           "Taskq '%s/%d' function '%s' dispatching\n",
			   tq_args[i].name, tq_args[i].id,
		           sym2str(splat_taskq_test2_func2));
		if (prealloc) {
			taskq_dispatch_ent(tq[i], splat_taskq_test2_func2,
			                &tq_args[i], TQ_SLEEP, &func2_tqes[i]);
			id = func2_tqes[i].tqent_id;
		} else {
			id = taskq_dispatch(tq[i], splat_taskq_test2_func2,
			                    &tq_args[i], TQ_SLEEP);
		}

		if (id == 0) {
			splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, "Taskq "
				     "'%s/%d' function '%s' dispatch failed\n",
			             tq_args[i].name, tq_args[i].id,
			             sym2str(splat_taskq_test2_func2));
			rc = -EINVAL;
			break;
		}
	}

	/* When rc is set we're effectively just doing cleanup here, so
	 * ignore new errors in that case.  They just cause noise. */
	for (i = 0; i < TEST2_TASKQS; i++) {
		if (tq[i] != NULL) {
			splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
			           "Taskq '%s/%d' waiting\n",
			           tq_args[i].name, tq_args[i].id);
			taskq_wait(tq[i]);
			splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
			           "Taskq '%s/%d; destroying\n",
			          tq_args[i].name, tq_args[i].id);

			taskq_destroy(tq[i]);

			if (!rc && tq_args[i].flag != ((i * 2) + 1)) {
				splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
				           "Taskq '%s/%d' processed tasks "
				           "out of order; %d != %d\n",
				           tq_args[i].name, tq_args[i].id,
				           tq_args[i].flag, i * 2 + 1);
				rc = -EINVAL;
			} else {
				splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
				           "Taskq '%s/%d' processed tasks "
					   "in the correct order; %d == %d\n",
				           tq_args[i].name, tq_args[i].id,
				           tq_args[i].flag, i * 2 + 1);
			}
		}
	}
out:
	if (func1_tqes)
		kfree(func1_tqes);

	if (func2_tqes)
		kfree(func2_tqes);

	return rc;
}
Example #12
0
static int
splat_taskq_test8_common(struct file *file, void *arg, int minalloc,
                         int maxalloc)
{
	taskq_t *tq;
	taskqid_t id;
	splat_taskq_arg_t tq_arg;
	taskq_ent_t **tqes;
	int i, j, rc = 0;

	tqes = vmalloc(sizeof(*tqes) * TEST8_NUM_TASKS);
	if (tqes == NULL)
		return -ENOMEM;
	memset(tqes, 0, sizeof(*tqes) * TEST8_NUM_TASKS);

	splat_vprint(file, SPLAT_TASKQ_TEST8_NAME,
		     "Taskq '%s' creating (%d/%d/%d)\n",
		     SPLAT_TASKQ_TEST8_NAME,
		     minalloc, maxalloc, TEST8_NUM_TASKS);
	if ((tq = taskq_create(SPLAT_TASKQ_TEST8_NAME, TEST8_THREADS_PER_TASKQ,
			       maxclsyspri, minalloc, maxalloc,
			       TASKQ_PREPOPULATE)) == NULL) {
		splat_vprint(file, SPLAT_TASKQ_TEST8_NAME,
		             "Taskq '%s' create failed\n",
		             SPLAT_TASKQ_TEST8_NAME);
		rc = -EINVAL;
		goto out_free;
	}

	tq_arg.file = file;
	tq_arg.name = SPLAT_TASKQ_TEST8_NAME;

	atomic_set(&tq_arg.count, 0);
	for (i = 0; i < TEST8_NUM_TASKS; i++) {
		tqes[i] = kmalloc(sizeof(taskq_ent_t), GFP_KERNEL);
		if (tqes[i] == NULL) {
			rc = -ENOMEM;
			goto out;
		}
		taskq_init_ent(tqes[i]);

		taskq_dispatch_ent(tq, splat_taskq_test8_func,
				   &tq_arg, TQ_SLEEP, tqes[i]);

		id = tqes[i]->tqent_id;

		if (id == 0) {
			splat_vprint(file, SPLAT_TASKQ_TEST8_NAME,
			        "Taskq '%s' function '%s' dispatch "
				"%d failed\n", tq_arg.name,
				sym2str(splat_taskq_test8_func), i);
				rc = -EINVAL;
				goto out;
		}
	}

	splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' "
		     "waiting for %d dispatches\n", tq_arg.name,
		     TEST8_NUM_TASKS);
	taskq_wait(tq);
	splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' "
		     "%d/%d dispatches finished\n", tq_arg.name,
		     atomic_read(&tq_arg.count), TEST8_NUM_TASKS);

	if (atomic_read(&tq_arg.count) != TEST8_NUM_TASKS)
		rc = -ERANGE;

out:
	splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' destroying\n",
	           tq_arg.name);
	taskq_destroy(tq);
out_free:
	for (j = 0; j < TEST8_NUM_TASKS && tqes[j] != NULL; j++)
		kfree(tqes[j]);
	vfree(tqes);

	return rc;
}
Example #13
0
static int
splat_mutex_test3(struct file *file, void *arg)
{
        mutex_priv_t mp;
        taskq_t *tq;
        int rc = 0;

        mp.mp_magic = SPLAT_MUTEX_TEST_MAGIC;
        mp.mp_file = file;
        mutex_init(&mp.mp_mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);

        if ((tq = taskq_create(SPLAT_MUTEX_TEST_NAME, 1, defclsyspri,
                               50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
                splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Taskq '%s' "
                             "create failed\n", SPLAT_MUTEX_TEST3_NAME);
                return -EINVAL;
        }

        mutex_enter(&mp.mp_mtx);

        /* Mutex should be owned by current */
        if (!mutex_owned(&mp.mp_mtx)) {
                splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Unowned mutex "
                             "should be owned by pid %d\n", current->pid);
                rc = -EINVAL;
                goto out_exit;
        }

        if (taskq_dispatch(tq, splat_mutex_owned, &mp, TQ_SLEEP) == 0) {
                splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Failed to "
                             "dispatch function '%s' to taskq\n",
                             sym2str(splat_mutex_owned));
                rc = -EINVAL;
                goto out_exit;
        }
        taskq_wait(tq);

        /* Mutex should not be owned which checked from a different thread */
        if (mp.mp_rc || mp.mp_rc2) {
                splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by "
                             "pid %d not by taskq\n", current->pid);
                rc = -EINVAL;
                goto out_exit;
        }

        mutex_exit(&mp.mp_mtx);

        /* Mutex should not be owned by current */
        if (mutex_owned(&mp.mp_mtx)) {
                splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by "
                             "pid %d it should be unowned\b", current->pid);
                rc = -EINVAL;
                goto out;
        }

        if (taskq_dispatch(tq, splat_mutex_owned, &mp, TQ_SLEEP) == 0) {
                splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Failed to "
                             "dispatch function '%s' to taskq\n",
                             sym2str(splat_mutex_owned));
                rc = -EINVAL;
                goto out;
        }
        taskq_wait(tq);

        /* Mutex should be owned by no one */
        if (mp.mp_rc || mp.mp_rc2) {
                splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by "
                             "no one, %d/%d disagrees\n", mp.mp_rc, mp.mp_rc2);
                rc = -EINVAL;
                goto out;
        }

        splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s",
                   "Correct mutex_owned() behavior\n");
        goto out;
out_exit:
        mutex_exit(&mp.mp_mtx);
out:
        mutex_destroy(&mp.mp_mtx);
        taskq_destroy(tq);

        return rc;
}
Example #14
0
static int
splat_taskq_throughput(struct file *file, void *arg, const char *name,
    int nthreads, int minalloc, int maxalloc, int flags, int tasks,
    struct timespec *delta)
{
	taskq_t *tq;
	taskqid_t id;
	splat_taskq_arg_t tq_arg;
	taskq_ent_t **tqes;
	atomic_t count;
	struct timespec start, stop;
	int i, j, rc = 0;

	tqes = vmalloc(sizeof (*tqes) * tasks);
	if (tqes == NULL)
		return (-ENOMEM);

	memset(tqes, 0, sizeof (*tqes) * tasks);

	splat_vprint(file, name, "Taskq '%s' creating (%d/%d/%d/%d)\n",
	    name, nthreads, minalloc, maxalloc, tasks);
	if ((tq = taskq_create(name, nthreads, maxclsyspri,
	    minalloc, maxalloc, flags)) == NULL) {
		splat_vprint(file, name, "Taskq '%s' create failed\n", name);
		rc = -EINVAL;
		goto out_free;
	}

	tq_arg.file = file;
	tq_arg.name = name;
	tq_arg.count = &count;
	atomic_set(tq_arg.count, 0);

	getnstimeofday(&start);

	for (i = 0; i < tasks; i++) {
		tqes[i] = kmalloc(sizeof (taskq_ent_t), GFP_KERNEL);
		if (tqes[i] == NULL) {
			rc = -ENOMEM;
			goto out;
		}

		taskq_init_ent(tqes[i]);
		taskq_dispatch_ent(tq, splat_taskq_throughput_func,
		    &tq_arg, TQ_SLEEP, tqes[i]);
		id = tqes[i]->tqent_id;

		if (id == 0) {
			splat_vprint(file, name, "Taskq '%s' function '%s' "
			    "dispatch %d failed\n", tq_arg.name,
			    sym2str(splat_taskq_throughput_func), i);
			rc = -EINVAL;
			goto out;
		}
	}

	splat_vprint(file, name, "Taskq '%s' waiting for %d dispatches\n",
	    tq_arg.name, tasks);

	taskq_wait(tq);

	if (delta != NULL) {
		getnstimeofday(&stop);
		*delta = timespec_sub(stop, start);
	}

	splat_vprint(file, name, "Taskq '%s' %d/%d dispatches finished\n",
	    tq_arg.name, atomic_read(tq_arg.count), tasks);

	if (atomic_read(tq_arg.count) != tasks)
		rc = -ERANGE;

out:
	splat_vprint(file, name, "Taskq '%s' destroying\n", tq_arg.name);
	taskq_destroy(tq);
out_free:
	for (j = 0; j < tasks && tqes[j] != NULL; j++)
		kfree(tqes[j]);

	vfree(tqes);

	return (rc);
}
Example #15
0
static int
splat_mutex_test2(struct file *file, void *arg)
{
        mutex_priv_t *mp;
        taskq_t *tq;
        int i, rc = 0;

        mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
        if (mp == NULL)
                return -ENOMEM;

        /* Create several threads allowing tasks to race with each other */
        tq = taskq_create(SPLAT_MUTEX_TEST_TASKQ, num_online_cpus(),
                          defclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE);
        if (tq == NULL) {
                rc = -ENOMEM;
                goto out;
        }

        mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC;
        mp->mp_file = file;
        mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
        mp->mp_rc = 0;

        /*
         * Schedule N work items to the work queue each of which enters the
         * mutex, sleeps briefly, then exits the mutex.  On a multiprocessor
         * box these work items will be handled by all available CPUs.  The
         * task function checks to ensure the tracked shared variable is
         * always only incremented by one.  Additionally, the mutex itself
         * is instrumented such that if any two processors are in the
         * critical region at the same time the system will panic.  If the
         * mutex is implemented right this will never happy, that's a pass.
         */
        for (i = 0; i < SPLAT_MUTEX_TEST_COUNT; i++) {
                if (!taskq_dispatch(tq, splat_mutex_test2_func, mp, TQ_SLEEP)) {
                        splat_vprint(file, SPLAT_MUTEX_TEST2_NAME,
                                     "Failed to queue task %d\n", i);
                        rc = -EINVAL;
                }
        }

        taskq_wait(tq);

        if (mp->mp_rc == SPLAT_MUTEX_TEST_COUNT) {
                splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads "
                           "correctly entered/exited the mutex %d times\n",
                           num_online_cpus(), mp->mp_rc);
        } else {
                splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads "
                           "only processed %d/%d mutex work items\n",
                           num_online_cpus(),mp->mp_rc,SPLAT_MUTEX_TEST_COUNT);
                rc = -EINVAL;
        }

        taskq_destroy(tq);
        mutex_destroy(&(mp->mp_mtx));
out:
        kfree(mp);
        return rc;
}
Example #16
0
static int
splat_taskq_test9(struct file *file, void *arg)
{
	taskq_t *tq;
	atomic_t count;
	int i, rc = 0;
	int minalloc = 1;
	int maxalloc = 10;
	int nr_tasks = 100;

	splat_vprint(file, SPLAT_TASKQ_TEST9_NAME,
	    "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n",
	    SPLAT_TASKQ_TEST9_NAME, "delay", minalloc, maxalloc, nr_tasks);
	if ((tq = taskq_create(SPLAT_TASKQ_TEST9_NAME, 3, maxclsyspri,
	    minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
		splat_vprint(file, SPLAT_TASKQ_TEST9_NAME,
		    "Taskq '%s' create failed\n", SPLAT_TASKQ_TEST9_NAME);
		return -EINVAL;
	}

	atomic_set(&count, 0);

	for (i = 1; i <= nr_tasks; i++) {
		splat_taskq_arg_t *tq_arg;
		taskqid_t id;
		uint32_t rnd;

		/* A random timeout in jiffies of at most 5 seconds */
		get_random_bytes((void *)&rnd, 4);
		rnd = rnd % (5 * HZ);

		tq_arg = kmem_alloc(sizeof(splat_taskq_arg_t), KM_SLEEP);
		tq_arg->file = file;
		tq_arg->name = SPLAT_TASKQ_TEST9_NAME;
		tq_arg->expire = ddi_get_lbolt() + rnd;
		tq_arg->count = &count;

		splat_vprint(file, SPLAT_TASKQ_TEST9_NAME,
		    "Taskq '%s' delay dispatch %u jiffies\n",
		    SPLAT_TASKQ_TEST9_NAME, rnd);

		id = taskq_dispatch_delay(tq, splat_taskq_test9_func,
		    tq_arg, TQ_SLEEP, ddi_get_lbolt() + rnd);

		if (id == 0) {
			splat_vprint(file, SPLAT_TASKQ_TEST9_NAME,
			   "Taskq '%s' delay dispatch failed\n",
			   SPLAT_TASKQ_TEST9_NAME);
			kmem_free(tq_arg, sizeof(splat_taskq_arg_t));
			taskq_wait(tq);
			rc = -EINVAL;
			goto out;
		}
	}

	splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, "Taskq '%s' waiting for "
	    "%d delay dispatches\n", SPLAT_TASKQ_TEST9_NAME, nr_tasks);

	taskq_wait(tq);
	if (atomic_read(&count) != nr_tasks)
		rc = -ERANGE;

	splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, "Taskq '%s' %d/%d delay "
	    "dispatches finished on time\n", SPLAT_TASKQ_TEST9_NAME,
	    atomic_read(&count), nr_tasks);
	splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, "Taskq '%s' destroying\n",
	    SPLAT_TASKQ_TEST9_NAME);
out:
	taskq_destroy(tq);

	return rc;
}
Example #17
0
File: taskq.c Project: AB17/zfs
void
taskq_wait_id(taskq_t *tq, taskqid_t id)
{
	taskq_wait(tq);
}
Example #18
0
/*
 * taskq_destroy().
 *
 * Assumes: by the time taskq_destroy is called no one will use this task queue
 * in any way and no one will try to dispatch entries in it.
 */
void
taskq_destroy(taskq_t *tq)
{
    taskq_bucket_t *b = tq->tq_buckets;
    int bid = 0;

    ASSERT(! (tq->tq_flags & TASKQ_CPR_SAFE));

    /*
     * Wait for any pending entries to complete.
     */
    taskq_wait(tq);

    mutex_enter(&tq->tq_lock);
    ASSERT((tq->tq_task.tqent_next == &tq->tq_task) &&
           (tq->tq_active == 0));

    if ((tq->tq_nthreads > 1) && (tq->tq_threadlist != NULL))
        kmem_free(tq->tq_threadlist, sizeof (kthread_t *) *
                  tq->tq_nthreads);

    tq->tq_flags &= ~TASKQ_ACTIVE;
    cv_broadcast(&tq->tq_dispatch_cv);
    while (tq->tq_nthreads != 0)
        cv_wait(&tq->tq_wait_cv, &tq->tq_lock);

    tq->tq_minalloc = 0;
    while (tq->tq_nalloc != 0)
        taskq_ent_free(tq, taskq_ent_alloc(tq, TQ_SLEEP));

    mutex_exit(&tq->tq_lock);

    /*
     * Mark each bucket as closing and wakeup all sleeping threads.
     */
    for (; (b != NULL) && (bid < tq->tq_nbuckets); b++, bid++) {
        taskq_ent_t *tqe;

        mutex_enter(&b->tqbucket_lock);

        b->tqbucket_flags |= TQBUCKET_CLOSE;
        /* Wakeup all sleeping threads */

        for (tqe = b->tqbucket_freelist.tqent_next;
                tqe != &b->tqbucket_freelist; tqe = tqe->tqent_next)
            cv_signal(&tqe->tqent_cv);

        ASSERT(b->tqbucket_nalloc == 0);

        /*
         * At this point we waited for all pending jobs to complete (in
         * both the task queue and the bucket and no new jobs should
         * arrive. Wait for all threads to die.
         */
        while (b->tqbucket_nfree > 0)
            cv_wait(&b->tqbucket_cv, &b->tqbucket_lock);
        mutex_exit(&b->tqbucket_lock);
        mutex_destroy(&b->tqbucket_lock);
        cv_destroy(&b->tqbucket_cv);
    }

    if (tq->tq_buckets != NULL) {
        ASSERT(tq->tq_flags & TASKQ_DYNAMIC);
        kmem_free(tq->tq_buckets,
                  sizeof (taskq_bucket_t) * tq->tq_nbuckets);

        /* Cleanup fields before returning tq to the cache */
        tq->tq_buckets = NULL;
        tq->tq_tcreates = 0;
        tq->tq_tdeaths = 0;
    } else {
        ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC));
    }

    tq->tq_totaltime = 0;
    tq->tq_tasks = 0;
    tq->tq_maxtasks = 0;
    tq->tq_executed = 0;
    kmem_cache_free(taskq_cache, tq);
}
Example #19
0
File: taskq.c Project: GeLiXin/zfs
void
taskq_wait_outstanding(taskq_t *tq, taskqid_t id)
{
	taskq_wait(tq);
}
Example #20
0
static int
splat_taskq_test10(struct file *file, void *arg)
{
	taskq_t *tq;
	splat_taskq_arg_t **tqas;
	atomic_t count;
	int i, j, rc = 0;
	int minalloc = 1;
	int maxalloc = 10;
	int nr_tasks = 100;
	int canceled = 0;
	int completed = 0;
	int blocked = 0;
	clock_t start, cancel;

	tqas = vmalloc(sizeof(*tqas) * nr_tasks);
	if (tqas == NULL)
		return -ENOMEM;
        memset(tqas, 0, sizeof(*tqas) * nr_tasks);

	splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
	    "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n",
	    SPLAT_TASKQ_TEST10_NAME, "delay", minalloc, maxalloc, nr_tasks);
	if ((tq = taskq_create(SPLAT_TASKQ_TEST10_NAME, 3, maxclsyspri,
	    minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
		splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
		    "Taskq '%s' create failed\n", SPLAT_TASKQ_TEST10_NAME);
		rc = -EINVAL;
		goto out_free;
	}

	atomic_set(&count, 0);

	for (i = 0; i < nr_tasks; i++) {
		splat_taskq_arg_t *tq_arg;
		uint32_t rnd;

		/* A random timeout in jiffies of at most 5 seconds */
		get_random_bytes((void *)&rnd, 4);
		rnd = rnd % (5 * HZ);

		tq_arg = kmem_alloc(sizeof(splat_taskq_arg_t), KM_SLEEP);
		tq_arg->file = file;
		tq_arg->name = SPLAT_TASKQ_TEST10_NAME;
		tq_arg->count = &count;
		tqas[i] = tq_arg;

		/*
		 * Dispatch every 1/3 one immediately to mix it up, the cancel
		 * code is inherently racy and we want to try and provoke any
		 * subtle concurrently issues.
		 */
		if ((i % 3) == 0) {
			tq_arg->expire = ddi_get_lbolt();
			tq_arg->id = taskq_dispatch(tq, splat_taskq_test10_func,
			    tq_arg, TQ_SLEEP);
		} else {
			tq_arg->expire = ddi_get_lbolt() + rnd;
			tq_arg->id = taskq_dispatch_delay(tq,
			    splat_taskq_test10_func,
			    tq_arg, TQ_SLEEP, ddi_get_lbolt() + rnd);
		}

		if (tq_arg->id == 0) {
			splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
			   "Taskq '%s' dispatch failed\n",
			   SPLAT_TASKQ_TEST10_NAME);
			kmem_free(tq_arg, sizeof(splat_taskq_arg_t));
			taskq_wait(tq);
			rc = -EINVAL;
			goto out;
		} else {
			splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
			    "Taskq '%s' dispatch %lu in %lu jiffies\n",
			    SPLAT_TASKQ_TEST10_NAME, (unsigned long)tq_arg->id,
			    !(i % 3) ? 0 : tq_arg->expire - ddi_get_lbolt());
		}
	}

	/*
	 * Start randomly canceling tasks for the duration of the test.  We
	 * happen to know the valid task id's will be in the range 1..nr_tasks
	 * because the taskq is private and was just created.  However, we
	 * have no idea of a particular task has already executed or not.
	 */
	splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, "Taskq '%s' randomly "
	    "canceling task ids\n", SPLAT_TASKQ_TEST10_NAME);

	start = ddi_get_lbolt();
	i = 0;

	while (ddi_time_before(ddi_get_lbolt(), start + 5 * HZ)) {
		taskqid_t id;
		uint32_t rnd;

		i++;
		cancel = ddi_get_lbolt();
		get_random_bytes((void *)&rnd, 4);
		id = 1 + (rnd % nr_tasks);
		rc = taskq_cancel_id(tq, id);

		/*
		 * Keep track of the results of the random cancels.
		 */
		if (rc == 0) {
			canceled++;
		} else if (rc == ENOENT) {
			completed++;
		} else if (rc == EBUSY) {
			blocked++;
		} else {
			rc = -EINVAL;
			break;
		}

		/*
		 * Verify we never get blocked to long in taskq_cancel_id().
		 * The worst case is 10ms if we happen to cancel the task
		 * which is currently executing.  We allow a factor of 2x.
		 */
		if (ddi_get_lbolt() - cancel > HZ / 50) {
			splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
			    "Taskq '%s' cancel for %lu took %lu\n",
			    SPLAT_TASKQ_TEST10_NAME, (unsigned long)id,
			    ddi_get_lbolt() - cancel);
			rc = -ETIMEDOUT;
			break;
		}

		get_random_bytes((void *)&rnd, 4);
		msleep(1 + (rnd % 100));
		rc = 0;
	}

	taskq_wait(tq);

	/*
	 * Cross check the results of taskq_cancel_id() with the number of
	 * times the dispatched function actually ran successfully.
	 */
	if ((rc == 0) && (nr_tasks - canceled != atomic_read(&count)))
		rc = -EDOM;

	splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, "Taskq '%s' %d attempts, "
	    "%d canceled, %d completed, %d blocked, %d/%d tasks run\n",
	    SPLAT_TASKQ_TEST10_NAME, i, canceled, completed, blocked,
	    atomic_read(&count), nr_tasks);
	splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, "Taskq '%s' destroying %d\n",
	    SPLAT_TASKQ_TEST10_NAME, rc);
out:
	taskq_destroy(tq);
out_free:
	for (j = 0; j < nr_tasks && tqas[j] != NULL; j++)
		kmem_free(tqas[j], sizeof(splat_taskq_arg_t));
	vfree(tqas);

	return rc;
}
Example #21
0
/*
 * Given a list of directories to search, find all pools stored on disk.  This
 * includes partial pools which are not available to import.  If no args are
 * given (argc is 0), then the default directory (/dev/dsk) is searched.
 * poolname or guid (but not both) are provided by the caller when trying
 * to import a specific pool.
 */
static nvlist_t *
zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg)
{
	int i, dirs = iarg->paths;
	struct dirent *dp;
	char path[MAXPATHLEN];
	char *end, **dir = iarg->path;
	size_t pathleft;
	nvlist_t *ret = NULL;
	pool_list_t pools = { 0 };
	pool_entry_t *pe, *penext;
	vdev_entry_t *ve, *venext;
	config_entry_t *ce, *cenext;
	name_entry_t *ne, *nenext;
	avl_tree_t slice_cache;
	rdsk_node_t *slice;
	void *cookie;

	verify(iarg->poolname == NULL || iarg->guid == 0);

	if (dirs == 0) {
#ifdef HAVE_LIBBLKID
		/* Use libblkid to scan all device for their type */
		if (zpool_find_import_blkid(hdl, &pools) == 0)
			goto skip_scanning;

		(void) zfs_error_fmt(hdl, EZFS_BADCACHE,
		    dgettext(TEXT_DOMAIN, "blkid failure falling back "
		    "to manual probing"));
#endif /* HAVE_LIBBLKID */

		dir = zpool_default_import_path;
		dirs = DEFAULT_IMPORT_PATH_SIZE;
	}

	/*
	 * Go through and read the label configuration information from every
	 * possible device, organizing the information according to pool GUID
	 * and toplevel GUID.
	 */
	for (i = 0; i < dirs; i++) {
		taskq_t *t;
		char rdsk[MAXPATHLEN];
		int dfd;
		boolean_t config_failed = B_FALSE;
		DIR *dirp;

		/* use realpath to normalize the path */
		if (realpath(dir[i], path) == 0) {

			/* it is safe to skip missing search paths */
			if (errno == ENOENT)
				continue;

			zfs_error_aux(hdl, strerror(errno));
			(void) zfs_error_fmt(hdl, EZFS_BADPATH,
			    dgettext(TEXT_DOMAIN, "cannot open '%s'"), dir[i]);
			goto error;
		}
		end = &path[strlen(path)];
		*end++ = '/';
		*end = 0;
		pathleft = &path[sizeof (path)] - end;

		/*
		 * Using raw devices instead of block devices when we're
		 * reading the labels skips a bunch of slow operations during
		 * close(2) processing, so we replace /dev/dsk with /dev/rdsk.
		 */
		if (strcmp(path, ZFS_DISK_ROOTD) == 0)
			(void) strlcpy(rdsk, ZFS_RDISK_ROOTD, sizeof (rdsk));
		else
			(void) strlcpy(rdsk, path, sizeof (rdsk));

		if ((dfd = open(rdsk, O_RDONLY)) < 0 ||
		    (dirp = fdopendir(dfd)) == NULL) {
			if (dfd >= 0)
				(void) close(dfd);
			zfs_error_aux(hdl, strerror(errno));
			(void) zfs_error_fmt(hdl, EZFS_BADPATH,
			    dgettext(TEXT_DOMAIN, "cannot open '%s'"),
			    rdsk);
			goto error;
		}

		avl_create(&slice_cache, slice_cache_compare,
		    sizeof (rdsk_node_t), offsetof(rdsk_node_t, rn_node));

		/*
		 * This is not MT-safe, but we have no MT consumers of libzfs
		 */
		while ((dp = readdir(dirp)) != NULL) {
			const char *name = dp->d_name;
			if (name[0] == '.' &&
			    (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
				continue;

			slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
			slice->rn_name = zfs_strdup(hdl, name);
			slice->rn_avl = &slice_cache;
			slice->rn_dfd = dfd;
			slice->rn_hdl = hdl;
			slice->rn_nozpool = B_FALSE;
			avl_add(&slice_cache, slice);
		}

		/*
		 * create a thread pool to do all of this in parallel;
		 * rn_nozpool is not protected, so this is racy in that
		 * multiple tasks could decide that the same slice can
		 * not hold a zpool, which is benign.  Also choose
		 * double the number of processors; we hold a lot of
		 * locks in the kernel, so going beyond this doesn't
		 * buy us much.
		 */
		t = taskq_create("z_import", 2 * max_ncpus, defclsyspri,
		    2 * max_ncpus, INT_MAX, TASKQ_PREPOPULATE);
		for (slice = avl_first(&slice_cache); slice;
		    (slice = avl_walk(&slice_cache, slice,
		    AVL_AFTER)))
			(void) taskq_dispatch(t, zpool_open_func, slice,
			    TQ_SLEEP);
		taskq_wait(t);
		taskq_destroy(t);

		cookie = NULL;
		while ((slice = avl_destroy_nodes(&slice_cache,
		    &cookie)) != NULL) {
			if (slice->rn_config != NULL && !config_failed) {
				nvlist_t *config = slice->rn_config;
				boolean_t matched = B_TRUE;

				if (iarg->poolname != NULL) {
					char *pname;

					matched = nvlist_lookup_string(config,
					    ZPOOL_CONFIG_POOL_NAME,
					    &pname) == 0 &&
					    strcmp(iarg->poolname, pname) == 0;
				} else if (iarg->guid != 0) {
					uint64_t this_guid;

					matched = nvlist_lookup_uint64(config,
					    ZPOOL_CONFIG_POOL_GUID,
					    &this_guid) == 0 &&
					    iarg->guid == this_guid;
				}
				if (!matched) {
					nvlist_free(config);
				} else {
					/*
					 * use the non-raw path for the config
					 */
					(void) strlcpy(end, slice->rn_name,
					    pathleft);
					if (add_config(hdl, &pools, path, i+1,
					    slice->rn_num_labels, config) != 0)
						config_failed = B_TRUE;
				}
			}
			free(slice->rn_name);
			free(slice);
		}
		avl_destroy(&slice_cache);

		(void) closedir(dirp);

		if (config_failed)
			goto error;
	}

#ifdef HAVE_LIBBLKID
skip_scanning:
#endif
	ret = get_configs(hdl, &pools, iarg->can_be_active, iarg->policy);

error:
	for (pe = pools.pools; pe != NULL; pe = penext) {
		penext = pe->pe_next;
		for (ve = pe->pe_vdevs; ve != NULL; ve = venext) {
			venext = ve->ve_next;
			for (ce = ve->ve_configs; ce != NULL; ce = cenext) {
				cenext = ce->ce_next;
				if (ce->ce_config)
					nvlist_free(ce->ce_config);
				free(ce);
			}
			free(ve);
		}
		free(pe);
	}

	for (ne = pools.names; ne != NULL; ne = nenext) {
		nenext = ne->ne_next;
		free(ne->ne_name);
		free(ne);
	}

	return (ret);
}
Example #22
0
/*
 * Routine called by both ioctl and k-api. The consumer should
 * bundle the parameters into a kcf_req_params_t structure. A bunch
 * of macros are available in ops_impl.h for this bundling. They are:
 *
 * 	KCF_WRAP_DIGEST_OPS_PARAMS()
 *	KCF_WRAP_MAC_OPS_PARAMS()
 *	KCF_WRAP_ENCRYPT_OPS_PARAMS()
 *	KCF_WRAP_DECRYPT_OPS_PARAMS() ... etc.
 *
 * It is the caller's responsibility to free the ctx argument when
 * appropriate. See the KCF_CONTEXT_COND_RELEASE macro for details.
 */
int
kcf_submit_request(kcf_provider_desc_t *pd, crypto_ctx_t *ctx,
    crypto_call_req_t *crq, kcf_req_params_t *params, boolean_t cont)
{
	int error = CRYPTO_SUCCESS;
	kcf_areq_node_t *areq;
	kcf_sreq_node_t *sreq;
	kcf_context_t *kcf_ctx;
	taskq_t *taskq = pd->pd_sched_info.ks_taskq;

	kcf_ctx = ctx ? (kcf_context_t *)ctx->cc_framework_private : NULL;

	/* Synchronous cases */
	if (crq == NULL) {
		switch (pd->pd_prov_type) {
		case CRYPTO_SW_PROVIDER:
			error = common_submit_request(pd, ctx, params,
			    KCF_RHNDL(KM_SLEEP));
			break;

		case CRYPTO_HW_PROVIDER:
			/*
			 * Special case for CRYPTO_SYNCHRONOUS providers that
			 * never return a CRYPTO_QUEUED error. We skip any
			 * request allocation and call the SPI directly.
			 */
			if ((pd->pd_flags & CRYPTO_SYNCHRONOUS) &&
			    EMPTY_TASKQ(taskq)) {
				KCF_PROV_IREFHOLD(pd);
				if (pd->pd_state == KCF_PROV_READY) {
					error = common_submit_request(pd, ctx,
					    params, KCF_RHNDL(KM_SLEEP));
					KCF_PROV_IREFRELE(pd);
					ASSERT(error != CRYPTO_QUEUED);
					break;
				}
				KCF_PROV_IREFRELE(pd);
			}

			sreq = kmem_cache_alloc(kcf_sreq_cache, KM_SLEEP);
			sreq->sn_state = REQ_ALLOCATED;
			sreq->sn_rv = CRYPTO_FAILED;
			sreq->sn_params = params;

			/*
			 * Note that we do not need to hold the context
			 * for synchronous case as the context will never
			 * become invalid underneath us. We do not need to hold
			 * the provider here either as the caller has a hold.
			 */
			sreq->sn_context = kcf_ctx;
			ASSERT(KCF_PROV_REFHELD(pd));
			sreq->sn_provider = pd;

			ASSERT(taskq != NULL);
			/*
			 * Call the SPI directly if the taskq is empty and the
			 * provider is not busy, else dispatch to the taskq.
			 * Calling directly is fine as this is the synchronous
			 * case. This is unlike the asynchronous case where we
			 * must always dispatch to the taskq.
			 */
			if (EMPTY_TASKQ(taskq) &&
			    pd->pd_state == KCF_PROV_READY) {
				process_req_hwp(sreq);
			} else {
				/*
				 * We can not tell from taskq_dispatch() return
				 * value if we exceeded maxalloc. Hence the
				 * check here. Since we are allowed to wait in
				 * the synchronous case, we wait for the taskq
				 * to become empty.
				 */
				if (taskq->tq_nalloc >= crypto_taskq_maxalloc) {
					taskq_wait(taskq);
				}

				(void) taskq_dispatch(taskq, process_req_hwp,
				    sreq, TQ_SLEEP);
			}

			/*
			 * Wait for the notification to arrive,
			 * if the operation is not done yet.
			 * Bug# 4722589 will make the wait a cv_wait_sig().
			 */
			mutex_enter(&sreq->sn_lock);
			while (sreq->sn_state < REQ_DONE)
				cv_wait(&sreq->sn_cv, &sreq->sn_lock);
			mutex_exit(&sreq->sn_lock);

			error = sreq->sn_rv;
			kmem_cache_free(kcf_sreq_cache, sreq);

			break;

		default:
			error = CRYPTO_FAILED;
			break;
		}

	} else {	/* Asynchronous cases */
		switch (pd->pd_prov_type) {
		case CRYPTO_SW_PROVIDER:
			if (!(crq->cr_flag & CRYPTO_ALWAYS_QUEUE)) {
				/*
				 * This case has less overhead since there is
				 * no switching of context.
				 */
				error = common_submit_request(pd, ctx, params,
				    KCF_RHNDL(KM_NOSLEEP));
			} else {
				/*
				 * CRYPTO_ALWAYS_QUEUE is set. We need to
				 * queue the request and return.
				 */
				areq = kcf_areqnode_alloc(pd, kcf_ctx, crq,
				    params, cont);
				if (areq == NULL)
					error = CRYPTO_HOST_MEMORY;
				else {
					if (!(crq->cr_flag
					    & CRYPTO_SKIP_REQID)) {
					/*
					 * Set the request handle. This handle
					 * is used for any crypto_cancel_req(9f)
					 * calls from the consumer. We have to
					 * do this before dispatching the
					 * request.
					 */
					crq->cr_reqid = kcf_reqid_insert(areq);
					}

					error = kcf_disp_sw_request(areq);
					/*
					 * There is an error processing this
					 * request. Remove the handle and
					 * release the request structure.
					 */
					if (error != CRYPTO_QUEUED) {
						if (!(crq->cr_flag
						    & CRYPTO_SKIP_REQID))
							kcf_reqid_delete(areq);
						KCF_AREQ_REFRELE(areq);
					}
				}
			}
			break;

		case CRYPTO_HW_PROVIDER:
			/*
			 * We need to queue the request and return.
			 */
			areq = kcf_areqnode_alloc(pd, kcf_ctx, crq, params,
			    cont);
			if (areq == NULL) {
				error = CRYPTO_HOST_MEMORY;
				goto done;
			}

			ASSERT(taskq != NULL);
			/*
			 * We can not tell from taskq_dispatch() return
			 * value if we exceeded maxalloc. Hence the check
			 * here.
			 */
			if (taskq->tq_nalloc >= crypto_taskq_maxalloc) {
				error = CRYPTO_BUSY;
				KCF_AREQ_REFRELE(areq);
				goto done;
			}

			if (!(crq->cr_flag & CRYPTO_SKIP_REQID)) {
			/*
			 * Set the request handle. This handle is used
			 * for any crypto_cancel_req(9f) calls from the
			 * consumer. We have to do this before dispatching
			 * the request.
			 */
			crq->cr_reqid = kcf_reqid_insert(areq);
			}

			if (taskq_dispatch(taskq,
			    process_req_hwp, areq, TQ_NOSLEEP) ==
			    TASKQID_INVALID) {
				error = CRYPTO_HOST_MEMORY;
				if (!(crq->cr_flag & CRYPTO_SKIP_REQID))
					kcf_reqid_delete(areq);
				KCF_AREQ_REFRELE(areq);
			} else {
				error = CRYPTO_QUEUED;
			}
			break;

		default:
			error = CRYPTO_FAILED;
			break;
		}
	}

done:
	return (error);
}