rdc_dev.c revision fcf3ce441efd61da9bb2884968af01cb7c1452cc
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/nsc_thread.h>
#ifdef DS_DDICT
#include "../contract.h"
#endif
#include "rdc.h"
#include "rdc_io.h"
#include "rdc_bitmap.h"
/*
* Remote Dual Copy
*
* This file contains the nsctl io provider functionality for RDC.
*
* RDC is implemented as a simple filter module that pushes itself between
* user (SIMCKD, STE, etc.) and SDBC.
*/
static int _rdc_open_count;
int rdc_eio_nobmp = 0;
static nsc_io_t *_rdc_io_hr;
void _rdc_deinit_dev();
extern void rdc_unintercept_diskq(rdc_group_t *);
rdc_aio_t *rdc_aio_tbuf_get(void *, void *, int, int, int, int, int);
static nsc_buf_t *_rdc_alloc_handle(void (*)(), void (*)(),
void (*)(), rdc_fd_t *);
#ifdef DEBUG
int rdc_overlap_cnt;
int rdc_overlap_hnd_cnt;
#endif
static rdc_info_dev_t *rdc_devices;
/*
* _rdc_init_dev
* Initialise the io provider.
*/
int
{
if (_rdc_io_hc == NULL)
if (_rdc_io_hr == NULL)
if (!_rdc_io_hc || !_rdc_io_hr) {
return (ENOMEM);
}
return (0);
}
/*
* _rdc_deinit_dev
* De-initialise the io provider.
*
*/
void
{
int rc;
if (_rdc_io_hc) {
"rdc: nsc_unregister_io (high, cache) failed: %d",
rc);
}
if (_rdc_io_hr) {
"rdc: nsc_unregister_io (high, raw) failed: %d",
rc);
}
}
/*
* rdc_idev_open
* - Open the nsctl file descriptors for the data devices.
*
* Must be called with rdc_conf_lock held.
* id_sets is protected by rdc_conf_lock.
*/
static rdc_info_dev_t *
{
break;
}
if (!dp) {
if (!dp)
return (NULL);
return (NULL);
}
return (NULL);
}
rdc_devices = dp;
}
return (dp);
}
/*
* rdc_idev_close
* - Close the nsctl file descriptors for the data devices.
*
* Must be called with rdc_conf_lock and dp->id_rlock held.
* Will release dp->id_rlock before returning.
*
* id_sets is protected by rdc_conf_lock.
*/
static void
{
#ifdef DEBUG
int count = 0;
#endif
return;
}
/* external references must have gone */
/* unlink from chain */
/* unlink */
break;
}
}
/*
* Wait for all reserves to go away - the rpc server is
* running asynchronously with this close, and so we
* have to wait for it to spot that the krdc is !IS_ENABLED()
* and throw away the nsc_buf_t's that it has allocated
* and release the device.
*/
#ifdef DEBUG
if (!(++count % 16)) {
"_rdc_idev_close(%s): waiting for nsc_release",
}
/* waited for 20 seconds - too long - panic */
"_rdc_idev_close(%s, %p): lost nsc_release",
(void *)krdc);
}
#endif
}
}
}
}
/*
* This function provokes an nsc_reserve() for the device which
* if successful will populate krdc->maxfbas and urdc->volume_size
* via the _rdc_attach_fd() callback.
*/
void
{
int rc;
/*
* if the vol is already reserved,
* volume_size won't be populated on enable because
* it is a *fake* reserve and does not make it to
* _rdc_attach_fd(). So do it here.
*/
if (rc != 0) {
#ifdef DEBUG
"rdc_get_details: partsize failed (%d)", rc);
#endif /* DEBUG */
}
if (rc != 0) {
#ifdef DEBUG
"rdc_get_details: maxfbas failed (%d)", rc);
#endif /* DEBUG */
maxfbas = 0;
}
}
}
/*
* Should only be used by the config code.
*/
int
{
int index;
int rc;
char *pathname;
if (options & RDC_OPT_PRIMARY)
else
if (!IS_CONFIGURED(krdc))
break;
}
if (index == rdc_max_sets) {
#ifdef DEBUG
#endif
goto out;
}
#ifdef DEBUG
#endif
goto out;
}
goto open_fail;
}
/*
* Grab the device size and maxfbas now.
*/
out:
return (index);
return (index);
}
void
{
#ifdef DEBUG
"rdc_dev_close(%p): c_fd %p r_fd %p", (void *)krdc,
}
#endif
/* rdc_idev_close will release id_rlock */
}
if (_rdc_open_count <= 0) {
"rdc: _rdc_open_count corrupt: %d",
}
}
/*
* rdc_intercept
*
* Register for IO on this device with nsctl.
*
* For a 1-to-many primary we register for each krdc and let nsctl sort
* out which it wants to be using. This means that we cannot tell which
* krdc will receive the incoming io from nsctl, though we do know that
* at any one time only one krdc will be 'attached' and so get io from
* nsctl.
*
* So the krdc->many_next pointer is maintained as a circular list. The
* result of these multiple nsc_register_paths is that we will see a
* using a single krdc.
*
* The major advantage of this scheme is that nsctl sorts out all the
* rdc_fd_t's so that they can only point to krdc's that are currently
* active.
*/
int
{
char *pathname;
char *bitmap;
} else {
}
(void) rdc_unintercept(krdc);
return (ENXIO);
}
return (0);
}
static void
{
}
static void
{
}
static void
{
return;
}
/*
* rdc_unintercept
*
* Unregister for IO on this device.
*
* See comments above rdc_intercept.
*/
int
{
int err = 0;
int rc;
if (rc) {
}
}
if (rc) {
if (!err)
}
}
if (rc) {
}
}
/* Wait for all necessary _rdc_close() calls to complete */
}
/*
* Check there are no outstanding writes in progress.
* This can happen when a set is being disabled which
* is one of the 'one_to_many' chain, that did not
* intercept the original write call.
*/
for (;;) {
/*
* This doesn't happen very often,
* just delay a bit and re-look.
*/
delay(50);
} else {
break;
}
}
return (err);
}
/*
* _rdc_rlse_d
* Internal version of _rdc_rlse_devs(), only concerned with the
* data device, not the bitmap.
*/
static void
{
if (!krdc) {
return;
}
/* decrement count */
if (raw) {
if (cip->bi_ofailed > 0) {
cip->bi_ofailed--;
}
} else {
}
}
/*
* reset nsc_fd ownership back link, it is only set if
* we have really done an underlying reserve, not for
* failed (faked) reserves.
*/
} else {
}
/* release nsc_fd */
}
/* decrement count */
if (raw) {
}
} else {
if (rip->bi_ofailed > 0) {
rip->bi_ofailed--;
}
}
/*
* reset nsc_fd ownership back link, it is only set if
* we have really done an underlying reserve, not for
* failed (faked) reserves.
*/
} else {
}
/* release nsc_fd and any waiters */
}
} else {
(void *) krdc);
}
}
/*
* _rdc_rlse_devs
* Release named underlying devices and take care of setting the
* back link on the nsc_fd to the correct parent iodev.
*
* NOTE: the 'devs' argument must be the same as that passed to
* the preceding _rdc_rsrv_devs call.
*/
void
{
}
}
}
}
/*
* _rdc_rsrv_d
* Reserve device flagged, unless its companion is already reserved,
* in that case increase the reserve on the companion. Take care
* of setting the nsc_fd ownership back link to the correct parent
* iodev pointer.
*/
static int
{
_rdc_info_dev_t *p = NULL;
int other = 0;
int rc;
#ifdef DEBUG
(rid->bi_ofailed < 0) ||
(cid->bi_ofailed < 0)) {
"_rdc_rsrv_d: negative counts (rsrv %d %d orsrv %d %d)",
"_rdc_rsrv_d: negative counts (fail %d %d ofail %d %d)",
(void *) krdc);
}
#endif
/*
* If user wants to do a cache reserve and it's already
* raw reserved internally, we need to do a real nsc_reserve, so wait
* until the release has been done.
*/
}
/* select underlying device to use */
p = rid;
if (!raw) {
other = 1;
}
p = cid;
if (raw) {
other = 1;
}
}
/* just increment count and return if already reserved */
if (p && !RFAILED(p)) {
if (other) {
p->bi_orsrv++;
} else {
p->bi_rsrv++;
}
/* set nsc_fd ownership back link */
return (0);
}
/* attempt reserve */
if (!p) {
}
if (!p->bi_fd) {
/* rpc server raced with rdc_dev_close() */
return (EIO);
}
/*
* convert failed counts into reserved counts, and add
* in this reserve.
*/
p->bi_orsrv = p->bi_ofailed;
if (other) {
p->bi_orsrv++;
} else {
p->bi_rsrv++;
}
p->bi_ofailed = 0;
p->bi_failed = 0;
/* set nsc_fd ownership back link */
/*
* If this is the master, and the secondary is not
* failed, then just fake this external reserve so that
* we can do remote io to the secondary and continue to
* provide service to the client.
*
* Subsequent calls to _rdc_rsrv_d() will re-try the
* nsc_reserve() until it succeeds.
*/
/* Primary, so reverse sync needed */
"nsc_reserve failed");
rc = -1;
#ifdef DEBUG
"with rc == %d\n", rc);
#endif
} else {
rc = 0;
}
if (other) {
p->bi_ofailed++;
} else {
p->bi_failed++;
}
/*
* fake a maxfbas value for remote i/o,
* this will get reset when the next
* successful reserve happens as part
* of the rdc_attach_fd() callback.
*/
}
}
}
}
return (rc);
}
/*
* _rdc_rsrv_devs
* Reserve named underlying devices.
*
*/
int
{
int write = 0;
int rc = 0;
int got = 0;
if (!krdc) {
return (EINVAL);
}
if (rc == -1) {
/*
* we need to call rdc_write_state()
* after we drop the mutex
*/
write = 1;
rc = 0;
} else {
"rdc: nsc_reserve(%s) failed %d\n",
}
} else {
}
}
"rdc: nsc_reserve(%s) failed %d\n",
} else {
}
if (!RDC_SUCCESS(rc)) {
/* Undo any previous reserve */
if (got != 0)
}
}
if (write) {
}
return (rc);
}
/*
* Read from the remote end, ensuring that if this is a many group in
* slave mode that we only remote read from the secondary with the
* valid data.
*/
int
{
int rc;
if (flag & NSC_RDAHEAD) {
/*
* no point in doing readahead remotely,
* just say we did it ok - the client is about to
* throw this buffer away as soon as we return.
*/
return (NSC_DONE);
}
/*
* If this is a many group with a reverse sync in progress and
* so that we can do the remote io from the correct secondary.
*/
if (!IS_ENABLED(urdc))
continue;
break;
}
}
/* cannot do remote io without the remote node! */
goto read2;
}
/* wait for the remote end to have the latest data */
}
}
}
}
/* If read error keep trying every secondary until no more */
if (!IS_ENABLED(urdc))
continue;
goto read1;
}
}
return (rc);
}
/*
* _rdc_alloc_buf
* Allocate a buffer of data
*
* Returns NSC_DONE or NSC_HIT for success, NSC_PENDING for async
* I/O, > 0 is an error code.
*
* Description:
*/
int rdcbufs = 0;
static int
{
rdc_buf_t *h;
int ioflag;
int rc = 0;
return (EIO);
if (len == 0)
return (EINVAL);
/*
* Forbid writes to secondary unless logging.
*/
return (EIO);
}
}
/*
* Forbid any io to secondary if it needs a sync.
*/
return (EIO);
}
/*
* Forbid any io to primary if it needs a reverse sync
* and is not actively syncing.
*/
return (EIO);
}
/* Bounds checking */
#ifdef DEBUG
"rdc: Attempt to access beyond end of rdc volume");
#endif
return (EIO);
}
h = *ptr;
if (h == NULL) {
/* should never happen (nsctl does this for us) */
#ifdef DEBUG
#endif
if (h == NULL)
return (ENOMEM);
*ptr = h;
}
if (flag & NSC_NOBLOCK) {
"_rdc_alloc_buf: removing unsupported NSC_NOBLOCK flag");
flag &= ~(NSC_NOBLOCK);
}
/*
* Use remote io when:
* - local volume is failed
* - reserve status is failed
*/
} else {
if (!RDC_SUCCESS(rc)) {
/* Primary, so reverse sync needed */
} else {
/* Secondary, so forward sync needed */
}
"nsc_alloc_buf failed");
}
}
if (RDC_SUCCESS(rc)) {
/*
* If in slave and reading data, remote read on top of
* the buffer to ensure that we have the latest data.
*/
/*
* Set NSC_MIXED so that the
* cache will throw away this buffer when we free
* it since we have combined data from multiple
* sources into a single buffer.
*/
}
}
/*
* If nsc_alloc_buf above fails, or local volume is failed or
* bitmap is failed or reserve, then we fill the buf from remote
*/
if (flag & NSC_NODATA) {
h->rdc_flags |= RDC_REMOTE_BUF;
} else {
if (!vec) {
goto error;
}
/* single flat buffer */
/* null terminator */
h->rdc_flags |= RDC_REMOTE_BUF;
h->rdc_flags |= RDC_VEC_ALLOC;
}
} else {
}
}
if (!RDC_SUCCESS(rc)) {
}
return (rc);
}
/*
* _rdc_free_buf
*/
static int
{
int rc = 0;
if (h->rdc_bufp) {
}
if (!RDC_SUCCESS(rc)) {
#ifdef DEBUG
"_rdc_free_buf(%p): nsc_free_buf(%p) returned %d",
#endif
return (rc);
}
}
if (h->rdc_flags & RDC_VEC_ALLOC) {
}
}
if (h->rdc_anon) {
/* anon buffers still pending */
}
if (!RDC_SUCCESS(rc)) {
#ifdef DEBUG
"_rdc_free_buf(%p): _rdc_free_handle returned %d",
(void *) h, rc);
#endif
return (rc);
}
} else {
h->rdc_vsize = 0;
}
return (0);
}
/*
* _rdc_open
* Open a device
*
* Returns a token to identify the device.
*
* Description:
* Performs the housekeeping operations associated with an upper layer
* of the nsctl stack opening a device.
*/
/* ARGSUSED */
static int
{
#ifdef DEBUG
#endif
int index;
int bmp = 0;
int queue = 0;
if (!rfd)
return (ENOMEM);
/*
* Take config lock to prevent a race with the
* (de)configuration code.
*/
if (index < 0) {
if (index >= 0)
bmp = 1;
}
if (index < 0) {
if (index >= 0)
queue = 1;
}
if (index < 0) {
/* not found in config */
return (ENXIO);
}
#ifdef DEBUG
#endif
if (bmp) {
} else if (raw) {
} else if (!queue) {
}
if (bmp)
else if (queue)
else
return (0);
}
static int
{
}
static int
{
}
/*
* _rdc_close
* Close a device
*
* Always succeeds - returns 0
*
* Description:
* Performs the housekeeping operations associated with an upper layer
* of the sd stack closing a shadowed device.
*/
static int
{
/*
* we don't keep ref counts for the queue, so skip this stuff.
* we may not even have a valid krdc at this point
*/
if (queue)
goto queue;
if (bmp) {
} else if (!queue) {
}
}
return (0);
}
/*
* _rdc_alloc_handle
* Allocate a handle
*
*/
static nsc_buf_t *
{
rdc_buf_t *h;
h = kmem_zalloc(sizeof (*h), KM_SLEEP);
if (!h)
return (NULL);
if (!h->rdc_bufp) {
/*
* This is a real failure from the io provider below.
*/
kmem_free(h, sizeof (*h));
return (NULL);
} else {
/* EMPTY */
/*
* This is just a failed primary device where
* we can do remote io to the secondary.
*/
}
}
return (&h->rdc_bufh);
}
/*
* _rdc_free_handle
* Free a handle
*
*/
/* ARGSUSED */
static int
{
int rc;
mutex_destroy(&h->aio_lock);
if (h->rdc_bufp) {
if (!RDC_SUCCESS(rc))
return (rc);
}
return (0);
}
/*
* _rdc_attach
* Attach
*
* Returns 0 for success, errno on failure.
*
* Description:
*/
static int
{
int rc;
return (EINVAL);
return (EINVAL);
return (rc);
}
/*
* _rdc_detach
* Detach
*
* Returns 0 for success, always succeeds
*
* Description:
*/
static int
{
/*
* Flush the async queue if necessary.
*/
int tries = 1;
}
/* force disgard of possibly blocked flusher threads */
#ifdef DEBUG
#endif
do {
"RDC: async I/O pending and not drained for %s during detach",
#ifdef DEBUG
" head: 0x%p tail: 0x%p",
#endif
}
}
return (0);
}
/*
* _rdc_get_pinned
*
* only affects local node.
*/
static int
{
}
/*
* _rdc_discard_pinned
*
* only affects local node.
*/
static int
{
}
/*
* _rdc_partsize
*
* only affects the local node.
*/
static int
{
/* Always return saved size */
return (0);
}
/*
* _rdc_maxfbas
*
* only affects local node
*/
/* ARGSUSED */
static int
{
int rc = 0;
return (EINVAL);
if (rc == 0) {
}
} else {
/* Always return saved size */
}
return (rc);
}
/* ARGSUSED */
static int
{
}
/*
* _rdc_attach_fd
*
* called by nsctl as part of nsc_reserve() processing when one of
* SNDR's underlying file descriptors becomes available and metadata
* should be re-acquired.
*/
static int
{
int rc;
"SNDR: cannot get volume size of %s, error %d",
/* set volume size for the first time */
/*
* SNDR cannot yet cope with a volume being resized,
* so fail it.
*/
else
"volume resized");
}
}
"SNDR: cannot get max transfer size for %s, error %d",
} else if (maxfbas > 0) {
}
return (0);
}
/*
* _rdc_pinned
*
* only affects local node
*/
static void
{
}
/*
* _rdc_unpinned
*
* only affects local node.
*/
static void
{
}
/*
* _rdc_read
*
* read the specified data into the buffer - go remote if local down,
* or the remote end has more recent data because an reverse sync is
* in progress.
*/
static int
{
"_rdc_read: bounds check: io(handle) pos %" NSC_XSZFMT
}
if (flag & NSC_NOBLOCK) {
"_rdc_read: removing unsupported NSC_NOBLOCK flag");
flag &= ~(NSC_NOBLOCK);
}
if (!remote) {
}
}
}
static int
{
int rc = 0;
return (EINVAL);
goto done;
}
/*
* this check for RDC_SYNCING may seem redundant, but there is a window
* in rdc_sync, where an async set has not yet been transformed into a
* sync set.
*/
RDC_REMOTE(h) ||
/* sync mode, or remote io mode, or local device is dead */
if ((rc == 0) &&
nsc_size_t, len);
/*
* If the current I/O's position plus length is
* greater then the sync block position, only
* clear those blocks upto sync block position
*/
if (pos < syncblockpos) {
else
}
} else {
}
} else if (rc != 0) {
"net write failed");
}
/* async mode */
} else {
#ifdef DEBUG
"enqueue write failed for handle %p",
(void *) h);
#endif
return (EINVAL);
}
/*
* get rid of the aio_buf_t now, as this
* may not be the set that this rdc_buf
* was allocated on, we are done with it anyways
* enqueuing code frees the nsc_abuf
*/
rdc_aio_buf_del(h, krdc);
}
} else {
ASSERT(0);
}
done:
/*
* Toss the anonymous buffer if we have one allocated.
*/
if (anon) {
rdc_aio_buf_del(h, krdc);
}
}
return (rc);
}
/*
* _rdc_multi_write
*
* Send to multihop remote. Obeys 1 to many if present and we are crazy
* enough to support it.
*
*/
int
{
"_rdc_multi_write: bounds check: io(handle) pos %" NSC_XSZFMT
return (EINVAL);
}
/* if this is a 1 to many, set all the bits for all the sets */
do {
/* set the error, but try other sets */
}
if (!IS_ENABLED(urdc))
continue;
break;
}
}
if (flag & NSC_NOBLOCK) {
"_rdc_multi_write: removing unsupported NSC_NOBLOCK flag");
flag &= ~(NSC_NOBLOCK);
}
}
}
if (!IS_ENABLED(urdc))
continue;
rc = 0;
goto multiwrite1;
}
}
return (retval);
}
void
{
int rc2;
/*
* overload flag with error return if any
*/
if (!RDC_SUCCESS(rc2)) {
} else {
p->flag = 0;
}
}
/*
* _rdc_sync_write_thr
* syncronous write thread which writes to network while
* local write is occuring
*/
void
{
#ifdef DEBUG
#endif
int rc2;
int bitmask;
#ifdef DEBUG
if (!IS_ENABLED(urdc)) {
}
#endif
/*
* overload flag with error return if any
*/
if (!RDC_SUCCESS(rc2)) {
} else {
p->flag = 0;
}
}
/*
* _rdc_write
*
* Commit changes to the buffer locally and send remote.
*
* If this write is whilst the local primary volume is being synced,
* then we write the remote end first to ensure that the new data
* cannot be overwritten by a concurrent sync operation.
*/
static int
{
int remote = RDC_REMOTE(h);
int first;
int rsync;
int nthr;
int winddown;
int thrrc = 0;
nsthread_t *tp;
/* If this is the multi-hop secondary, move along to the primary */
if (!IS_ENABLED(urdc)) {
}
}
/*
* If this is a many group with a reverse sync in progress and
* so that we can do the remote io to the correct secondary
* before the local io.
*/
if (!IS_ENABLED(urdc))
continue;
break;
}
}
first = 1;
nthr = 0;
"_rdc_write: bounds check: io(handle) pos %" NSC_XSZFMT
}
/* if this is a 1 to many, set all the bits for all the sets */
do {
if (rdc_eio_nobmp) {
/* set the error, but try the other sets */
}
}
if (!IS_ENABLED(urdc))
continue;
break;
}
}
/* just in case we switch mode during write */
h->rdc_flags |= RDC_ASYNC_BUF;
}
if (BUF_IS_ASYNC(h)) {
/*
* We are async mode
*/
aio_buf_t *p;
goto localwrite;
}
/*
* overload remote as we don't want to do local
* IO later. forge ahead with async
*/
remote++;
}
goto localwrite;
}
if (p == NULL) {
#ifdef DEBUG
"rdc_alloc_buf aio_buf allocation failed");
#endif
goto localwrite;
}
mutex_enter(&h->aio_lock);
if (!RDC_SUCCESS(rc1)) {
#ifdef DEBUG
"rdc_alloc_buf NSC_ANON allocation failed"
" rc %d",
rc1);
#endif
mutex_exit(&h->aio_lock);
goto localwrite;
}
h->rdc_flags |= RDC_ASYNC_VEC;
mutex_exit(&h->aio_lock);
/*
* Copy buffer into anonymous buffer
*/
rc1 =
if (!RDC_SUCCESS(rc1)) {
#ifdef DEBUG
"_rdc_write: nsc_copy failed rc=%d state %x",
#endif
rdc_aio_buf_del(h, krdc);
"nsc_copy failure");
}
/*
* using a diskq, launch a thread to queue it
* and free the aio->h and aio
* if the thread fails, do it the old way (see localwrite)
*/
if (nthr >= SNDR_MAXTHREADS) {
#ifdef DEBUG
#endif
goto localwrite;
}
#ifdef DEBUG
"%p", (void *)h);
#endif
goto localwrite;
}
/* get a populated rdc_aio_t */
#ifdef DEBUG
"kmem_alloc failed bp aio (1)");
#endif
goto localwrite;
}
/* start the queue io */
#ifdef DEBUG
"_rdcwrite: nst_create failure");
#endif
} else {
nthr++;
}
/*
* the handle that is to be enqueued is now in
* the rdc_aio_t, and will be freed there.
* dump the aio_t now. If this is 1 to many
* we may not do this in _rdc_free_buf()
* if this was not the index that the rdc_buf_t
* was allocated on.
*/
rdc_aio_buf_del(h, krdc);
}
} /* end of async */
/*
* We try to overlap local and network IO for the sync case
* (we already do it for async)
* If one to many, we need to track the resulting nst_thread
* so we don't trash the nsc_buf on a free
* Start network IO first then do local (sync only)
*/
!BUF_IS_ASYNC(h)) {
/*
* if forward syncing, we must do local IO first
* then remote io. Don't spawn thread
*/
goto localwrite;
}
if (IS_ENABLED(utmp))
}
if (nthr >= SNDR_MAXTHREADS) {
#ifdef DEBUG
#endif
goto localwrite;
}
#ifdef DEBUG
#endif
goto localwrite;
}
#ifdef DEBUG
"_rdcwrite: nst_create failure");
#endif
} else {
nthr++;
}
}
if (!RDC_SUCCESS(rc1)) {
if (IS_PRIMARY(urdc))
/* Primary, so reverse sync needed */
else
/* Secondary, so sync needed */
"local write failed");
}
}
/*
* This is where we either enqueue async IO for the flusher
* or do sync IO in the case of an error in thread creation
* or we are doing a forward sync
* NOTE: if we are async, and using a diskq, we have
* already enqueued this write.
* _rdc_remote_write will end up enqueuueing to memory,
* or in case of a thread creation error above, try again
* enqueue the diskq write if thrrc == ENOEXEC
*/
thrrc = 0;
if (IS_ENABLED(utmp))
}
}
if (!RDC_SUCCESS(rc1)) {
}
}
/*
* If one to many, jump back into the loop to continue IO
*/
if (!IS_ENABLED(urdc))
continue;
h->rdc_flags &= ~RDC_ASYNC_BUF;
goto write1;
}
}
/*
* collect all of our threads if any
*/
if (nthr) {
/* wait for the threads */
}
/* collect status */
winddown = 0;
/*
* Get any error return from thread
*/
}
winddown++;
}
}
if (!RDC_SUCCESS(rc1)) {
/* rsync, so reverse sync needed already set */
"rsync local write failed");
/*
* only report the error if a remote error
* occurred as well.
*/
}
}
if (multi) {
/* Multi-hop secondary, just set bits in the bitmap */
}
}
static void
{
nsc_vec_t *v;
uchar_t *a;
int l;
"_rdc_bzero: bounds check: io(handle) pos %" NSC_XSZFMT
return;
}
if (!len)
return;
/* find starting point */
v = h->sb_vec;
/* zero */
while (len) {
if (!a) /* end of vec */
break;
l -= sz;
a += sz;
if (!l) {
v++;
a = v->sv_addr;
l = v->sv_len;
}
}
}
/*
* _rdc_zero
*
* Zero and commit the specified area of the buffer.
*
* If this write is whilst the local primary volume is being synced,
* then we write the remote end first to ensure that the new data
* cannot be overwritten by a concurrent sync operation.
*/
static int
{
int remote = RDC_REMOTE(h);
int first;
int rsync;
/* If this is the multi-hop secondary, move along to the primary */
if (!IS_ENABLED(urdc)) {
}
}
/*
* If this is a many group with a reverse sync in progress and
* so that we can do the remote io to the correct secondary
* before the local io.
*/
if (!IS_ENABLED(urdc))
continue;
break;
}
}
first = 1;
"_rdc_zero: bounds check: io(handle) pos %" NSC_XSZFMT
}
goto zero2;
}
/*
* We are async mode
*/
aio_buf_t *p;
goto localzero;
}
goto zero2;
}
goto localzero;
}
if (p == NULL) {
#ifdef DEBUG
"rdc_alloc_buf aio_buf allocation failed");
#endif
goto localzero;
}
mutex_enter(&h->aio_lock);
if (!RDC_SUCCESS(rc1)) {
#ifdef DEBUG
"rdc_alloc_buf NSC_ANON allocation failed rc %d",
rc1);
#endif
mutex_exit(&h->aio_lock);
goto localzero;
}
h->rdc_flags |= RDC_ASYNC_VEC;
mutex_exit(&h->aio_lock);
/*
* Copy buffer into anonymous buffer
*/
if (!RDC_SUCCESS(rc1)) {
#ifdef DEBUG
"_rdc_zero: nsc_zero failed rc=%d state %x",
#endif
rdc_aio_buf_del(h, krdc);
"nsc_zero failed");
}
} /* end of async */
if (flag & NSC_NOBLOCK) {
"_rdc_zero: removing unsupported NSC_NOBLOCK flag");
flag &= ~(NSC_NOBLOCK);
}
if (!RDC_SUCCESS(rc1)) {
/* Primary, so reverse sync needed */
"nsc_zero failed");
}
}
/*
* send new data to remote end - nsc_zero has zero'd
* the data in the buffer, or _rdc_bzero will be used below.
*/
/* bzero so that we can send new data to remote node */
}
if (IS_ENABLED(utmp))
}
}
if (!RDC_SUCCESS(rc1)) {
}
}
if (!IS_ENABLED(urdc))
continue;
goto zero1;
}
}
if (!RDC_SUCCESS(rc1)) {
/* rsync, so reverse sync needed already set */
"nsc_write failed");
/*
* only report the error if a remote error
* occurred as well.
*/
}
}
if (multi) {
/* Multi-hop secondary, just set bits in the bitmap */
}
}
/*
* _rdc_uncommit
* - refresh specified data region in the buffer to prevent the cache
* serving the scribbled on data back to another client.
*
* Only needs to happen on the local node. If in remote io mode, then
* just return 0 - we do not cache the data on the local node and the
* changed data will not have made it to the cache on the other node,
* so it has no need to uncommit.
*/
static int
{
int remote = RDC_REMOTE(h);
int rc = 0;
"_rdc_uncommit: bounds check: io(handle) pos %" NSC_XSZFMT
}
if (flag & NSC_NOBLOCK) {
"_rdc_uncommit: removing unsupported NSC_NOBLOCK flag");
flag &= ~(NSC_NOBLOCK);
}
if (!remote) {
}
if (!RDC_SUCCESS(rc))
return (rc);
}
/*
* _rdc_trksize
*
* only needs to happen on local node.
*/
static int
{
}
static nsc_def_t _rdc_fd_def[] = {
0, 0, 0
};
static nsc_def_t _rdc_io_def[] = {
"Provide", 0, 0,
0, 0, 0
};
static nsc_def_t _rdc_ior_def[] = {
"Provide", 0, 0,
0, 0, 0
};