xref: /freebsd/sys/contrib/openzfs/cmd/zstream/zstream_queue.c (revision 2f10ffc003be396f3fc23cd2888023896560252b)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * https://opensource.org/license/CDDL-1.0.
11  */
12 
13 /*
14  * Copyright (c) 2026 by Garth Snyder. All rights reserved.
15  */
16 
17 #include <assert.h>
18 #include <atomic.h>
19 #include <err.h>
20 #include <errno.h>
21 #include <math.h>
22 #include <pthread.h>
23 #include <sched.h>
24 #include <stddef.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/param.h>
30 #include <sys/random.h>
31 #include <sys/stdtypes.h>
32 #include <sys/sysmacros.h>
33 #include <sys/time.h>
34 #include <time.h>
35 #include <unistd.h>
36 
37 #include "zstream_queue.h"
38 #include "zstream_util.h"
39 
40 #define	ENQUEUE_DELAY_NSEC	(100 * 1000)		/* 100us */
41 #define	DISPATCH_BACKUP_NSEC	(1000 * 1000)		/* 1ms */
42 #define	BATCH_TIME_NSEC		(400 * 1000)		/* 400us */
43 
44 #define	Q_MOD(index)		((index) % ZQ_SLOTS_PER_QUEUE)
45 #define	Q_SLOT(queue, index)	((queue)->zq_slots[Q_MOD(index)])
46 
47 #define	Q_FULL(queue)	((queue)->zq_ix.enqueue - (queue)->zq_ix.dequeue >= \
48 	    ZQ_SLOTS_PER_QUEUE)
49 
50 // #define	MONITOR_QUEUES		/* Queue tenancy data per interval */
51 // #define	SHOW_BATCH_HISTOGRAMS	/* Batch size histograms per queue */
52 
53 /*
54  * A zstream_queue is a ring buffer with four indexes: enqueue, claim,
55  * complete, and dequeue, in that order. No index can move beyond its
56  * preceding index. Every interval between indexes contains work items in a
57  * particular state: enqueued, claimed for work, or completed. Items never
58  * leave the ring buffer, so FIFO order is guaranteed on dequeueing.
59  *
60  * In concept, every index has a corresponding condition that threads can
61  * wait on if they are interested in knowing when that index moves:
62  * enqueued, claimed, completed, dequeued. However, the reality deviates
63  * from this model in two ways:
64  *
65  * - There is no "claimed" condition, because no thread would wait on it.
66  * Claiming and processing are one unified operation. Dequeuers await the
67  * "completed" condition.
68  *
69  * - All queues share one thread pool, so idle threads are not bound to any
70  * particular queue. Instead of having queue-specific "enqueued" conditions,
71  * queues share a centralized dispatch system. On being awakened, worker
72  * threads assign themselves to a queue through a scoring mechanism
73  * described in the comments at score_queue().
74  *
75  * LOCKING
76  *
77  * There are three types of lock:
78  *
79  * - One global lock that gates changes to the thread pool and queue cohort.
80  * This lock also acts as the mutex for the tp_wake_worker condition.
81  *
82  * - A second, low-contention global lock that protects the dispatch system
83  *
84  * - One lock for each queue
85  *
86  * Any operation that adds or removes queues or threads should hold the pool
87  * lock. Any operation that moves a queue's indexes should hold the queue
88  * lock. Any thread waiting for work waits on tp_wake_worker.
89  *
90  * Worker threads hold no locks while they are actually processing items.
91  *
92  * The global locking order is dispatch -> pool -> queue.
93  *
94  * DISPATCH
95  *
96  * Four events trigger dispatch loops:
97  *
98  * 1) A worker thread completing its batch. Threads always check to see if
99  * there's more claimable work before going to sleep.
100  *
101  * 2) A worker thread discovering more work than it can handle on its own.
102  * Before starting work on its own batch, the worker attempts to signal
103  * another thread to wake up and assess the current state.
104  *
105  * 3) Enqueues. These go through the dispatch system and are batched.
106  * Roughly ENQUEUE_DELAY_NSEC after an enqueue (on any queue), the dispatch
107  * thread attempts to awaken a worker.
108  *
109  * 4) The expiration of a backup timer. The atomic value tp_unclaimed tracks
110  * the total number of enqueued-but-unclaimed items across all queues. When
111  * zero, it indicates that no worker dispatch is currently necessary. This
112  * value is rigorously maintained and the increments and decrements are
113  * sequentially consistent. However, the reads are relaxed, so a reader may
114  * see a stale value. In the event that a critical worker wakeup is dropped,
115  * the backup timer intervenes to keep dispatches running.
116  */
117 
118 typedef struct {
119 	queue_item_t	*qs_item;
120 	size_t		qs_cost;
121 	boolean_t	qs_completed;
122 	boolean_t	qs_end_of_stream;
123 } queue_slot_t;
124 
125 typedef struct {
126 	uint64_t	enqueue;
127 	uint64_t	claim;
128 	uint64_t	complete;
129 	uint64_t	dequeue;
130 } zq_indexes_t;
131 
132 typedef struct {
133 	pthread_cond_t	completed;
134 	pthread_cond_t	dequeued;
135 } zq_conditions_t;
136 
137 #ifdef MONITOR_QUEUES
138 /*
139  * Running mean and standard deviation of queue depth, accumulated by
140  * Welford's online algorithm. Welford is numerically stable and doesn't
141  * need wider-than-platform integers.
142  */
143 typedef struct {
144 	int		min;
145 	int		max;
146 	uint64_t	ds_samples;
147 	double		ds_mean;
148 	double		ds_m2;		/* Sum of squared deviations */
149 } depth_stats_t;
150 #endif
151 
152 typedef struct {
153 #ifdef MONITOR_QUEUES
154 	depth_stats_t	depth;
155 #endif
156 	uint64_t	nsec_used;
157 	size_t		cost_processed;
158 } zq_stats_t;
159 
160 struct zstream_queue {
161 	int		zq_id;
162 	queue_slot_t	*zq_slots;
163 	pthread_mutex_t	zq_mutex;
164 	zq_indexes_t	zq_ix;
165 	zq_conditions_t	zq_cond;
166 	zq_params_t	zq_params;
167 	boolean_t	zq_disallow_enqueue;
168 	zq_stats_t	zq_stats;
169 #ifdef SHOW_BATCH_HISTOGRAMS
170 	uint64_t	zq_histogram[ZQ_MAX_BATCH+1];	/* Batch sizes */
171 #endif
172 };
173 
174 typedef struct {
175 	pthread_mutex_t	tp_pool_mutex;
176 	pthread_cond_t	tp_wake_worker;		/* Awaited by workers */
177 
178 	pthread_mutex_t	tp_dispatch_mutex;
179 	pthread_cond_t	tp_request_dispatch;	/* By dispatch thread */
180 	boolean_t	tp_dispatch_requested;
181 
182 	zstream_queue_t	*tp_queues[ZQ_MAX_QUEUES];
183 	int		tp_num_queues;
184 
185 	boolean_t	tp_threads_created;
186 	int		tp_num_threads;
187 
188 	uint64_t	tp_unclaimed;		/* Atomic, all queues */
189 } thread_pool_t;
190 
191 typedef union {
192 	long long	ll;
193 	long double	ld;
194 	void		*p;
195 	void		(*fp)(void);
196 } worst_case_alignment_t;
197 
198 static void *queue_worker(void *);
199 static void *dispatch_worker(void *);
200 
201 #ifdef MONITOR_QUEUES
202 static void *cpu_and_queue_monitor(void *);
203 static inline void initialize_monitor_data(zstream_queue_t *);
204 static inline void update_monitor_data(zstream_queue_t *);
205 #endif
206 
207 #ifdef SHOW_BATCH_HISTOGRAMS
208 static void print_batch_size_histogram(zstream_queue_t *);
209 #endif
210 
211 static thread_pool_t	pool = {0};
212 static pthread_once_t	once_control = PTHREAD_ONCE_INIT;
213 
214 /*
215  * The dispatch timer needs sub-millisecond accuracy. POSIX timers on
216  * FreeBSD don't implement that, but nanosleep() works fine.
217  */
218 static void
sleep_nsec(uint64_t nsec)219 sleep_nsec(uint64_t nsec)
220 {
221 	struct timespec ts = {
222 		.tv_sec = nsec / NANOSEC,
223 		.tv_nsec = nsec % NANOSEC
224 	};
225 	while (nanosleep(&ts, &ts) != 0) {
226 		if (errno != EINTR)
227 			err(1, "nanosleep failed");
228 	}
229 }
230 
231 static void
thread_pool_init(void)232 thread_pool_init(void)
233 {
234 	pthread_mutex_init(&pool.tp_pool_mutex, NULL);
235 	pthread_cond_init(&pool.tp_wake_worker, NULL);
236 
237 	pthread_mutex_init(&pool.tp_dispatch_mutex, NULL);
238 	pthread_cond_init(&pool.tp_request_dispatch, NULL);
239 
240 	safe_create_thread(dispatch_worker, NULL, "dispatch", B_TRUE);
241 }
242 
243 /*
244  * If this function is to be called at all, it must be called before any
245  * queues have been created.
246  */
247 void
zstream_queue_set_num_threads(int n)248 zstream_queue_set_num_threads(int n)
249 {
250 	pthread_once(&once_control, thread_pool_init);
251 	pthread_mutex_lock(&pool.tp_pool_mutex);
252 	if (pool.tp_threads_created) {
253 		errx(1, "thread pool size must be set before creating queues");
254 	} else if (n < 1) {
255 		errx(1, "number of threads must be at least 1");
256 	} else if (n < ZQ_MIN_THREADS) {
257 		warnx("using only %d threads may limit performance, setting "
258 		    "anyway...", n);
259 	} else if (n > 256) {
260 		warnx("num_threads = %d seems suspiciously high, setting "
261 		    "anyway...", n);
262 	}
263 	pool.tp_num_threads = n;
264 	pthread_mutex_unlock(&pool.tp_pool_mutex);
265 }
266 
267 /*
268  * Locking: the caller must hold the pool mutex.
269  *
270  * If tp_num_threads is nonzero, it sets the number of threads to spawn.
271  * Otherwise, one thread is spawned per core, with a minimum of 6 threads.
272  *
273  * sched_getaffinity() is a better estimate of available threads than
274  * sysconf because sysconf doesn't account for limits that might be set on,
275  * e.g., a container.
276  */
277 static void
thread_pool_spinup(void)278 thread_pool_spinup(void)
279 {
280 	if (pool.tp_num_threads == 0) {
281 #ifdef	CPU_COUNT
282 		cpu_set_t cpu_set;
283 		if (sched_getaffinity(0, sizeof (cpu_set_t), &cpu_set) != 0) {
284 			warn("sched_getaffinity failed, using sysconf");
285 			pool.tp_num_threads = sysconf(_SC_NPROCESSORS_ONLN);
286 		} else {
287 			pool.tp_num_threads = CPU_COUNT(&cpu_set);
288 		}
289 #else
290 		pool.tp_num_threads = sysconf(_SC_NPROCESSORS_ONLN);
291 #endif
292 		pool.tp_num_threads = MAX(pool.tp_num_threads, ZQ_MIN_THREADS);
293 	}
294 	for (int i = 0; i < pool.tp_num_threads; i++) {
295 		char name[32];
296 		snprintf(name, sizeof (name), "queue-%d", i);
297 		safe_create_thread(queue_worker, NULL, name, B_TRUE);
298 	}
299 #ifdef MONITOR_QUEUES
300 	safe_create_thread(cpu_and_queue_monitor, NULL, "monitor", B_TRUE);
301 #endif
302 }
303 
304 zstream_queue_t *
zstream_queue_create(zq_params_t * params)305 zstream_queue_create(zq_params_t *params)
306 {
307 	static int next_queue_id = 0;
308 
309 	VERIFY3P(params->qp_process, !=, NULL);
310 	VERIFY3P(params->qp_cost, !=, NULL);
311 	VERIFY3U(params->qp_item_size, >, 0);
312 
313 	pthread_once(&once_control, thread_pool_init);
314 	pthread_mutex_lock(&pool.tp_pool_mutex);
315 	VERIFY3S(pool.tp_num_queues, <, ZQ_MAX_QUEUES);
316 
317 	if (!pool.tp_threads_created) {
318 		thread_pool_spinup();
319 		pool.tp_threads_created = B_TRUE;
320 	}
321 
322 	zstream_queue_t *queue = safe_malloc(sizeof (zstream_queue_t));
323 	*queue = (zstream_queue_t) {
324 		.zq_id = next_queue_id++,
325 		.zq_params = *params,
326 		.zq_slots = safe_malloc(ZQ_SLOTS_PER_QUEUE *
327 		    (sizeof (queue_slot_t)))
328 	};
329 	pool.tp_queues[pool.tp_num_queues] = queue;
330 
331 #ifdef MONITOR_QUEUES
332 	initialize_monitor_data(queue);
333 #endif
334 
335 	size_t qpis_rounded = P2ROUNDUP(params->qp_item_size,
336 	    _Alignof(worst_case_alignment_t));
337 	uint8_t *items = safe_malloc(ZQ_SLOTS_PER_QUEUE * qpis_rounded);
338 	for (int i = 0; i < ZQ_SLOTS_PER_QUEUE; i++) {
339 		queue->zq_slots[i].qs_item =
340 		    (queue_item_t *)(items + i * qpis_rounded);
341 	}
342 
343 	pthread_mutex_init(&queue->zq_mutex, NULL);
344 	pthread_cond_init(&queue->zq_cond.completed, NULL);
345 	pthread_cond_init(&queue->zq_cond.dequeued, NULL);
346 
347 	pool.tp_num_queues++;
348 	pthread_mutex_unlock(&pool.tp_pool_mutex);
349 	return (queue);
350 }
351 
352 /*
353  * Try to advance the "claim" and "complete" indexes as far as possible by
354  * examining the qs_completed flag on each item. This can't be done directly
355  * by the threads that complete work, for a couple of reasons:
356  *
357  * - Items can be completed in any order. Just because you (a thread) have
358  * finished your batch doesn't mean that all prior batches have completed.
359  * If there are uncompleted items ahead of you in the ring buffer, you can't
360  * advance the completion index past them on your way out.
361  *
362  * - Items for which the cost function returns 0 are marked as qs_completed
363  * on enqueue and are never seen by a worker thread. So, there needs to be
364  * an independent mechanism to sweep the completion index past these items
365  * whenever that becomes possible.
366  *
367  * This function is called:
368  *
369  * - Whenever a thread completes a batch
370  * - Whenever a thread claims a batch
371  * - Whenever an item of cost 0 is enqueued
372  *
373  * Strictly speaking, advancing on claiming a batch is not logically
374  * necessary. However, the claimer already holds the queue mutex, and it's
375  * in our interest to make completed items available for dequeueing as
376  * expeditiously as possible.
377  *
378  * Sweeping of the "claim" index is also an optimization. It is not
379  * necessary for correctness. However, if we don't do it here, it can only
380  * be done by threads as they claim jobs to work on. In some cases, not
381  * advancing the "claim" index here can result in an empty batch and a
382  * wasted claim cycle.
383  *
384  * Locking: the caller must hold the queue mutex.
385  */
386 static inline void
advance_indexes(zstream_queue_t * queue)387 advance_indexes(zstream_queue_t *queue)
388 {
389 	boolean_t any_completed = B_FALSE;
390 	uint64_t claimed = 0;
391 
392 	while (queue->zq_ix.claim < queue->zq_ix.enqueue &&
393 	    Q_SLOT(queue, queue->zq_ix.claim).qs_completed) {
394 		queue->zq_ix.claim++;
395 		claimed++;
396 	}
397 	if (claimed > 0) {
398 		/*
399 		 * tp_unclaimed is decremented both here and in
400 		 * claim_batch(). The conditions are mutually exclusive, so
401 		 * double counting will not occur.
402 		 */
403 		atomic_sub_64(&pool.tp_unclaimed, claimed);
404 	}
405 	while (queue->zq_ix.complete < queue->zq_ix.claim &&
406 	    Q_SLOT(queue, queue->zq_ix.complete).qs_completed) {
407 		queue->zq_ix.complete++;
408 		any_completed = B_TRUE;
409 	}
410 	if (any_completed) {
411 		pthread_cond_signal(&queue->zq_cond.completed);
412 	}
413 }
414 
415 /*
416  * Score a queue according to its need for workers. Higher is better.
417  *
418  * Threads are distributed among queues in proportion to their scores (see
419  * select_stochastic()), so a queue with twice as much outstanding work
420  * attracts twice as many workers. Queues with no claimable work score 0 and
421  * are never selected.
422  *
423  * The score is just the number of unclaimed items. More elaborate scoring
424  * is possible. Earlier versions weighted queues by how close they were to
425  * being full and by how little completed work they had available to
426  * dequeue. But, benchmarking showed no benefit over the simple count below.
427  * Also not effective: backpressure-style differencing with the next
428  * downstream queue.
429  *
430  * Locking: the caller must hold the thread pool mutex and the queue mutex.
431  */
432 static inline uint32_t
score_queue(zstream_queue_t * queue)433 score_queue(zstream_queue_t *queue)
434 {
435 	return (queue->zq_ix.enqueue - queue->zq_ix.claim);
436 }
437 
438 /*
439  * Return a random index into weights[], with the likelihood of index i
440  * being selected equal to weights[i] / sum(weights).
441  *
442  * The caller must guarantee that at least one weight is nonzero;
443  * a uniformly zero array has no meaningful answer and would divide by
444  * zero. assign_queue_and_get_work() checks this before calling.
445  */
446 static inline int
select_stochastic(uint32_t weights[],int num_values)447 select_stochastic(uint32_t weights[], int num_values)
448 {
449 	uint64_t randval;
450 	uint32_t total = 0;
451 
452 	for (int i = 0; i < num_values; i++) {
453 		total += weights[i];
454 	}
455 	if (total == 0)
456 		return (0);
457 	random_get_pseudo_bytes((uint8_t *)&randval, sizeof (randval));
458 	uint64_t select_val = randval % total;
459 	for (int i = 0; i < num_values; i++) {
460 		if (select_val < weights[i])
461 			return (i);
462 		select_val -= weights[i];
463 	}
464 	return (num_values - 1);
465 }
466 
467 /*
468  * Claim up to ZQ_MAX_BATCH work items from the given queue. All items in a
469  * batch will be drawn from the same queue.
470  *
471  * Queues track the historical relationship between cost values and
472  * processing times, which is assumed to be roughly linear. The goal is for
473  * each batch to have a processing time of BATCH_TIME_NSEC. However, most
474  * batches fall short of this goal because of the availability of work.
475  * claim_batch() does not block waiting to fill the budget; it returns
476  * whatever is available.
477  *
478  * The cost/time calculation is a cumulative average over the life of the
479  * queue, so it converges early and changes only slowly. Stationarity is
480  * assumed. If nonstationary work is being performed, callers should adjust
481  * their cost functions to reflect that.
482  *
483  * Locking: this function must be called with both the queue mutex and the
484  * thread pool mutex held. zstream_queue_destroy() can't hold a queue's
485  * mutex while destroying it (because destruction entails destroying the
486  * queue mutex, which must be unlocked), so holding the queue mutex while
487  * attempting to claim work is not a sufficient guarantee of correctness.
488  *
489  * In other contexts, we have more certainty about whether a queue still has
490  * work to do. If it does, it can't be destroyed while we hold the queue
491  * mutex alone. But here, we merely suspect that there's work available
492  * based on possibly outdated queue scoring information. By the time we get
493  * here, the queue might already have been finalized. Holding the thread
494  * pool mutex guarantees that the queue won't have been destroyed out from
495  * under us.
496  */
497 static int
claim_batch(zstream_queue_t * queue,queue_slot_t ** batch)498 claim_batch(zstream_queue_t *queue, queue_slot_t **batch)
499 {
500 	size_t cost_to_claim, cost_claimed = 0;
501 	int count = 0;
502 	uint64_t passed = 0;
503 	boolean_t more_to_claim, more_slots, more_budget;
504 	boolean_t have_cost_data = queue->zq_stats.nsec_used != 0 &&
505 	    queue->zq_stats.cost_processed != 0;
506 
507 	if (have_cost_data) {
508 		double ns_per_cost = (double)queue->zq_stats.nsec_used /
509 		    (double)queue->zq_stats.cost_processed;
510 		double budget = BATCH_TIME_NSEC / ns_per_cost;
511 		if (budget >= (double)SIZE_MAX)
512 			cost_to_claim = SIZE_MAX;
513 		else
514 			cost_to_claim = MAX(1, (size_t)budget);
515 	} else {
516 		cost_to_claim = 1;
517 	}
518 
519 	while (B_TRUE) {
520 		more_to_claim = queue->zq_ix.claim < queue->zq_ix.enqueue;
521 		more_slots = count < ZQ_MAX_BATCH;
522 		more_budget = cost_claimed < cost_to_claim;
523 
524 		if (!more_to_claim || !more_slots || !more_budget) {
525 			break;
526 		}
527 		queue_slot_t *slot = &Q_SLOT(queue, queue->zq_ix.claim);
528 		if (!slot->qs_completed) {
529 			cost_claimed += slot->qs_cost;
530 			batch[count++] = slot;
531 		}
532 		queue->zq_ix.claim++;
533 		passed++;
534 	}
535 
536 	/*
537 	 * Every slot the claim index moved over leaves the unclaimed pool,
538 	 * whether we took it for the batch or skipped it as already complete.
539 	 */
540 	if (passed > 0) {
541 		atomic_sub_64(&pool.tp_unclaimed, passed);
542 	}
543 	advance_indexes(queue);
544 #ifdef SHOW_BATCH_HISTOGRAMS
545 	queue->zq_histogram[count]++;
546 #endif
547 	return (count);
548 }
549 
550 /*
551  * Threads are assigned to a queue on each loop so they can be shifted
552  * dynamically to follow available work. Idle threads will typically be
553  * waiting on the tp_wake_worker condition within this function.
554  *
555  * Locking: we hold the pool mutex throughout, both to keep a queue from
556  * being destroyed out from under us while we score it or claim from it, and
557  * because it is the mutex for tp_wake_worker. Individual queues are locked
558  * for only as long as it takes to score or claim from them.
559  */
560 static int
assign_queue_and_get_work(zstream_queue_t ** queue,queue_slot_t ** batch)561 assign_queue_and_get_work(zstream_queue_t **queue, queue_slot_t **batch)
562 {
563 	pthread_mutex_lock(&pool.tp_pool_mutex);
564 
565 	while (B_TRUE) {
566 		int num_queues = pool.tp_num_queues;
567 		uint32_t weights[ZQ_MAX_QUEUES];
568 		int queues_with_work = 0;
569 
570 		for (int i = 0; i < num_queues; i++) {
571 			zstream_queue_t *to_score = pool.tp_queues[i];
572 			pthread_mutex_lock(&to_score->zq_mutex);
573 			weights[i] = score_queue(to_score);
574 			pthread_mutex_unlock(&to_score->zq_mutex);
575 			if (weights[i] > 0)
576 				queues_with_work++;
577 		}
578 		if (!queues_with_work) {
579 			pthread_cond_wait(&pool.tp_wake_worker,
580 			    &pool.tp_pool_mutex);
581 		} else {
582 			int q = select_stochastic(weights, num_queues);
583 			*queue = pool.tp_queues[q];
584 			pthread_mutex_lock(&(*queue)->zq_mutex);
585 			int count = claim_batch(*queue, batch);
586 			pthread_mutex_unlock(&(*queue)->zq_mutex);
587 			/*
588 			 * Try to wake up another worker thread if there
589 			 * still seems to be work available (on any queue).
590 			 */
591 			if (atomic_load_64(&pool.tp_unclaimed) > 0) {
592 				pthread_cond_signal(&pool.tp_wake_worker);
593 			}
594 			pthread_mutex_unlock(&pool.tp_pool_mutex);
595 			return (count);
596 		}
597 	}
598 }
599 
600 static inline uint64_t
time_now_ns(void)601 time_now_ns(void)
602 {
603 	struct timespec ts;
604 	clock_gettime(CLOCK_MONOTONIC, &ts);
605 	return ((uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec);
606 }
607 
608 /*
609  * Batches are processed without holding any locks. The existence of the
610  * items we're working on guarantees that the queue can't be destroyed out
611  * from under us.
612  *
613  * However, we can't mark items completed without holding the queue lock
614  * because that creates a potential race condition with advance_indexes()
615  * being called on another thread.
616  */
617 static void *
queue_worker(void * dummy)618 queue_worker(void *dummy)
619 {
620 	(void) dummy;
621 	zstream_queue_t *queue;
622 	queue_slot_t *batch[ZQ_MAX_BATCH];
623 	int count;
624 
625 	while (B_TRUE) {
626 		count = assign_queue_and_get_work(&queue, batch);
627 		if (count) {
628 			size_t batch_cost = 0;
629 			uint64_t start = time_now_ns();
630 			zq_process_item_f *process =
631 			    queue->zq_params.qp_process;
632 			void *context = queue->zq_params.qp_context;
633 			for (int i = 0; i < count; i++) {
634 				process(batch[i]->qs_item, context);
635 				batch_cost += batch[i]->qs_cost;
636 			}
637 			uint64_t nsec = time_now_ns() - start;
638 			pthread_mutex_lock(&queue->zq_mutex);
639 			for (int i = 0; i < count; i++) {
640 				batch[i]->qs_completed = B_TRUE;
641 			}
642 			/* Collect processing rate data */
643 			queue->zq_stats.cost_processed += batch_cost;
644 			queue->zq_stats.nsec_used += nsec;
645 			advance_indexes(queue);
646 			pthread_mutex_unlock(&queue->zq_mutex);
647 		}
648 	}
649 	return (NULL);
650 }
651 
652 /*
653  * Locking: must be called with the dispatch mutex held
654  *
655  * Skips the wakeup if tp_unclaimed == 0.
656  */
657 static inline void
maybe_wake_worker(void)658 maybe_wake_worker(void)
659 {
660 	pool.tp_dispatch_requested = B_FALSE;
661 	if (atomic_load_64(&pool.tp_unclaimed) > 0) {
662 		pthread_mutex_lock(&pool.tp_pool_mutex);
663 		pthread_cond_signal(&pool.tp_wake_worker);
664 		pthread_mutex_unlock(&pool.tp_pool_mutex);
665 	}
666 }
667 
668 static inline struct timespec
timeout_deadline(void)669 timeout_deadline(void)
670 {
671 	struct timespec expire;
672 	struct timeval tv;
673 
674 	if (gettimeofday(&tv, NULL) != 0)
675 		err(1, "couldn't gettimeofday()");
676 	uint64_t nsec = tv.tv_usec * 1000 + DISPATCH_BACKUP_NSEC;
677 	expire.tv_sec = tv.tv_sec + nsec / NANOSEC;
678 	expire.tv_nsec = nsec % NANOSEC;
679 	return (expire);
680 }
681 
682 /*
683  * The enqueue notification pacing thread, which converts a notification
684  * from an enqueuer into a possible worker wakeup roughly ENQUEUE_DELAY_NSEC
685  * later. The delay facilitates larger batch sizes and keeps enqueuers on a
686  * less-contested mutex.
687  *
688  * The condwait timeout is necessary because the tp_unclaimed count is not
689  * the final word on whether there is actually any work to claim. It is
690  * calculated rigorously. However, it's a bare atomic and therefore
691  * potentially out of date at any given moment. A backup strategy is
692  * necessary to restart processing in the event of a race.
693  */
694 static void *
dispatch_worker(void * nope)695 dispatch_worker(void *nope)
696 {
697 	(void) nope;
698 	pthread_mutex_lock(&pool.tp_dispatch_mutex);
699 	while (B_TRUE) {
700 		while (!pool.tp_dispatch_requested) {
701 			int rc;
702 			struct timespec expire = timeout_deadline();
703 			rc = pthread_cond_timedwait(&pool.tp_request_dispatch,
704 			    &pool.tp_dispatch_mutex, &expire);
705 			if (rc == ETIMEDOUT) {
706 				maybe_wake_worker();
707 			} else if (rc != 0) {
708 				errx(1, "pthread_cond_timedwait() failed: %s",
709 				    strerror(rc));
710 			}
711 		}
712 		pthread_mutex_unlock(&pool.tp_dispatch_mutex);
713 		sleep_nsec(ENQUEUE_DELAY_NSEC);
714 		pthread_mutex_lock(&pool.tp_dispatch_mutex);
715 		maybe_wake_worker();
716 	}
717 	return (NULL);
718 }
719 
720 /*
721  * Implements both _enqueue and _fini. item == NULL for fini.
722  */
723 void
zstream_enqueue(zstream_queue_t * queue,queue_item_t * item)724 zstream_enqueue(zstream_queue_t *queue, queue_item_t *item)
725 {
726 	VERIFY3P(queue, !=, NULL);
727 	pthread_mutex_lock(&queue->zq_mutex);
728 
729 	VERIFY3B(queue->zq_disallow_enqueue, ==, B_FALSE);
730 	while (Q_FULL(queue)) {
731 		pthread_cond_wait(&queue->zq_cond.dequeued, &queue->zq_mutex);
732 	}
733 	VERIFY3B(queue->zq_disallow_enqueue, ==, B_FALSE);
734 	queue_slot_t *slot = &Q_SLOT(queue, queue->zq_ix.enqueue);
735 	if (item) {
736 		slot->qs_cost =
737 		    queue->zq_params.qp_cost(item, queue->zq_params.qp_context);
738 		slot->qs_completed = slot->qs_cost == 0;
739 		slot->qs_end_of_stream = B_FALSE;
740 		memcpy(slot->qs_item, item, queue->zq_params.qp_item_size);
741 	} else {
742 		slot->qs_cost = 0;
743 		slot->qs_completed = B_TRUE;
744 		slot->qs_end_of_stream = B_TRUE;
745 		queue->zq_disallow_enqueue = B_TRUE;
746 	}
747 	queue->zq_ix.enqueue++;
748 	atomic_inc_64(&pool.tp_unclaimed);
749 	if (slot->qs_cost == 0)
750 		advance_indexes(queue);
751 
752 #ifdef MONITOR_QUEUES
753 	update_monitor_data(queue);
754 #endif
755 
756 	pthread_mutex_unlock(&queue->zq_mutex);
757 
758 	pthread_mutex_lock(&pool.tp_dispatch_mutex);
759 	pool.tp_dispatch_requested = B_TRUE;
760 	pthread_cond_signal(&pool.tp_request_dispatch);
761 	pthread_mutex_unlock(&pool.tp_dispatch_mutex);
762 }
763 
764 void
zstream_queue_fini(zstream_queue_t * queue)765 zstream_queue_fini(zstream_queue_t *queue)
766 {
767 	zstream_enqueue(queue, NULL);
768 }
769 
770 /*
771  * This function is not public. The only way to destroy a queue through the
772  * public API is to call zstream_queue_fini(), wait for all items to be
773  * processed, and then dequeue all items. As a consequence, threads are
774  * entitled to assume that any queue with unprocessed work will not be
775  * removed without locking the pool mutex.
776  *
777  * Locking: the caller must NOT hold the queue lock. The pool mutex is held
778  * while destroying the queue.
779  */
780 static void
zstream_queue_destroy(zstream_queue_t * queue)781 zstream_queue_destroy(zstream_queue_t *queue)
782 {
783 	pthread_mutex_lock(&pool.tp_pool_mutex);
784 
785 #ifdef SHOW_BATCH_HISTOGRAMS
786 	print_batch_size_histogram(queue);
787 #endif
788 
789 	VERIFY0(pthread_mutex_destroy(&queue->zq_mutex));
790 	VERIFY0(pthread_cond_destroy(&queue->zq_cond.dequeued));
791 	if (pthread_cond_destroy(&queue->zq_cond.completed) != 0) {
792 		errx(1, "cannot destroy zstream_queue completed condition - "
793 		    "are you attempting to dequeue from multiple threads "
794 		    "simultaneously?");
795 	}
796 	pool.tp_num_queues--;
797 	if (pool.tp_num_queues > 0) {
798 		/* Gaps are not allowed in the tp_queues array */
799 		zstream_queue_t **qscan = &pool.tp_queues[0];
800 		int i = pool.tp_num_queues;
801 		while (*qscan != queue) { qscan++; i--; }
802 		if (i > 0)
803 			memmove(qscan, qscan + 1, i * sizeof (*qscan));
804 	}
805 	/*
806 	 * Items are allocated as a single block. The address of the first
807 	 * item field is in fact the start of the block.
808 	 */
809 	free(queue->zq_slots[0].qs_item);
810 	free(queue->zq_slots);
811 	queue->zq_slots = NULL;
812 	free(queue);
813 
814 	pthread_mutex_unlock(&pool.tp_pool_mutex);
815 }
816 
817 /*
818  * Locking: if more than one thread attempts to dequeue items
819  * simultaneously, disaster is likely. It will work fine until the end of
820  * the stream, at which point it becomes a tossup between a race condition
821  * with multiple attempts to destroy the whole queue vs. an attempt to
822  * delete a condition that another thread is waiting on. Hence the warning
823  * not to do multithreaded dequeues in zstream_queue.h.
824  *
825  * Returns B_TRUE if real data is returned, B_FALSE if the end of the queue
826  * has been reached.
827  */
828 boolean_t
zstream_dequeue(zstream_queue_t * queue,queue_item_t * item)829 zstream_dequeue(zstream_queue_t *queue, queue_item_t *item)
830 {
831 	pthread_mutex_lock(&queue->zq_mutex);
832 	while (queue->zq_ix.dequeue >= queue->zq_ix.complete) {
833 		pthread_cond_wait(&queue->zq_cond.completed, &queue->zq_mutex);
834 	}
835 	queue_slot_t *slot = &Q_SLOT(queue, queue->zq_ix.dequeue);
836 	queue->zq_ix.dequeue++;
837 
838 #ifdef MONITOR_QUEUES
839 	update_monitor_data(queue);
840 #endif
841 
842 	if (slot->qs_end_of_stream) {
843 		pthread_mutex_unlock(&queue->zq_mutex);
844 		/* Potential multi-dequeuer race point */
845 		zstream_queue_destroy(queue);
846 		return (B_FALSE);
847 	} else {
848 		memcpy(item, slot->qs_item, queue->zq_params.qp_item_size);
849 		pthread_cond_signal(&queue->zq_cond.dequeued);
850 		pthread_mutex_unlock(&queue->zq_mutex);
851 		return (B_TRUE);
852 	}
853 }
854 
855 #ifdef SHOW_BATCH_HISTOGRAMS
856 
857 /*
858  * Called only by zstream_queue_destroy(), under the pool mutex
859  *
860  * Prints one dense line per queue: the bucket counts for batch sizes 0
861  * through the largest batch observed. Now that ZQ_MAX_BATCH is 1024 and
862  * batches routinely reach the cap, that line can run to a thousand
863  * comma-separated values. Not very readable in a terminal, but still useful
864  * when plotted.
865  */
866 static void
print_batch_size_histogram(zstream_queue_t * queue)867 print_batch_size_histogram(zstream_queue_t *queue)
868 {
869 	int last_nonzero = 0;
870 	static int lines_printed = 0;
871 
872 	if (lines_printed++ == 0)
873 		fprintf(stderr, "\nBatch size histograms:\n");
874 	for (last_nonzero = ZQ_MAX_BATCH; last_nonzero >= 0; last_nonzero--) {
875 		if (queue->zq_histogram[last_nonzero] > 0)
876 			break;
877 	}
878 	fprintf(stderr, "Queue %d: ", queue->zq_id);
879 	const char *sep = "";
880 	for (int i = 0; i <= last_nonzero; i++) {
881 		fprintf(stderr, "%s%llu", sep,
882 		    (u_longlong_t)queue->zq_histogram[i]);
883 		sep = ", ";
884 	}
885 	fprintf(stderr, "\n");
886 	fflush(stderr);
887 }
888 
889 #endif
890 
891 #ifdef MONITOR_QUEUES
892 
893 #define	USEC_PER_JIFFY		10000
894 #define	SAMPLE_DURATION_USEC	1000000
895 #define	CPU_FIELD_WIDTH		14
896 
897 static inline void
update_depth_stats(depth_stats_t * ds,uint64_t depth)898 update_depth_stats(depth_stats_t *ds, uint64_t depth)
899 {
900 	double delta = (double)depth - ds->ds_mean;
901 	ds->ds_samples++;
902 	ds->ds_mean += delta / (double)ds->ds_samples;
903 	ds->ds_m2 += delta * ((double)depth - ds->ds_mean);
904 }
905 
906 static inline double
depth_stdev(const depth_stats_t * ds)907 depth_stdev(const depth_stats_t *ds)
908 {
909 	if (ds->ds_samples < 2)
910 		return (0.0);
911 	return (sqrt(ds->ds_m2 / (double)(ds->ds_samples - 1)));
912 }
913 
914 static inline void
initialize_monitor_data(zstream_queue_t * queue)915 initialize_monitor_data(zstream_queue_t *queue)
916 {
917 	zq_stats_t *stats = &queue->zq_stats;
918 	stats->depth = (depth_stats_t) {0};
919 	stats->depth.min = INT_MAX;
920 }
921 
922 /*
923  * Sample the current depth. Called on every enqueue and every dequeue, so
924  * the statistics are per-event rather than per-unit-time; a queue that sits
925  * full while nothing moves contributes no samples.
926  */
927 static inline void
update_monitor_data(zstream_queue_t * queue)928 update_monitor_data(zstream_queue_t *queue)
929 {
930 	uint64_t depth = queue->zq_ix.enqueue - queue->zq_ix.dequeue;
931 
932 	queue->zq_stats.depth.max = MAX(queue->zq_stats.depth.max, depth);
933 	queue->zq_stats.depth.min = MIN(queue->zq_stats.depth.min, depth);
934 	update_depth_stats(&queue->zq_stats.depth, depth);
935 }
936 
937 /*
938  * Monitor queue and CPU usage from a separate thread. This is all
939  * Linux-specific. It's largely a development remnant. Now that queue depths
940  * are standardized and batch sizes are sized to BATCH_TIME_NSEC, there are
941  * relatively few levers that need tuning.
942  *
943  * For each period, prints the minimum and maximum queue depth observed,
944  * followed by the mean depth and its standard deviation.
945  *
946  * Example output:
947  *
948  * CPU: 99.85% Queue 0: 745-4096 (2553 +/- 994) Queue 1: ...
949  */
950 static void *
cpu_and_queue_monitor(void * dummy)951 cpu_and_queue_monitor(void *dummy)
952 {
953 	(void) dummy;
954 	uint64_t period = SAMPLE_DURATION_USEC;
955 	struct timespec clock = {0};
956 	uint64_t start_us, end_us;
957 	uint64_t cpu_jif_prior = 0;
958 	uint64_t delta_jif, delta_cpu_jif;
959 	long unsigned int utime, stime;
960 	char buff[1024];
961 	FILE *fp;
962 
963 	fprintf(stderr, "Queue depths:\n");
964 
965 	while (B_TRUE) {
966 
967 		usleep(period);
968 
969 		fp = fopen("/proc/self/stat", "r");
970 		VERIFY3P(fp, !=, NULL);
971 		VERIFY3P(fgets(buff, sizeof (buff), fp), !=, NULL);
972 		fclose(fp);
973 		char *p = strrchr(buff, ')');
974 		VERIFY3P(p, !=, NULL);
975 		p += 2;  /* skip ") " and fields 3-13 */
976 		for (int i = 0; i < 11; i++) {
977 			p = strchr(p, ' ');
978 			VERIFY3P(p, !=, NULL);
979 			p++;
980 		}
981 		VERIFY3U(sscanf(p, "%lu %lu", &utime, &stime), ==, 2);
982 
983 		pthread_mutex_lock(&pool.tp_pool_mutex);
984 
985 		clock_gettime(CLOCK_MONOTONIC, &clock);
986 		end_us = clock.tv_sec * 1000000 + clock.tv_nsec / 1000;
987 
988 		if (cpu_jif_prior > 0) {
989 			delta_cpu_jif = utime + stime - cpu_jif_prior;
990 			delta_jif = (end_us - start_us) / USEC_PER_JIFFY;
991 			double cpu_pct = (double)delta_cpu_jif /
992 			    (pool.tp_num_threads * delta_jif);
993 			cpu_pct = MIN(cpu_pct, 0.9999); /* Don't print 100% */
994 			fprintf(stderr, "CPU: %5.2f%%   ", 100 * cpu_pct);
995 		} else {
996 			/* No CPU data available for the first interval */
997 			fprintf(stderr, "%*s", CPU_FIELD_WIDTH, "");
998 		}
999 
1000 		for (int i = 0; i < pool.tp_num_queues; i++) {
1001 			zstream_queue_t *q = pool.tp_queues[i];
1002 			pthread_mutex_lock(&q->zq_mutex);
1003 			zq_stats_t *stats = &q->zq_stats;
1004 			double avg = stats->depth.ds_mean;
1005 			double stdev = depth_stdev(&stats->depth);
1006 			if (stats->depth.min > stats->depth.max)
1007 				stats->depth.min = stats->depth.max = 0;
1008 			const char *plusminus = "\xc2\xb1";  /* UTF-8 */
1009 			fprintf(stderr, "Queue %d: %4d-%-4d (%4d %s %-4d)   ",
1010 			    q->zq_id, stats->depth.min, stats->depth.max,
1011 			    (int)avg, plusminus, (int)stdev);
1012 			fflush(stderr);
1013 			initialize_monitor_data(q);
1014 			pthread_mutex_unlock(&q->zq_mutex);
1015 		}
1016 
1017 		pthread_mutex_unlock(&pool.tp_pool_mutex);
1018 
1019 		fprintf(stderr, "\n");
1020 		fflush(stderr);
1021 
1022 		cpu_jif_prior = utime + stime;
1023 		start_us = end_us;
1024 	}
1025 	return (NULL);
1026 }
1027 
1028 #endif	/* MONITOR_QUEUES */
1029