/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "../solaris/nsc_thread.h"
#ifdef DS_DDICT
#include "../contract.h"
#endif
#include "rdc_io.h"
#include "rdc_bitmap.h"
#include "rdc_diskq.h"
#include "rdc_clnt.h"
extern nsc_io_t *_rdc_io_hc;
int rdc_diskq_coalesce = 0;
int
{
int rc = 0;
return (EIO);
"!rdc: nsc_reserve(%s) failed %d\n",
} else {
}
return (rc);
}
void
{
}
}
void
{
while (q->busycnt > 0)
}
void
{
q->busycnt++;
}
void
{
q->busycnt--;
if (q->busycnt == 0)
cv_broadcast(&q->busycv);
}
int
{
#ifdef DEBUG
#endif
int index;
#ifdef DEBUG
#endif
if (!IS_ENABLED(urdc))
continue;
NSC_MAXPATH) == 0)
return (index);
}
return (-1);
}
void
{
if (!RDC_IS_DISKQ(grp))
return;
}
void
{
#ifdef DEBUG
#endif
return;
}
#ifdef DEBUG
#else
;
/*EMPTY*/
#endif
}
}
}
/*
* nsc_open the diskq and attach
* the nsc_fd to krdc->diskqfd
*/
int
{
int sts;
char *diskqname;
int mutexheld = 0;
mutexheld++;
goto fail;
}
goto fail;
}
}
mutexheld--;
/* just test a reserve release */
if (!RDC_SUCCESS(sts)) {
goto fail;
}
goto fail;
}
return (0);
fail:
if (mutexheld)
return (-1);
}
/*
* rdc_count_vecs
* simply vec++'s until sb_addr is null
* returns number of vectors encountered
*/
int
{
int i = 0;
vecp++;
i++;
}
return (i+1);
}
/*
* rdc_setid2idx
* given setid, return index
*/
int
int index = 0;
break;
}
if (index >= rdc_max_sets)
index = -1;
return (index);
}
/*
* rdc_idx2setid
* given an index, return its setid
*/
int
{
}
/*
* rdc_fill_ioheader
* fill in all the stuff you want to save on disk
* at the beginnig of each queued write
*/
void
{
}
/*
* rdc_dump_iohdrs
* give back the iohdr list
* and clear out q->lastio
*/
void
{
io_hdr *p, *r;
p = q->iohdrs;
while (p) {
kmem_free(p, sizeof (*p));
q->hdrcnt--;
p = r;
}
q->hdrcnt = 0;
}
/*
* rdc_fail_diskq
* set flags, throw away q info
* clean up what you can
* wait for flusher threads to stop (taking into account this may be one)
* takes group_lock, so conf, many, and bitmap may not be held
*/
void
{
rdc_k_info_t *p;
if (IS_STATE(q, RDC_DISKQ_FAILED))
return;
if (!(flag & RDC_NOFAIL))
"disk queue failed");
}
/*
* quick stop of the flushers
* other cleanup is done on the un-failing of the diskq
*/
SET_LASTQTAIL(dq, 0);
else if (!(flag & RDC_GROUP_LOCKED))
if (!(flag & RDC_NOFAIL)) {
}
rdc_clr_flags(q, RDC_QUEUING);
q = &rdc_u_info[p->index];
if (!IS_ENABLED(q))
continue;
if (!(flag & RDC_NOFAIL)) {
}
rdc_clr_flags(q, RDC_QUEUING);
/* RDC_QUEUING is cleared in group_log() */
}
/* can't wait for myself to go away, I'm a flusher */
while (group->rdc_thrnum)
delay(2);
}
/*
* rdc_stamp_diskq
* write out diskq header info
* must have disk_qlock held
* if rsrvd flag is 0, the nsc_reserve is done
*/
int
{
disk_queue *q;
urdc->disk_queue);
mutex_exit(QLOCK(q));
mutex_enter(QLOCK(q));
return (-1);
}
if (!RDC_SUCCESS(rc)) {
&urdc->disk_queue[0]);
mutex_exit(QLOCK(q));
mutex_enter(QLOCK(q));
return (-1);
}
#ifdef DEBUG_DISKQ
"%x head: %d tail: %d size: %d nitems: %d blocks: %d",
#endif
if (!RDC_SUCCESS(rc)) {
if (!rsrvd)
mutex_exit(QLOCK(q));
mutex_enter(QLOCK(q));
return (-1);
}
(void) nsc_free_buf(head);
if (!rsrvd)
return (0);
}
/*
* rdc_init_diskq_header
* load initial values into the header
*/
void
{
int rc;
int type = 0;
/* save q type if this is a failure */
if (QSTATE(q) & RDC_QNOBLOCK)
type = RDC_QNOBLOCK;
SET_QNXTIO(q, QHEAD(q));
/* do this last, as this might be a failure. get the kernel state ok */
if (!RDC_SUCCESS(rc)) {
return;
}
}
/*
* rdc_unfail_diskq
* the diskq failed for some reason, lets try and re-start it
* the old stuff has already been thrown away
* should just be called from rdc_sync
*/
void
{
rdc_k_info_t *p;
rdc_clr_flags(q, RDC_ASYNC);
/* someone else won the race... */
if (!IS_STATE(q, RDC_DISKQ_FAILED)) {
return;
}
q = &rdc_u_info[p->index];
if (!IS_ENABLED(q))
continue;
rdc_clr_flags(q, RDC_ASYNC);
if (IS_STATE(q, RDC_QUEUING))
rdc_clr_flags(q, RDC_QUEUING);
}
/* real i/o to the queue */
/* clear RDC_AUXSYNCIP because we cannot halt a sync that's not here */
goto fail;
}
SET_QHDRCNT(dq, 0);
/* should be none, but.. */
fail:
return;
}
int
{
int rc;
&urdc->group_name[0]);
return (-1);
}
return (-1);
}
if (!RDC_SUCCESS(rc)) {
&urdc->group_name[0]);
return (-1);
}
return (0);
}
/*
* rdc_stop_diskq_flusher
*/
void
{
#ifdef DEBUG
#endif
/* save the queue info */
q = *qp;
/* lie a little */
/* drop locks to allow flushers to die */
while (group->rdc_thrnum)
delay(2);
*qp = q;
}
/*
* rdc_enable_diskq
* open the diskq
* and stamp the header onto it.
*/
int
{
disk_queue *q;
if (rdc_open_diskq(krdc) < 0)
goto fail;
mutex_enter(QLOCK(q));
mutex_exit(QLOCK(q));
goto fail;
}
SET_QNXTIO(q, QHEAD(q));
mutex_exit(QLOCK(q));
return (0);
fail:
/* caller has to fail diskq after dropping conf & many locks */
return (RDC_EQNOADD);
}
/*
* rdc_resume_diskq
* open the diskq and read the header
*/
int
{
disk_queue *q;
int rc = 0;
if (rdc_open_diskq(krdc) < 0) {
rc = RDC_EQNOADD;
goto fail;
}
mutex_enter(QLOCK(q));
if (rdc_read_diskq_header(krdc) < 0) {
SET_QSTATE(q, RDC_QBADRESUME);
rc = RDC_EQNOADD;
}
/* check diskq magic number */
if (QMAGIC(q) != RDC_DISKQ_MAGIC) {
SET_QSTATE(q, RDC_QBADRESUME);
rc = RDC_EQNOADD;
} else switch (QVERS(q)) {
#ifdef NSC_MULTI_TERABYTE
case RDC_DISKQ_VER_ORIG:
/* version 1 diskq header, upgrade to 64bit version */
SET_QSTATE(q, RDC_QBADRESUME);
rc = RDC_EQNOADD;
}
break;
#else
case RDC_DISKQ_VER_64BIT:
" diskq header newer than current version",
urdc->disk_queue);
SET_QSTATE(q, RDC_QBADRESUME);
rc = RDC_EQNOADD;
break;
#endif
case RDC_DISKQ_VERS:
/* okay, current version diskq */
break;
default:
SET_QSTATE(q, RDC_QBADRESUME);
rc = RDC_EQNOADD;
break;
}
if (IS_QSTATE(q, RDC_SHUTDOWN_BAD)) {
SET_QSTATE(q, RDC_QBADRESUME);
rc = RDC_EQNOADD;
}
/* bad, until proven not bad */
rc = RDC_EQNOADD;
}
SET_QNXTIO(q, QHEAD(q));
mutex_exit(QLOCK(q));
#ifdef DEBUG
urdc->disk_queue);
#endif
if (rc == 0)
return (0);
fail:
/* caller has to set the diskq failed after dropping it's locks */
return (rc);
}
int
{
int rc;
disk_queue *q;
/* grab both diskq locks as we are going to kill the flusher */
mutex_enter(QHEADLOCK(q));
mutex_enter(QLOCK(q));
}
/* let's make sure that the flusher has stopped.. */
mutex_exit(QLOCK(q));
mutex_exit(QHEADLOCK(q));
delay(5);
mutex_enter(QLOCK(q));
mutex_enter(QHEADLOCK(q));
}
/* write refcount to the bitmap */
goto fail;
}
if (!QEMPTY(q)) {
} else {
}
/* fill in diskq header info */
#ifdef DEBUG
#endif
/* to avoid a possible deadlock, release in order, and reacquire */
mutex_exit(QLOCK(q));
mutex_exit(QHEADLOCK(q));
goto fail; /* just stamp on the last suspend */
}
mutex_enter(QLOCK(q));
mutex_exit(QLOCK(q));
fail:
/* diskq already failed if stamp failed */
return (rc);
}
/*
* copy orig aio to copy, including the nsc_buf_t
*/
int
{
int rc;
return (0);
if (!RDC_SUCCESS(rc)) {
#ifdef DEBUG
#endif
return (rc);
}
if (!RDC_SUCCESS(rc)) {
#ifdef DEBUG
#endif
return (rc);
}
return (0);
}
/*
* rdc_qfill_shldwakeup()
* 0 if the memory queue has filled, and the low water
* mark has not been reached. 0 if diskq is empty.
* 1 if less than low water mark
* net_queue mutex is already held
*/
int
{
return (0);
return (0);
return (1);
return (0);
return (0);
return (0);
}
} else {
return (0);
}
}
#ifdef DEBUG_DISKQ_NOISY
#endif
return (1);
}
return (0);
}
/*
* rdc_diskq_enqueue
* enqueue one i/o to the diskq
* after appending some metadata to the front
*/
int
{
disk_queue *q;
int numvecs;
int i, j, rc = 0;
int retries = 0;
int qtail;
#ifdef DEBUG_WRITER_UBERNOISE
int qhead;
#endif
mutex_enter(QLOCK(q));
/*
* there is a thread that is blocking because the queue is full,
* don't try to set up this write until all is clear
* check before and after for logging or failed queue just
* in case a thread was in flight while the queue was full,
* and in the proccess of failing
*/
mutex_exit(QLOCK(q));
return (-1);
}
mutex_exit(QLOCK(q));
return (-1);
}
}
SET_QSTATE(q, QTAILBUSY);
/* we're only going to write the header to the queue */
} else {
/* find out how many vecs */
}
/*
* this, in conjunction with QTAILBUSY, will prevent
* premature dequeuing
*/
SET_LASTQTAIL(q, QTAIL(q));
if (!vec) {
} else {
}
if (vec)
if (iohdr)
CLR_QSTATE(q, QTAILBUSY);
SET_LASTQTAIL(q, 0);
mutex_exit(QLOCK(q));
return (ENOMEM);
}
/* now add the write itself */
i < numvecs; i++, j++) {
}
/* check for queue wrap, then check for overflow */
CLR_QSTATE(q, QTAILBUSY);
SET_LASTQTAIL(q, 0);
CLR_QSTATE(q, RDC_QFULL);
cv_broadcast(&q->qfullcv);
}
mutex_exit(QLOCK(q));
return (-1);
}
if (QTAILSHLDWRAP(q, iofbas)) {
/*
* just go back to the beginning of the disk
* it's not worth the trouble breaking up the write
*/
#ifdef DEBUG_DISKQWRAP
#endif
/*LINTED*/
WRAPQTAIL(q);
}
/*
* prepend the write's metadata
*/
/* check for tail < head */
/*
* don't allow any more writes to start
*/
SET_QSTATE(q, RDC_QFULL);
mutex_exit(QLOCK(q));
q->throttle_delay += delay_time;
retries++;
delay_time = 2;
if (print_msg) {
&urdc->disk_queue[0]);
print_msg = 0;
#ifdef DEBUG
#else
#endif
}
/*
* if this is a no-block queue, or this is a blocking
* queue that is not flushing. reset and log
*/
if ((QSTATE(q) & RDC_QNOBLOCK) ||
}
RDC_DOLOG | RDC_NOFAIL);
mutex_enter(QLOCK(q));
cv_broadcast(&q->qfullcv);
mutex_exit(QLOCK(q));
SET_LASTQTAIL(q, 0);
return (ENOMEM);
}
}
mutex_enter(QLOCK(q));
goto retry;
}
#ifdef DEBUG_WRITER_UBERNOISE
#endif
/* update tail pointer, nitems on queue and blocks on queue */
INC_QNITEMS(q, 1);
/* increment counter for i/o blocks only */
if (QNITEMS(q) > q->nitems_hwm)
q->nitems_hwm = QNITEMS(q);
if (QBLOCKS(q) > q->blocks_hwm)
q->blocks_hwm = QBLOCKS(q);
CLR_QSTATE(q, RDC_QFULL);
cv_broadcast(&q->qfullcv);
}
mutex_exit(QLOCK(q));
/*
* if (krdc->io_kstats) {
* mutex_enter(krdc->io_kstats->ks_lock);
* kstat_waitq_enter(KSTAT_IO_PTR(krdc->io_kstats));
* mutex_exit(krdc->io_kstats->ks_lock);
* }
*/
if (_rdc_rsrv_diskq(group)) {
&urdc->disk_queue[0]);
mutex_enter(QLOCK(q));
CLR_QSTATE(q, QTAILBUSY);
SET_LASTQTAIL(q, 0);
mutex_exit(QLOCK(q));
return (-1);
}
if (!RDC_SUCCESS(rc)) {
goto fail;
}
/* move vec and write to queue */
#ifdef DEBUG_WRITER_UBERNOISE
"qtail: %d, len: %d contents: %c%c%c%c%c",
#endif
if (!RDC_SUCCESS(rc)) {
goto fail;
}
mutex_enter(QLOCK(q));
SET_LASTQTAIL(q, 0);
CLR_QSTATE(q, QTAILBUSY);
mutex_exit(QLOCK(q));
fail:
/*
* return what should be returned
* the aio is returned in _rdc_write after status is gathered.
*/
if (qbuf)
(void) nsc_free_buf(qbuf);
/* free the iohdr and the vecs */
if (iohdr)
if (vec)
/* if no flusher running, start one */
return (rc);
}
/*
* place this on the pending list of io_hdr's out for flushing
*/
void
{
disk_queue *q = NULL;
#ifdef DEBUG
io_hdr *p;
#endif
/* paranoia */
mutex_enter(QLOCK(q));
#ifdef DEBUG /* AAAH! double flush!? */
p = q->iohdrs;
while (p) {
mutex_exit(QLOCK(q));
return;
}
}
#endif
q->hdrcnt = 1;
mutex_exit(QLOCK(q));
return;
}
q->hdrcnt++;
mutex_exit(QLOCK(q));
return;
}
/*
* mark an io header as flushed. If it is the qhead,
* then update the qpointers
* free the io_hdrs
* called after the bitmap is cleared by flusher
*/
void
{
disk_queue *q = NULL;
int found = 0;
int cnt = 0;
#ifndef NSC_MULTI_TERABYTE
if (qpos < 0) /* not a diskq offset */
return;
#endif
mutex_enter(QLOCK(q));
/* find outstanding io_hdr */
while (hp) {
found++;
break;
}
cnt++;
p = hp;
}
if (!found) {
#ifdef DEBUG
#endif
mutex_exit(QLOCK(q));
return;
}
mutex_exit(QLOCK(q));
return;
}
/* mark it as flushed */
/*
* if it is the head pointer, travel the list updating the queue
* pointers until the next unflushed is reached, freeing on the way.
*/
#ifdef DEBUG_FLUSHER_UBERNOISE
" qpos %d hpos %d len %d flag 0x%x iostatus %x setid %d",
#endif
} else {
}
DEC_QNITEMS(q, 1);
if (QHEADSHLDWRAP(q)) { /* simple enough */
#ifdef DEBUG_DISKQWRAP
#endif
/*LINTED*/
WRAPQHEAD(q);
}
/* get rid of the iohdr */
} else {
q->hdr_last = p;
}
q->hdrcnt--;
}
!(IS_QSTATE(q, RDC_QDISABLEPEND))) {
#ifdef DEBUG_FLUSHER_UBERNOISE
#endif
SET_QNXTIO(q, QHEAD(q));
}
/* wakeup any blocked enqueue threads */
cv_broadcast(&q->qfullcv);
mutex_exit(QLOCK(q));
}
/*
* put in whatever useful checks we can on the io header
*/
int
{
goto bad;
return (1);
bad:
#ifdef DEBUG
#else
#endif
return (0);
}
/*
* rdc_netqueue_insert()
* add an item to a netqueue. No locks necessary as it should only
* be used in a single threaded manor. If that changes, then
* a lock or assertion should be done here
*/
void
{
/* paranoid check for bit set */
} else {
}
q->nitems++;
if (q->nitems > q->nitems_hwm) {
q->nitems_hwm = q->nitems;
}
if (q->blocks > q->blocks_hwm) {
q->nitems_hwm = q->blocks;
}
}
/*
* rdc_fill_aio(aio, hdr)
* take the pertinent info from an io_hdr and stick it in
* an aio, including seq number, abuf.
*/
void
{
} else {
}
return;
return;
}
}
}
#ifdef DEBUG
int maxaios_perbuf = 0;
int midaios_perbuf = 0;
int aveaios_perbuf = 0;
int totaios_perbuf = 0;
int buf2qcalls = 0;
void
{
if (totaios_perbuf < 0) {
maxaios_perbuf = 0;
midaios_perbuf = 0;
aveaios_perbuf = 0;
totaios_perbuf = 0;
buf2qcalls = 0;
}
if (items > maxaios_perbuf)
totaios_perbuf += items;
}
#endif
/*
* rdc_discard_tmpq()
* free up the passed temporary queue
* NOTE: no cv's or mutexes have been initialized
*/
void
{
if (q == NULL)
return;
while (q->net_qhead) {
}
}
q->nitems--;
}
kmem_free(q, sizeof (*q));
}
/*
* rdc_diskq_buf2queue()
* take a chunk of the diskq, parse it and assemble
* a chain of rdc_aio_t's.
* updates QNXTIO()
*/
{
int nullbuf = 0;
int vlen;
long bufcnt = 0;
int nullblocks = 0;
return (NULL);
return (NULL);
}
#ifdef DEBUG_FLUSHER_UBERNOISE
#endif
/* CONSTCOND */
while (1) {
fail = 0;
goto fail;
}
#ifdef DEBUG_FLUSHER_UBERNOISE
#endif
break;
if (vlen <= 0) {
vecp++;
break;
}
/* get the iohdr information */
"!SNDR: unable to alocate net queue header");
goto fail;
}
if (!rdc_iohdr_ok(hdr)) {
"!unable to retrieve i/o data from queue %s "
#ifdef DEBUG_DISKQ
#endif
goto fail;
}
/* out of buffer, set nxtio to re read this last hdr */
break;
}
goto fail;
}
if (!nullbuf) {
/* move to next iohdr in big buf */
}
goto fail;
}
fail = 0;
goto fail;
}
/* no more buffer, skip the below logic */
break;
}
/* abuf = NULL; */
}
/* free extraneous header */
if (hdr) {
}
/*
* probably won't happen, but if we didn't goto fail, but
* we don't contain anything meaningful.. return NULL
* decide
*/
return (NULL);
}
#ifdef DEBUG
buf2qcalls++;
#endif
fail = 0;
goto fail;
}
return (netq);
fail:
if (hdr) {
}
if (netq) {
/* the never can happen case ... */
(void) nsc_free_buf(buf);
}
}
if (fail) { /* real failure, not just state change */
#ifdef DEBUG
urdc->disk_queue);
#endif
}
return (NULL);
}
/*
* rdc_diskq_unqueue
* remove one chunk from the diskq belonging to
* rdc_k_info[index]
* updates the head and tail pointers in the disk header
* but does not write. The header should be written on ack
* flusher should free whatever..
*/
{
int nullhandle = 0;
disk_queue *q = NULL;
return (NULL);
if (!aio) {
return (NULL);
}
if (!iohdr) {
return (NULL);
}
mutex_enter(QLOCK(q));
rdc_set_qbusy(q); /* make sure no one disables the queue */
mutex_exit(QLOCK(q));
if (_rdc_rsrv_diskq(group)) {
urdc->disk_queue);
goto fail;
}
mutex_enter(QHEADLOCK(q));
mutex_enter(QLOCK(q));
rdc_clr_qbusy(q);
mutex_exit(QLOCK(q));
mutex_exit(QHEADLOCK(q));
return (NULL);
}
if (QNXTIOSHLDWRAP(q)) {
#ifdef DEBUG_DISKQWRAP
#endif
/*LINTED*/
WRAPQNXTIO(q);
}
/* read the metainfo at q->nxt_io first */
mutex_exit(QHEADLOCK(q));
rdc_clr_qbusy(q);
mutex_exit(QLOCK(q));
return (NULL);
}
/*
* have to drop the lock here, sigh. Cannot block incoming io
* we have to wait until after this read to find out how
* much to increment QNXTIO. Might as well grab the seq then too
*/
mutex_exit(QLOCK(q));
#ifdef DEBUG_DISKQ
#endif
delay(5);
mutex_enter(QLOCK(q));
}
mutex_exit(QLOCK(q));
#ifdef DEBUG_DISKQ
#endif
mutex_exit(QHEADLOCK(q));
goto fail;
}
/* XXX process buffer here, creating rdc_aio_t's */
mutex_enter(QLOCK(q));
/* update the next pointer */
nullhandle = 1;
} else {
}
mutex_exit(QLOCK(q));
mutex_exit(QHEADLOCK(q));
#ifdef DEBUG_FLUSHER_UBERNOISE
#endif
if (nullhandle) /* nothing to get from queue */
goto nullbuf;
/* now that we know how much to get (iohdr.dat.len), get it */
/* and get somewhere to keep it for a bit */
urdc->disk_queue);
goto fail;
}
/* move it on over... */
if (!RDC_SUCCESS(rc2)) {
#ifdef DEBUG
#endif
goto fail;
}
/* let go of the real buf, we've got the abuf */
(void) nsc_free_buf(buf);
/* Hack in the original sb_pos */
/* skip the RDC_HANDLE_LIMITS check */
if (nullhandle) {
}
/* set up the rest of the aio values, seq set above ... */
#ifdef DEBUG
#endif
goto fail;
}
h = &q->disk_hdr.h;
"magic: %x\nstate: %d\nhead_offset: %d\n"
"tail_offset: %d\ndisk_size: %d\nnitems: %d\nblocks: %d\n",
#endif
mutex_enter(QLOCK(q));
rdc_clr_qbusy(q);
mutex_exit(QLOCK(q));
#ifdef DEBUG_FLUSHER_UBERNOISE
if (!nullhandle) {
" contents: %c%c%c%c%c pos: %d len: %d",
} else {
}
#endif
return (aio);
fail:
if (aio)
if (iohdr)
if (buf)
(void) nsc_free_buf(buf);
if (abuf)
(void) nsc_free_buf(abuf);
#ifdef DEBUG
#endif
mutex_enter(QLOCK(q));
rdc_clr_qbusy(q);
mutex_exit(QLOCK(q));
return (NULL);
}
int
{
char *group;
int index;
if ((rdc_lookup_bitmap(diskq) >= 0) ||
(rdc_lookup_configured(diskq) >= 0)) {
return (1);
}
if (!IS_ENABLED(urdc))
continue;
/* same diskq different group */
return (1);
}
}
/* last, but not least, lets see if someone is getting really funky */
return (1);
}
return (0);
}
#ifdef DEBUG
int maxlen = 0;
int avelen = 0;
int totalen = 0;
int lencalls = 0;
void
{
if (lencalls == 0) {
lencalls = 1;
avelen = 0;
maxlen = 0;
totalen = 0;
}
}
#endif
/*
* rdc_calc_len()
* returns the size of the diskq that can be read for dequeuing
* always <= RDC_MAX_DISKQREAD
*/
int
{
/* ---H-----N-----T--- */
/* ---T-----H-----N--- */
} else { /* should never happen */
}
}
#ifdef DEBUG
lencalls++;
#endif
return ((int)len);
}
/*
* lie a little if we can, so we don't get tied up in
* _nsc_wait_dbuf() on the next read. sb_len MUST be
* restored before nsc_free_buf() however, or we will
* be looking at memory leak city..
* so update the entire queue with the info as well
* and the one that ends up freeing it, can fix the len
* IMPORTANT: This assumes that we are not cached, in
* 3.2 caching was turned off for data volumes, if that
* changes, then this must too
*/
void
{
rdc_aio_t *p;
int len;
return;
}
p = q->net_qhead;
do {
p = p->next;
} while (p);
}
/*
* rdc_read_diskq_buf()
* read a large as possible chunk of the diskq into a nsc_buf_t
* and convert it to a net_queue of rdc_aio_t's to be appended
* to the group's netqueue
*/
{
int len = 0;
int rc;
int fail = 0;
int offset = 0;
return (NULL);
}
if (_rdc_rsrv_diskq(group)) {
urdc->disk_queue);
return (NULL);
}
goto done;
}
/*
* real corner case here, we need to let the flusher wrap first.
* we've gotten too far ahead, so just delay and try again
*/
goto done;
}
if (QNXTIOSHLDWRAP(dq)) {
#ifdef DEBUG_DISKQWRAP
#endif
/*LINTED*/
WRAPQNXTIO(dq);
}
/* read the metainfo at q->nxt_io first */
goto done;
}
/*
* a write could be trying to get on the queue, or if
* the queue is really really small, a complete image
* of it could be on the net queue waiting for flush.
* the latter being a fairly stupid scenario and a gross
* misconfiguration.. but what the heck, why make the thread
* thrash around.. just pause a little here.
*/
if (len <= 0)
delay(50);
goto done;
}
#ifdef DEBUG_FLUSHER_UBERNOISE
#endif
#ifdef DEBUG_FLUSHER_UBERNOISE
#endif
delay(20);
}
/*
* one last check to see if we have gone logging, or should.
* we may have released the mutex above, so check again
*/
goto done;
}
if (!RDC_SUCCESS(rc)) {
fail++;
goto done;
}
/*
* convert buf to a net_queue. buf2queue will
* update the QNXTIO pointer for us, based on
* the last readable queue item
*/
#ifdef DEBUG_FLUSHER_UBERNOISE
#endif
done:
/* we don't need to retain the buf */
if (buf) {
(void) nsc_free_buf(buf);
}
if (fail) {
}
return (tmpnq);
}
/*
* rdc_dequeue()
* removes the head of the memory queue
*/
{
*rc = 0;
if (q == NULL)
return (NULL);
mutex_enter(&q->net_qlock);
#ifdef DEBUG
"rdc_dequeue(1): q %p, q blocks %" NSC_SZFMT
}
#endif
mutex_exit(&q->net_qlock);
(!(q->qfflags & RDC_QFILLSLEEP)) &&
}
goto done;
}
/* aio remove from q */
q->nitems--;
#ifdef DEBUG
" , qhead %p qtail %p",
}
}
#endif
mutex_exit(&q->net_qlock);
done:
mutex_enter(&q->net_qlock);
if (rdc_qfill_shldwakeup(krdc))
cv_broadcast(&q->qfcv);
/*
* clear EAGAIN if
* logging or q filler thread is sleeping or stopping altogether
* or if q filler thread is dead already
* or if syncing, this will return a null aio, with no error code set
* telling the flusher to die
*/
(q->qfill_sleeping == RDC_QFILL_DEAD) ||
*rc = 0;
}
mutex_exit(&q->net_qlock);
return (aio);
}
/*
* rdc_qfill_shldsleep()
* returns 1 if the qfilling code should cv_wait() 0 if not.
* reasons for going into cv_wait();
* there is nothing in the diskq to flush to mem.
* the memory queue has gotten too big and needs more flushing attn.
*/
int
{
return (1);
#ifdef DEBUG_DISKQ_NOISY
#endif
return (1);
}
#ifdef DEBUG_DISKQ_NOISY
#endif
return (1);
}
#ifdef DEBUG_DISKQ_NOISY
#endif
return (1);
}
/* stuck flushers ? */
#ifdef DEBUG_DISKQ_NOISY
#endif
return (1);
}
return (0);
}
/*
* rdc_join_netqueues(a, b)
* appends queue b to queue a updating all the queue info
* as it is assumed queue a is the important one,
* it's mutex must be held. no one can add to queue b
*/
void
{
#ifdef DEBUG
}
#endif
} else {
}
if (q->nitems > q->nitems_hwm) {
q->nitems_hwm = q->nitems;
}
if (q->blocks > q->blocks_hwm) {
q->blocks_hwm = q->blocks;
}
}
/*
* rdc_qfiller_thr() single thread that moves
* data from the diskq to a memory queue for
* the flusher to pick up.
*/
void
{
while (!(q->qfflags & RDC_QFILLSTOP)) {
if (!RDC_IS_DISKQ(grp) ||
(q->qfflags & RDC_QFILLSLEEP)) {
goto nulltmpq;
}
goto nulltmpq;
if ((q->qfflags & RDC_QFILLSLEEP) ||
goto nulltmpq;
}
mutex_enter(&q->net_qlock);
/* race with log, redundant yet paranoid */
if ((q->qfflags & RDC_QFILLSLEEP) ||
mutex_exit(&q->net_qlock);
goto nulltmpq;
}
rdc_join_netqueues(q, tmpq);
mutex_exit(&q->net_qlock);
/*
* sleep for a while if we can.
* the enqueuing or flushing code will
* wake us if if necessary.
*/
mutex_enter(&q->net_qlock);
while (rdc_qfill_shldsleep(krdc)) {
if (q->qfflags & RDC_QFILLSTOP) {
#ifdef DEBUG_DISKQ
"!rdc_qfiller_thr: recieved kill signal");
#endif
mutex_exit(&q->net_qlock);
goto done;
}
}
mutex_exit(&q->net_qlock);
}
done:
#ifdef DEBUG
#endif
q->qfflags &= ~RDC_QFILLSTOP;
}
int
{
int rc;
#ifdef DEBUG
#endif
rc = -1;
goto fail;
}
/* if the enable fails, this is bzero'ed */
#ifdef DEBUG
#endif
if (rc == RDC_EQNOADD) {
goto fail;
}
/* size lives in the diskq structure, already set by enable */
}
fail:
return (rc);
}
/*
*/
int
{
char *diskq;
int rc;
int index;
if (index < 0) {
rc = RDC_EALREADY;
goto failed;
}
rc = RDC_EQNOQUEUE;
goto failed;
}
do {
goto failed;
}
/* make sure that we have enough bitmap vol */
if (!RDC_SUCCESS(rc)) {
"!rdc_open_diskq: Bitmap reserve failed");
rc = RDC_EBITMAP;
goto failed;
}
goto failed;
}
rc = RDC_EQALREADY;
goto failed;
}
goto failed;
}
goto failed;
}
rc = RDC_EQNOADD;
}
return (rc);
}
int
{
SET_QNXTIO(q, QHEAD(q));
goto fail;
return (0);
fail:
return (-1);
}
/*
* inititalize the disk queue. This is a destructive
* operation that will not check for emptiness of the queue.
*/
int
{
int rc = 0;
int index;
if (index < 0) {
rc = RDC_EALREADY;
goto fail;
}
rc = RDC_EQUEISREP;
goto fail;
}
/*
* a couple of big "ifs" here. in the first implementation
* neither of these will be possible. This will come into
* play when we persist the queue across reboots
*/
if (group->rdc_writer) {
urdc->disk_queue);
rc = RDC_EQFLUSHING;
} else {
urdc->disk_queue);
rc = RDC_EQNOTEMPTY;
}
goto fail;
}
}
if (_rdc_init_diskq(krdc) < 0) {
goto fail;
}
}
return (0);
fail:
/* generic queue failure */
if (!rc) {
rc = RDC_EQINITFAIL;
}
return (rc);
}
int
{
rdc_k_info_t *p;
#ifdef DEBUG
#endif
mutex_enter(QLOCK(q));
rdc_dump_iohdrs(q);
/*
* nsc_close the queue and zero out the queue name
*/
rdc_wait_qbusy(q);
mutex_exit(QLOCK(q));
SET_QSIZE(q, 0);
}
#ifdef DEBUG
#endif
return (0);
}
/*
* remove this diskq regardless of whether it is draining or not
* stops the flusher by invalidating the qdata (ie, instant empty)
* remove the disk qeueue from the group, leaving the group with a memory
* queue.
*/
int
{
int rc;
int index;
if (index < 0) {
rc = RDC_EALREADY;
goto failed;
}
rc = RDC_EQNOQUEUE;
goto failed;
}
/*
* if (!IS_STATE(urdc, RDC_LOGGING)) {
* spcs_s_add(kstatus, RDC_EQNOTLOGGING,
* uparms->rdc_set->disk_queue);
* rc = RDC_EQNOTLOGGING;
* goto failed;
* }
*/
return (rc);
}
/*
* remove a diskq from a group.
* removal of a diskq from a set, or rather
* a set from a queue, is done by reconfigging out
* of the group. This removes the diskq from a whole
* group and replaces it with a memory based queue
*/
int
{
int index;
long blocks;
if (index < 0) {
return (RDC_EALREADY);
}
do {
urdc->disk_queue);
return (RDC_EQNOTLOGGING);
}
/*
* If there is no group or diskq configured, we can leave now
*/
return (0);
/*
* Wait if not QEMPTY or threads still active
*/
counter = 0;
/*
* Capture counters to determine if progress is being made
*/
/*
* Wait
*/
/*
* Has the group or disk queue gone away while delayed?
*/
return (0);
/*
* Are we still seeing progress?
*/
/*
* No progress see, decrement retry counter
*/
if (counter++ > NUM_RETRIES) {
/*
* No progress seen, increment retry counter
*/
return (rc);
}
} else {
/*
* Reset counter, as we've made progress
*/
counter = 0;
}
}
return (0);
}