Home | History | Annotate | Line # | Download | only in raidframe
      1 /*	$NetBSD: rf_diskqueue.c,v 1.67 2026/10/24 00:20:24 tls Exp $	*/
      2 /*
      3  * Copyright (c) 1995 Carnegie-Mellon University.
      4  * All rights reserved.
      5  *
      6  * Author: Mark Holland
      7  *
      8  * Permission to use, copy, modify and distribute this software and
      9  * its documentation is hereby granted, provided that both the copyright
     10  * notice and this permission notice appear in all copies of the
     11  * software, derivative works or modified versions, and any portions
     12  * thereof, and that both notices appear in supporting documentation.
     13  *
     14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
     15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
     16  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     17  *
     18  * Carnegie Mellon requests users of this software to return to
     19  *
     20  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     21  *  School of Computer Science
     22  *  Carnegie Mellon University
     23  *  Pittsburgh PA 15213-3890
     24  *
     25  * any improvements or extensions that they make and grant Carnegie the
     26  * rights to redistribute these changes.
     27  */
     28 
     29 /****************************************************************************
     30  *
     31  * rf_diskqueue.c -- higher-level disk queue code
     32  *
     33  * the routines here are a generic wrapper around the actual queueing
     34  * routines.  The code here implements thread scheduling, synchronization,
     35  * and locking ops (see below) on top of the lower-level queueing code.
     36  *
     37  * to support atomic RMW, we implement "locking operations".  When a
     38  * locking op is dispatched to the lower levels of the driver, the
     39  * queue is locked, and no further I/Os are dispatched until the queue
     40  * receives & completes a corresponding "unlocking operation".  This
     41  * code relies on the higher layers to guarantee that a locking op
     42  * will always be eventually followed by an unlocking op.  The model
     43  * is that the higher layers are structured so locking and unlocking
     44  * ops occur in pairs, i.e.  an unlocking op cannot be generated until
     45  * after a locking op reports completion.  There is no good way to
     46  * check to see that an unlocking op "corresponds" to the op that
     47  * currently has the queue locked, so we make no such attempt.  Since
     48  * by definition there can be only one locking op outstanding on a
     49  * disk, this should not be a problem.
     50  *
     51  * In the kernel, we allow multiple I/Os to be concurrently dispatched
     52  * to the disk driver.  In order to support locking ops in this
     53  * environment, when we decide to do a locking op, we stop dispatching
     54  * new I/Os and wait until all dispatched I/Os have completed before
     55  * dispatching the locking op.
     56  *
     57  * Unfortunately, the code is different in the 3 different operating
     58  * states (user level, kernel, simulator).  In the kernel, I/O is
     59  * non-blocking, and we have no disk threads to dispatch for us.
     60  * Therefore, we have to dispatch new I/Os to the scsi driver at the
     61  * time of enqueue, and also at the time of completion.  At user
     62  * level, I/O is blocking, and so only the disk threads may dispatch
     63  * I/Os.  Thus at user level, all we can do at enqueue time is enqueue
     64  * and wake up the disk thread to do the dispatch.
     65  *
     66  ****************************************************************************/
     67 
     68 #include <sys/cdefs.h>
     69 __KERNEL_RCSID(0, "$NetBSD: rf_diskqueue.c,v 1.67 2026/10/24 00:20:24 tls Exp $");
     70 
     71 #include <dev/raidframe/raidframevar.h>
     72 
     73 #include "rf_threadstuff.h"
     74 #include "rf_raid.h"
     75 #include "rf_diskqueue.h"
     76 #include "rf_alloclist.h"
     77 #include "rf_acctrace.h"
     78 #include "rf_etimer.h"
     79 #include "rf_general.h"
     80 #include "rf_debugprint.h"
     81 #include "rf_shutdown.h"
     82 #include "rf_cvscan.h"
     83 #include "rf_sstf.h"
     84 #include "rf_fifo.h"
     85 #include "rf_kintf.h"
     86 
     87 #include <sys/buf.h>
     88 
     89 static void rf_ShutdownDiskQueueSystem(void *);
     90 
     91 #ifndef RF_DEBUG_DISKQUEUE
     92 #define RF_DEBUG_DISKQUEUE 0
     93 #endif
     94 
     95 #if RF_DEBUG_DISKQUEUE
     96 #define Dprintf1(s,a)         if (rf_queueDebug) rf_debug_printf(s,(void *)((unsigned long)a),NULL,NULL,NULL,NULL,NULL,NULL,NULL)
     97 #define Dprintf2(s,a,b)       if (rf_queueDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),NULL,NULL,NULL,NULL,NULL,NULL)
     98 #define Dprintf3(s,a,b,c)     if (rf_queueDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),NULL,NULL,NULL,NULL,NULL)
     99 #else
    100 #define Dprintf1(s,a)
    101 #define Dprintf2(s,a,b)
    102 #define Dprintf3(s,a,b,c)
    103 #endif
    104 
    105 /*****************************************************************************
    106  *
    107  * the disk queue switch defines all the functions used in the
    108  * different queueing disciplines queue ID, init routine, enqueue
    109  * routine, dequeue routine
    110  *
    111  ****************************************************************************/
    112 
    113 static const RF_DiskQueueSW_t diskqueuesw[] = {
    114 	{"fifo",		/* FIFO */
    115 		rf_FifoCreate,
    116 		rf_FifoEnqueue,
    117 		rf_FifoDequeue,
    118 		rf_FifoPromote},
    119 
    120 	{"cvscan",		/* cvscan */
    121 		rf_CvscanCreate,
    122 		rf_CvscanEnqueue,
    123 		rf_CvscanDequeue,
    124 		rf_CvscanPromote},
    125 
    126 	{"sstf",		/* shortest seek time first */
    127 		rf_SstfCreate,
    128 		rf_SstfEnqueue,
    129 		rf_SstfDequeue,
    130 		rf_SstfPromote},
    131 
    132 	{"scan",		/* SCAN (two-way elevator) */
    133 		rf_ScanCreate,
    134 		rf_SstfEnqueue,
    135 		rf_ScanDequeue,
    136 		rf_SstfPromote},
    137 
    138 	{"cscan",		/* CSCAN (one-way elevator) */
    139 		rf_CscanCreate,
    140 		rf_SstfEnqueue,
    141 		rf_CscanDequeue,
    142 		rf_SstfPromote},
    143 
    144 };
    145 #define NUM_DISK_QUEUE_TYPES (sizeof(diskqueuesw)/sizeof(RF_DiskQueueSW_t))
    146 
    147 /* configures a single disk queue */
    148 
    149 static void
    150 rf_ShutdownDiskQueue(void *arg)
    151 {
    152 	RF_DiskQueue_t *diskqueue = arg;
    153 
    154 	rf_destroy_mutex2(diskqueue->mutex);
    155 }
    156 
    157 int
    158 rf_ConfigureDiskQueue(RF_Raid_t *raidPtr, RF_DiskQueue_t *diskqueue,
    159 		      RF_RowCol_t c, const RF_DiskQueueSW_t *p,
    160 		      RF_SectorCount_t sectPerDisk, dev_t dev,
    161 		      int maxOutstanding, RF_ShutdownList_t **listp,
    162 		      RF_AllocListElem_t *clList)
    163 {
    164 	diskqueue->col = c;
    165 	diskqueue->qPtr = p;
    166 	diskqueue->qHdr = (p->Create) (sectPerDisk, clList, listp);
    167 	diskqueue->dev = dev;
    168 	diskqueue->numOutstanding = 0;
    169 	diskqueue->queueLength = 0;
    170 	diskqueue->maxOutstanding = maxOutstanding;
    171 	diskqueue->curPriority = RF_IO_NORMAL_PRIORITY;
    172 	diskqueue->flags = 0;
    173 	diskqueue->raidPtr = raidPtr;
    174 	diskqueue->rf_cinfo = &raidPtr->raid_cinfo[c];
    175 	rf_init_mutex2(diskqueue->mutex, IPL_VM);
    176 	rf_ShutdownCreate(listp, rf_ShutdownDiskQueue, diskqueue);
    177 	return (0);
    178 }
    179 
    180 int
    181 rf_UpdateDiskQueue(RF_DiskQueue_t *diskqueue, RF_RaidDisk_t *disk)
    182 {
    183 	diskqueue->dev = disk->dev;
    184 	return(0);
    185 }
    186 
    187 static void
    188 rf_ShutdownDiskQueueSystem(void *arg)
    189 {
    190 	RF_Raid_t *raidPtr;
    191 
    192 	raidPtr = (RF_Raid_t *) arg;
    193 
    194 	pool_destroy(&raidPtr->pools.dqd);
    195 	pool_destroy(&raidPtr->pools.bufio);
    196 }
    197 
    198 int
    199 rf_ConfigureDiskQueueSystem(RF_ShutdownList_t **listp, RF_Raid_t *raidPtr,
    200 			    RF_Config_t *cfgPtr)
    201 
    202 {
    203 	size_t stripe_data_bytes, min_dqd, max_dqd, min_bufio, max_bufio;
    204 	u_int max_stripes_per_bp, c, o, stripes, recon_io;
    205 
    206 	c = raidPtr->numCol;
    207 	o = raidPtr->openings;
    208 	stripe_data_bytes = (size_t)raidPtr->Layout.dataSectorsPerStripe <<
    209 	    raidPtr->logBytesPerSector;
    210 	/* Total active stripes for 'openings' MAXPHYS I/Os + 1 parity op. */
    211 	max_stripes_per_bp = howmany(MAXPHYS, stripe_data_bytes) + 1;
    212 	stripes = o * max_stripes_per_bp + 1;
    213 	/* Active recon reads/writes (c-1 exclusive + c-1 forced + floating). */
    214 	recon_io = (raidPtr->Layout.map->SubmitReconBuffer == NULL) ?
    215 	    0 : 2 * (c - 1) + raidPtr->numFloatingReconBufs;
    216 
    217 	/*
    218 	 * Prime (xmin) for up to 2*c disk ops per active stripe + recon I/Os
    219 	 * so rf_EngineThread / rf_RaidIOThread never block in pool_get(PR_WAITOK).
    220 	 */
    221 	min_dqd = 2 * c * stripes + recon_io;
    222 	max_dqd = uimax(2 * min_dqd,
    223 	    (raidPtr->numCol + raidPtr->numSpare) * raidPtr->maxQueueDepth);
    224 
    225 	/* 1 buf_t per RF_DiskQueueData_t. */
    226 	min_bufio = min_dqd;
    227 	max_bufio = max_dqd;
    228 
    229 	rf_pool_init(raidPtr, raidPtr->poolNames.dqd, &raidPtr->pools.dqd, sizeof(RF_DiskQueueData_t),
    230 		     "dqd", min_dqd, max_dqd);
    231 	rf_pool_init(raidPtr, raidPtr->poolNames.bufio, &raidPtr->pools.bufio, sizeof(buf_t),
    232 		     "bufio", min_bufio, max_bufio);
    233 	rf_ShutdownCreate(listp, rf_ShutdownDiskQueueSystem, raidPtr);
    234 
    235 	return (0);
    236 }
    237 
    238 int
    239 rf_ConfigureDiskQueues(RF_ShutdownList_t **listp, RF_Raid_t *raidPtr,
    240 		       RF_Config_t *cfgPtr)
    241 {
    242 	RF_DiskQueue_t *diskQueues, *spareQueues;
    243 	const RF_DiskQueueSW_t *p;
    244 	RF_RowCol_t r,c;
    245 	int     rc, i;
    246 
    247 	raidPtr->maxQueueDepth = (uint8_t)cfgPtr->maxOutstandingDiskReqs;
    248 	if (raidPtr->maxQueueDepth == 0)
    249 		raidPtr->maxQueueDepth = RF_DEFAULT_QUEUE_DEPTH;
    250 
    251 	for (p = NULL, i = 0; i < NUM_DISK_QUEUE_TYPES; i++) {
    252 		if (!strcmp(diskqueuesw[i].queueType, cfgPtr->diskQueueType)) {
    253 			p = &diskqueuesw[i];
    254 			break;
    255 		}
    256 	}
    257 	if (p == NULL) {
    258 		RF_ERRORMSG2("Unknown queue type \"%s\".  Using %s\n", cfgPtr->diskQueueType, diskqueuesw[0].queueType);
    259 		p = &diskqueuesw[0];
    260 	}
    261 	raidPtr->qType = p;
    262 
    263 	diskQueues = RF_MallocAndAdd(
    264 	    (raidPtr->numCol + RF_MAXSPARE) * sizeof(*diskQueues),
    265 	    raidPtr->cleanupList);
    266 	if (diskQueues == NULL)
    267 		return (ENOMEM);
    268 	raidPtr->Queues = diskQueues;
    269 
    270 	for (c = 0; c < raidPtr->numCol; c++) {
    271 		rc = rf_ConfigureDiskQueue(raidPtr, &diskQueues[c],
    272 					   c, p,
    273 					   raidPtr->sectorsPerDisk,
    274 					   raidPtr->Disks[c].dev,
    275 					   raidPtr->maxQueueDepth,
    276 					   listp, raidPtr->cleanupList);
    277 		if (rc)
    278 			return (rc);
    279 	}
    280 
    281 	spareQueues = &raidPtr->Queues[raidPtr->numCol];
    282 	for (r = 0; r < raidPtr->maxQueue; r++) {
    283 		rc = rf_ConfigureDiskQueue(raidPtr, &spareQueues[r],
    284 					   raidPtr->numCol + r, p,
    285 					   raidPtr->sectorsPerDisk,
    286 					   raidPtr->Disks[raidPtr->numCol + r].dev,
    287 					   raidPtr->maxQueueDepth, listp,
    288 					   raidPtr->cleanupList);
    289 		if (rc)
    290 			return (rc);
    291 	}
    292 	return (0);
    293 }
    294 /* Enqueue a disk I/O
    295  *
    296  * In the kernel, I/O is non-blocking and so we'd like to have multiple
    297  * I/Os outstanding on the physical disks when possible.
    298  *
    299  * when any request arrives at a queue, we have two choices:
    300  *    dispatch it to the lower levels
    301  *    queue it up
    302  *
    303  * kernel rules for when to do what:
    304  *    unlocking req  :  always dispatch it
    305  *    normal req     :  queue empty => dispatch it & set priority
    306  *                      queue not full & priority is ok => dispatch it
    307  *                      else queue it
    308  */
    309 void
    310 rf_DiskIOEnqueue(RF_DiskQueue_t *queue, RF_DiskQueueData_t *req, int pri)
    311 {
    312 	RF_ETIMER_START(req->qtime);
    313 	RF_ASSERT(req->type == RF_IO_TYPE_NOP || req->numSector);
    314 	req->priority = pri;
    315 
    316 #if RF_DEBUG_DISKQUEUE
    317 	if (rf_queueDebug && (req->numSector == 0)) {
    318 		printf("Warning: Enqueueing zero-sector access\n");
    319 	}
    320 #endif
    321 	RF_LOCK_QUEUE_MUTEX(queue, "DiskIOEnqueue");
    322 	if (RF_OK_TO_DISPATCH(queue, req)) {
    323 		Dprintf2("Dispatching pri %d regular op to c %d (ok to dispatch)\n", pri, queue->col);
    324 		rf_DispatchKernelIO(queue, req);
    325 	} else {
    326 		queue->queueLength++;	/* increment count of number of requests waiting in this queue */
    327 		Dprintf2("Enqueueing pri %d regular op to c %d (not ok to dispatch)\n", pri, queue->col);
    328 		req->queue = (void *) queue;
    329 		(queue->qPtr->Enqueue) (queue->qHdr, req, pri);
    330 	}
    331 	RF_UNLOCK_QUEUE_MUTEX(queue, "DiskIOEnqueue");
    332 }
    333 
    334 
    335 /* get the next set of I/Os started */
    336 void
    337 rf_DiskIOComplete(RF_DiskQueue_t *queue, RF_DiskQueueData_t *req, int status)
    338 {
    339 	int     done = 0;
    340 
    341 	RF_LOCK_QUEUE_MUTEX(queue, "DiskIOComplete");
    342 	queue->numOutstanding--;
    343 	RF_ASSERT(queue->numOutstanding >= 0);
    344 
    345 	/* dispatch requests to the disk until we find one that we can't. */
    346 	/* no reason to continue once we've filled up the queue */
    347 	/* no reason to even start if the queue is locked */
    348 
    349 	while (!done && !RF_QUEUE_FULL(queue)) {
    350 		req = (queue->qPtr->Dequeue) (queue->qHdr);
    351 		if (req) {
    352 			Dprintf2("DiskIOComplete: extracting pri %d req from queue at c %d\n", req->priority, queue->col);
    353 			queue->queueLength--;	/* decrement count of number of requests waiting in this queue */
    354 			RF_ASSERT(queue->queueLength >= 0);
    355 			if (RF_OK_TO_DISPATCH(queue, req)) {
    356 				Dprintf2("DiskIOComplete: dispatching pri %d regular req to c %d (ok to dispatch)\n", req->priority, queue->col);
    357 				rf_DispatchKernelIO(queue, req);
    358 			} else {
    359 				/* we can't dispatch it, so just re-enqueue it.
    360 				   potential trouble here if disk queues batch reqs */
    361 				Dprintf2("DiskIOComplete: re-enqueueing pri %d regular req to c %d\n", req->priority, queue->col);
    362 				queue->queueLength++;
    363 				(queue->qPtr->Enqueue) (queue->qHdr, req, req->priority);
    364 				done = 1;
    365 			}
    366 		} else {
    367 			Dprintf1("DiskIOComplete: no more requests to extract.\n", "");
    368 			done = 1;
    369 		}
    370 	}
    371 
    372 	RF_UNLOCK_QUEUE_MUTEX(queue, "DiskIOComplete");
    373 }
    374 /* promotes accesses tagged with the given parityStripeID from low priority
    375  * to normal priority.  This promotion is optional, meaning that a queue
    376  * need not implement it.  If there is no promotion routine associated with
    377  * a queue, this routine does nothing and returns -1.
    378  */
    379 int
    380 rf_DiskIOPromote(RF_DiskQueue_t *queue, RF_StripeNum_t parityStripeID,
    381 		 RF_ReconUnitNum_t which_ru)
    382 {
    383 	int     retval;
    384 
    385 	if (!queue->qPtr->Promote)
    386 		return (-1);
    387 	RF_LOCK_QUEUE_MUTEX(queue, "DiskIOPromote");
    388 	retval = (queue->qPtr->Promote) (queue->qHdr, parityStripeID, which_ru);
    389 	RF_UNLOCK_QUEUE_MUTEX(queue, "DiskIOPromote");
    390 	return (retval);
    391 }
    392 
    393 RF_DiskQueueData_t *
    394 rf_CreateDiskQueueData(RF_IoType_t typ, RF_SectorNum_t ssect,
    395 		       RF_SectorCount_t nsect, void *bf,
    396 		       RF_StripeNum_t parityStripeID,
    397 		       RF_ReconUnitNum_t which_ru,
    398 		       void (*wakeF) (void *, int), void *arg,
    399 		       RF_AccTraceEntry_t *tracerec, RF_Raid_t *raidPtr,
    400 		       RF_DiskQueueDataFlags_t flags, const struct buf *mbp)
    401 {
    402 	RF_DiskQueueData_t *p;
    403 
    404 	p = pool_get(&raidPtr->pools.dqd, PR_WAITOK | PR_ZERO);
    405 	KASSERT(p != NULL);
    406 
    407 	/* Obtain a buffer from our own pool.  It is possible for the
    408 	   regular getiobuf() to run out of memory and return NULL.
    409 	   We need to guarantee that never happens, as RAIDframe
    410 	   doesn't have a good way to recover if memory allocation
    411 	   fails here.
    412 	*/
    413 	p->bp = pool_get(&raidPtr->pools.bufio, PR_WAITOK | PR_ZERO);
    414 	KASSERT(p->bp != NULL);
    415 
    416 	buf_init(p->bp);
    417 
    418 	SET(p->bp->b_cflags, BC_BUSY);	/* mark buffer busy */
    419 	if (mbp) {
    420 		SET(p->bp->b_flags, mbp->b_flags & rf_b_pass);
    421 		p->bp->b_proc = mbp->b_proc;
    422 	}
    423 
    424 	p->sectorOffset = ssect + rf_protectedSectors;
    425 	p->numSector = nsect;
    426 	p->type = typ;
    427 	p->buf = bf;
    428 	p->parityStripeID = parityStripeID;
    429 	p->which_ru = which_ru;
    430 	p->CompleteFunc = wakeF;
    431 	p->argument = arg;
    432 	p->next = NULL;
    433 	p->tracerec = tracerec;
    434 	p->priority = RF_IO_NORMAL_PRIORITY;
    435 	p->raidPtr = raidPtr;
    436 	p->flags = flags;
    437 	return (p);
    438 }
    439 
    440 void
    441 rf_FreeDiskQueueData(RF_DiskQueueData_t *p)
    442 {
    443 
    444 	buf_destroy(p->bp);
    445 
    446 	pool_put(&p->raidPtr->pools.bufio, p->bp);
    447 	pool_put(&p->raidPtr->pools.dqd, p);
    448 }
    449