mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-07-26 02:29:51 +02:00
3d239d3db5
* refactor: cleanup va_start/va_end - Missing va_end statements; - Incorrect va_copy ordering in BFR_VSNPRINTF; - Sanitize snprintf results or ensure buffer is initially \0'd. * refactor: introduce vasprintf Replace dynamic format string allocation (i.e., incrementing a buffer by a fixed amount until vsnprintf returns success) with vasprintf. Polyfill'ing for builds that do not include the GNU extension.
7337 lines
245 KiB
C
7337 lines
245 KiB
C
/* CCKDDASD.C (C) Copyright Roger Bowler, 1999-2012 */
|
|
/* (C) Copyright Greg Smith, 2002-2012 */
|
|
/* (C) and others 2013-2022 */
|
|
/* */
|
|
/* CCKD (Compressed CKD) Device Handler */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module contains device functions for compressed emulated */
|
|
/* count-key-data direct access storage devices. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#define _CCKDDASD_C_
|
|
#define _HDASD_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "devtype.h"
|
|
#include "opcode.h"
|
|
#include "dasdblks.h"
|
|
#include "cckddasd.h"
|
|
#include "ccwarn.h"
|
|
|
|
DISABLE_GCC_UNUSED_SET_WARNING;
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Global Variables */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
DLL_EXPORT CCKDBLK cckdblk; /* cckd global area */
|
|
|
|
char* compname [] = { "none", "zlib", "bzip2" };
|
|
CCKD_L2ENT empty_l2 [ CKD_NULLTRK_FMTMAX + 1 ][256] = {0};
|
|
CCKD64_L2ENT empty64_l2 [ CKD_NULLTRK_FMTMAX + 1 ][256] = {0};
|
|
|
|
DLL_EXPORT int gctab[5] = /* Garbage Collection parameters */
|
|
{
|
|
4096, /* critical 50% - 100% */
|
|
2048, /* severe 25% - 50% */
|
|
1024, /* moderate 12.5% - 25% */
|
|
512, /* light 6.3% - 12.5% */
|
|
256 /* none 0% - 6.3% */
|
|
};
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* CCKD global initialization */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_dasd_init( int argc, BYTE* argv[] )
|
|
{
|
|
int i, j; /* Loop indexes */
|
|
|
|
UNREFERENCED(argc);
|
|
UNREFERENCED(argv);
|
|
|
|
if (memcmp( &cckdblk.id, CCKDBLK_ID, sizeof( cckdblk.id )) == 0)
|
|
return 0;
|
|
|
|
/* Clear the cckdblk */
|
|
|
|
memset( &cckdblk, 0, sizeof( cckdblk ));
|
|
|
|
/* Initialize locks and conditions */
|
|
|
|
memcpy( &cckdblk.id, CCKDBLK_ID, sizeof( cckdblk.id ));
|
|
|
|
initialize_lock( &cckdblk.dhlock );
|
|
initialize_lock( &cckdblk.gclock );
|
|
initialize_lock( &cckdblk.dh_gc_lock );
|
|
initialize_lock( &cckdblk.ralock );
|
|
initialize_lock( &cckdblk.wrlock );
|
|
initialize_lock( &cckdblk.devlock );
|
|
initialize_lock( &cckdblk.trclock );
|
|
|
|
initialize_condition( &cckdblk.dhcond );
|
|
initialize_condition( &cckdblk.gccond );
|
|
initialize_condition( &cckdblk.racond );
|
|
initialize_condition( &cckdblk.wrcond );
|
|
initialize_condition( &cckdblk.devcond );
|
|
initialize_condition( &cckdblk.termcond );
|
|
|
|
/* Initialize trace table */
|
|
|
|
cckdblk.itrace = calloc( CCKD_DEF_NUM_TRACE, sizeof( CCKD_ITRACE ));
|
|
cckdblk.itracep = cckdblk.itrace;
|
|
cckdblk.itracex = cckdblk.itrace + CCKD_DEF_NUM_TRACE;
|
|
cckdblk.itracen = CCKD_DEF_NUM_TRACE;
|
|
cckdblk.itracec = 0;
|
|
|
|
/* Initialize some other variables */
|
|
|
|
cckdblk.ranbr = CCKD_DEF_RA_SIZE;
|
|
cckdblk.ramax = CCKD_DEF_RA;
|
|
cckdblk.wrmax = CCKD_DEF_WRITER;
|
|
cckdblk.dhmax = CCKD_DEF_DHMAX;
|
|
cckdblk.dhint = CCKD_DEF_DHINT;
|
|
cckdblk.dhstart = CCKD_DEF_DHSTART;
|
|
cckdblk.gcmax = CCKD_DEF_GCOL;
|
|
cckdblk.gcint = CCKD_DEF_GCINT;
|
|
cckdblk.gcparm = CCKD_DEF_GCPARM;
|
|
cckdblk.readaheads = CCKD_DEF_READAHEADS;
|
|
cckdblk.freepend = CCKD_DEF_FREEPEND;
|
|
|
|
#if defined( HAVE_ZLIB )
|
|
cckdblk.comps |= CCKD_COMPRESS_ZLIB;
|
|
#endif
|
|
#if defined( CCKD_BZIP2 )
|
|
cckdblk.comps |= CCKD_COMPRESS_BZIP2;
|
|
#endif
|
|
cckdblk.comp = 0xff;
|
|
cckdblk.compparm = -1;
|
|
|
|
/* Set the writer thread's priority just BELOW the CPU threads'
|
|
in order to minimize any potential impact from compression.
|
|
*/
|
|
cckdblk.wrprio = sysblk.cpuprio - 1;
|
|
|
|
/* Initialize the readahead queue */
|
|
|
|
cckdblk.ra1st = -1;
|
|
cckdblk.ralast = -1;
|
|
cckdblk.rafree = 0;
|
|
|
|
for (i=0; i < cckdblk.ranbr; i++)
|
|
cckdblk.ra[i].ra_idxnxt = i + 1;
|
|
|
|
cckdblk.ra[cckdblk.ranbr - 1].ra_idxnxt = -1;
|
|
|
|
/* Clear the empty L2 tables */
|
|
|
|
for (i=0; i <= CKD_NULLTRK_FMTMAX; i++)
|
|
for (j=0; j < 256; j++)
|
|
{
|
|
empty_l2 [i][j] . L2_trkoff = 0;
|
|
empty_l2 [i][j] . L2_len = i;
|
|
empty_l2 [i][j] . L2_size = i;
|
|
|
|
empty64_l2 [i][j] . L2_trkoff = 0;
|
|
empty64_l2 [i][j] . L2_len = i;
|
|
empty64_l2 [i][j] . L2_size = i;
|
|
}
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_dasd_init */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Perform global CCKD dasd termination (if appropriate) */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_dasd_term_if_appropriate()
|
|
{
|
|
int max;
|
|
|
|
/* Check if it's time to terminate yet */
|
|
obtain_lock( &cckdblk.devlock );
|
|
{
|
|
if (cckdblk.dev1st)
|
|
{
|
|
/* Not time yet; return without doing anything */
|
|
release_lock( &cckdblk.devlock );
|
|
return;
|
|
}
|
|
/* cckdblk.dev1st == NULL: time to globally terminate */
|
|
}
|
|
release_lock( &cckdblk.devlock );
|
|
|
|
/* Terminate all readahead threads... */
|
|
obtain_lock( &cckdblk.ralock );
|
|
{
|
|
max = cckdblk.ramax; /* Save current value */
|
|
cckdblk.ramax = 0; /* signal all threads to terminate */
|
|
while (cckdblk.ras > 0) /* wait for all threads to terminate */
|
|
{
|
|
broadcast_condition( &cckdblk.racond );
|
|
wait_condition( &cckdblk.termcond, &cckdblk.ralock );
|
|
}
|
|
cckdblk.ramax = max; /* Restore original value */
|
|
}
|
|
release_lock( &cckdblk.ralock );
|
|
|
|
/* Terminate all Dasd Hardener threads... (Should be only one!) */
|
|
if (cckdblk.dhint != 0)
|
|
{
|
|
obtain_lock( &cckdblk.dhlock );
|
|
{
|
|
max = cckdblk.dhmax; /* Save current value */
|
|
cckdblk.dhmax = 0; /* signal all threads to terminate */
|
|
while (cckdblk.dhs > 0) /* wait for all threads to terminate */
|
|
{
|
|
broadcast_condition( &cckdblk.dhcond );
|
|
wait_condition( &cckdblk.termcond, &cckdblk.dhlock );
|
|
}
|
|
cckdblk.dhmax = max; /* Restore original value */
|
|
}
|
|
release_lock( &cckdblk.dhlock );
|
|
}
|
|
|
|
/* Terminate all Garbage Collection threads... (Should be only one!) */
|
|
obtain_lock( &cckdblk.gclock );
|
|
{
|
|
max = cckdblk.gcmax; /* Save current value */
|
|
cckdblk.gcmax = 0; /* signal all threads to terminate */
|
|
while (cckdblk.gcs > 0) /* wait for all threads to terminate */
|
|
{
|
|
broadcast_condition( &cckdblk.gccond );
|
|
wait_condition( &cckdblk.termcond, &cckdblk.gclock );
|
|
}
|
|
cckdblk.gcmax = max; /* Restore original value */
|
|
}
|
|
release_lock( &cckdblk.gclock );
|
|
|
|
/* Terminate all writer threads... */
|
|
obtain_lock( &cckdblk.wrlock );
|
|
{
|
|
max = cckdblk.termwr; /* Save current value */
|
|
cckdblk.termwr = 1; /* signal all threads to terminate */
|
|
while (cckdblk.wrs > 0) /* wait for all threads to terminate */
|
|
{
|
|
broadcast_condition( &cckdblk.wrcond );
|
|
wait_condition( &cckdblk.termcond, &cckdblk.wrlock );
|
|
}
|
|
cckdblk.termwr = max; /* Restore original value */
|
|
}
|
|
release_lock( &cckdblk.wrlock );
|
|
|
|
} /* end function cckd_dasd_term */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed CKD dasd initialization */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_dasd_init_handler ( DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int i; /* Counter */
|
|
int fdflags; /* File flags */
|
|
char buf[32]; /* Work buffer */
|
|
|
|
UNREFERENCED( argc );
|
|
UNREFERENCED( argv );
|
|
|
|
/* Initialize the global cckd block if necessary */
|
|
if (memcmp( &cckdblk.id, CCKDBLK_ID, sizeof( cckdblk.id )))
|
|
cckd_dasd_init( 0, NULL );
|
|
|
|
/* Obtain area for cckd extension */
|
|
dev->cckd_ext = cckd = cckd_calloc( dev, "ext", 1, sizeof( CCKD_EXT ));
|
|
if (cckd == NULL)
|
|
return -1;
|
|
|
|
/* Initialize locks and conditions */
|
|
|
|
initialize_lock( &cckd->cckdiolock );
|
|
MSGBUF( buf, "&cckd->cckdiolock %1d:%04X", LCSS_DEVNUM );
|
|
set_lock_name( &cckd->cckdiolock, buf );
|
|
|
|
initialize_lock( &cckd->filelock );
|
|
MSGBUF( buf, "&cckd->filelock %1d:%04X", LCSS_DEVNUM );
|
|
set_lock_name( &cckd->filelock, buf );
|
|
|
|
initialize_condition( &cckd->cckdiocond );
|
|
|
|
/* Initialize some variables */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
cckd->L1idx = cckd->sfx = cckd->L2_active = -1;
|
|
dev->cache = cckd->free_idx1st = -1;
|
|
cckd->fd[0] = dev->fd;
|
|
fdflags = get_file_accmode_flags( dev->fd );
|
|
cckd->open[0] = (fdflags & O_RDWR) ? CCKD_OPEN_RW : CCKD_OPEN_RO;
|
|
for (i = 1; i <= CCKD_MAX_SF; i++)
|
|
{
|
|
cckd->fd[i] = -1;
|
|
cckd->open[i] = CCKD_OPEN_NONE;
|
|
}
|
|
cckd->cckd_maxsize = CCKD_MAXSIZE;
|
|
|
|
/* call the chkdsk function */
|
|
if (cckd_chkdsk (dev, 0) < 0)
|
|
return -1;
|
|
|
|
/* Perform initial read */
|
|
if (cckd_read_init (dev) < 0)
|
|
return -1;
|
|
if (cckd->fbadasd) dev->ckdtrksz = CFBA_BLKGRP_SIZE;
|
|
|
|
/* open the shadow files */
|
|
if (cckd_sf_init (dev) < 0)
|
|
{
|
|
WRMSG (HHC00300, "E", LCSS_DEVNUM);
|
|
return -1;
|
|
}
|
|
|
|
/* Update the device handler routines */
|
|
if (cckd->ckddasd)
|
|
dev->hnd = &cckd_dasd_device_hndinfo;
|
|
else
|
|
dev->hnd = &cfba_dasd_device_hndinfo;
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
/* Insert the device into the cckd device queue */
|
|
cckd_lock_devchain(1);
|
|
{
|
|
add_dev_to_cckd_chain_locked( dev );
|
|
}
|
|
cckd_unlock_devchain();
|
|
|
|
cckdblk.batch = dev->batch;
|
|
|
|
if (cckdblk.batch)
|
|
{
|
|
cckdblk.nostress = 1;
|
|
cckdblk.freepend = 0;
|
|
cckdblk.linuxnull = 1;
|
|
}
|
|
|
|
return 0;
|
|
} /* end function cckd_dasd_init_handler */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Close a Compressed CKD Device */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_dasd_close_device (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc, i; /* Return code, Loop index */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_dasd_close_device( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Wait for readaheads to finish */
|
|
obtain_lock(&cckdblk.ralock);
|
|
cckd->stopping = 1;
|
|
while (cckd->ras)
|
|
{
|
|
release_lock(&cckdblk.ralock);
|
|
USLEEP(1);
|
|
obtain_lock(&cckdblk.ralock);
|
|
}
|
|
release_lock(&cckdblk.ralock);
|
|
|
|
/* Flush the cache and wait for the writes to complete */
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cckd->stopping = 1;
|
|
cckd_flush_cache( dev );
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
rc = timed_wait_condition_relative_usecs(
|
|
&cckd->cckdiocond, &cckd->cckdiolock, 1000000, NULL );
|
|
cckd->cckdwaiters--;
|
|
cckd_flush_cache( dev );
|
|
if (EINTR == rc)
|
|
continue;
|
|
/* Prevent rare but possible hang at shutdown */
|
|
if (1
|
|
&& ETIMEDOUT == rc
|
|
&& (cckd->wrpending || cckd->cckdioact)
|
|
&& sysblk.shutdown
|
|
)
|
|
{
|
|
CCKD_TRACE( "closing device while wrpending=%d cckdioact=%d",
|
|
cckd->wrpending, cckd->cckdioact );
|
|
// "%1d:%04X CCKD file %s: closing device while wrpending=%d cckdioact=%d"
|
|
WRMSG( HHC00381, "W", LCSS_DEVNUM, dev->filename,
|
|
cckd->wrpending, cckd->cckdioact );
|
|
break;
|
|
}
|
|
}
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
cckd_purge_cache( dev );
|
|
cckd_purge_l2( dev );
|
|
dev->bufcur = -1;
|
|
dev->cache = -1;
|
|
if (cckd->newbuf)
|
|
cckd_free( dev, "newbuf", cckd->newbuf );
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* Remove the device from the cckd queue */
|
|
cckd_lock_devchain(1);
|
|
{
|
|
remove_dev_from_cckd_chain_locked( dev );
|
|
}
|
|
cckd_unlock_devchain();
|
|
|
|
/* harden the file */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
cckd_harden (dev);
|
|
|
|
/* close the shadow files */
|
|
for (i = 1; i <= cckd->sfn; i++)
|
|
{
|
|
cckd_close (dev, i);
|
|
cckd->open[i] = 0;
|
|
}
|
|
|
|
/* free the level 1 tables */
|
|
for (i = 0; i <= cckd->sfn; i++)
|
|
cckd->L1tab[i] = cckd_free (dev, "l1", cckd->L1tab[i]);
|
|
|
|
/* reset the device handler */
|
|
if (cckd->ckddasd)
|
|
dev->hnd = &ckd_dasd_device_hndinfo;
|
|
else
|
|
dev->hnd = &fba_dasd_device_hndinfo;
|
|
|
|
/* write some statistics */
|
|
if (!dev->batch && !cckdblk.nosfd)
|
|
cckd_sf_stats (dev);
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
/* If no more devices then perform global termination */
|
|
cckd_dasd_term_if_appropriate();
|
|
|
|
/* Destroy the cckd extension's locks and conditions */
|
|
destroy_lock( &cckd->cckdiolock );
|
|
destroy_lock( &cckd->filelock );
|
|
destroy_condition( &cckd->cckdiocond );
|
|
|
|
/* free the cckd extension itself */
|
|
dev->cckd_ext = cckd_free (dev, "ext", cckd);
|
|
|
|
if (dev->dasdsfn) free (dev->dasdsfn);
|
|
dev->dasdsfn = NULL;
|
|
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
|
|
dev->buf = NULL;
|
|
dev->bufsize = 0;
|
|
|
|
/* return to caller */
|
|
return 0;
|
|
} /* end function cckd_dasd_close_device */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed ckd start/resume channel program */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_dasd_start (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
U16 devnum = 0; /* Last active device number */
|
|
int trk = 0; /* Last active track */
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_dasd_start( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "start i/o file[%d] bufcur %d cache[%d]",
|
|
cckd->sfn, dev->bufcur, dev->cache);
|
|
|
|
/* Reset buffer offsets */
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = cckd->ckddasd ? dev->ckdtrksz : CFBA_BLKGRP_SIZE;
|
|
|
|
/* Check for merge */
|
|
obtain_lock(&cckd->cckdiolock);
|
|
if (cckd->merging)
|
|
{
|
|
CCKD_TRACE( "start i/o waiting for merge%s","");
|
|
while (cckd->merging)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
|
|
cckd->cckdwaiters--;
|
|
}
|
|
dev->bufcur = dev->cache = -1;
|
|
}
|
|
cckd->cckdioact = 1;
|
|
|
|
cache_lock(CACHE_DEVBUF);
|
|
|
|
if (dev->cache >= 0)
|
|
CCKD_CACHE_GETKEY(dev->cache, devnum, trk);
|
|
|
|
/* Check if previous active entry is still valid and not busy */
|
|
if (dev->cache >= 0 && dev->devnum == devnum && dev->bufcur == trk
|
|
&& !(cache_getflag(CACHE_DEVBUF, dev->cache) & CCKD_CACHE_IOBUSY))
|
|
{
|
|
/* Make the entry active again */
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_ACTIVE);
|
|
|
|
/* If the entry is pending write then change it to `updated' */
|
|
if (cache_getflag(CACHE_DEVBUF, dev->cache) & CCKD_CACHE_WRITE)
|
|
{
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_WRITE, CCKD_CACHE_UPDATED);
|
|
cckd->wrpending--;
|
|
if (cckd->cckdwaiters && !cckd->wrpending)
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
}
|
|
}
|
|
else
|
|
dev->bufcur = dev->cache = -1;
|
|
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
return;
|
|
|
|
} /* end function cckd_dasd_start */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed ckd end/suspend channel program */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_dasd_end (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_dasd_end( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Update length if previous image was updated */
|
|
if (dev->bufupd && dev->bufcur >= 0 && dev->cache >= 0)
|
|
{
|
|
dev->buflen = cckd_trklen (dev, dev->buf);
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
}
|
|
|
|
dev->bufupd = 0;
|
|
|
|
CCKD_TRACE( "end i/o bufcur %d cache[%d] waiters %d",
|
|
dev->bufcur, dev->cache, cckd->cckdwaiters);
|
|
|
|
obtain_lock (&cckd->cckdiolock);
|
|
|
|
cckd->cckdioact = 0;
|
|
|
|
/* Make the current entry inactive */
|
|
if (dev->cache >= 0)
|
|
{
|
|
cache_lock (CACHE_DEVBUF);
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_ACTIVE, 0);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
}
|
|
|
|
/* Cause writers to start after first update */
|
|
if (cckd->updated && (cckdblk.wrs == 0 || cckd->cckdwaiters != 0))
|
|
cckd_flush_cache (dev);
|
|
else if (cckd->cckdwaiters)
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
} /* end function cckd_dasd_end */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Open a cckd file */
|
|
/* */
|
|
/* If O_CREAT is not set and mode is non-zero then the error message */
|
|
/* will be suppressed. */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_open (DEVBLK *dev, int sfx, int flags, mode_t mode)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int err; /* 1 = issue error message */
|
|
char pathname[MAX_PATH]; /* file path in host format */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
err = !((flags & O_CREAT) == 0 && mode != 0);
|
|
|
|
if (cckd->fd[sfx] >= 0)
|
|
cckd_close (dev, sfx);
|
|
|
|
hostpath(pathname, cckd_sf_name (dev, sfx), sizeof(pathname));
|
|
cckd->fd[sfx] = HOPEN (pathname, flags, mode);
|
|
if (sfx == 0) dev->fd = cckd->fd[sfx];
|
|
|
|
if (cckd->fd[sfx] >= 0)
|
|
cckd->open[sfx] = flags & O_RDWR ? CCKD_OPEN_RW :
|
|
cckd->open[sfx] == CCKD_OPEN_RW ?
|
|
CCKD_OPEN_RD : CCKD_OPEN_RO;
|
|
else
|
|
{
|
|
if (err)
|
|
{
|
|
WRMSG (HHC00301, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
|
|
"open()", strerror(errno));
|
|
CCKD_TRACE( "file[%d] fd[%d] open %s error flags %8.8x mode %8.8x",
|
|
sfx, cckd->fd[sfx], cckd_sf_name (dev, sfx), flags, mode);
|
|
cckd_print_itrace ();
|
|
}
|
|
cckd->open[sfx] = CCKD_OPEN_NONE;
|
|
}
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] open %s, flags %8.8x mode %8.8x",
|
|
sfx, cckd->fd[sfx], cckd_sf_name (dev, sfx), flags, mode);
|
|
|
|
return cckd->fd[sfx];
|
|
|
|
} /* end function cckd_open */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Close a cckd file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_close (DEVBLK *dev, int sfx)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc = 0; /* Return code */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] close %s",
|
|
sfx, cckd->fd[sfx], cckd_sf_name(dev, sfx));
|
|
|
|
if (cckd->fd[sfx] >= 0)
|
|
rc = close (cckd->fd[sfx]);
|
|
|
|
if (rc < 0)
|
|
{
|
|
WRMSG (HHC00301, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
|
|
"close()", strerror(errno));
|
|
cckd_print_itrace ();
|
|
}
|
|
|
|
cckd->fd[sfx] = -1;
|
|
if (sfx == 0) dev->fd = -1;
|
|
|
|
return rc;
|
|
|
|
} /* end function cckd_close */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read from a cckd file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read( DEVBLK* dev, int sfx, off_t off, void* buf, unsigned int len )
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] read, off 0x%16.16"PRIx64" len %d",
|
|
sfx, cckd->fd[ sfx ], off, len );
|
|
|
|
/* Seek to specified offset */
|
|
if (lseek( cckd->fd[ sfx ], off, SEEK_SET ) < 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"lseek()", off, strerror( errno ));
|
|
cckd_print_itrace();
|
|
return -1;
|
|
}
|
|
|
|
/* Read the data */
|
|
rc = read( cckd->fd[ sfx ], buf, len );
|
|
if (rc < (int)len)
|
|
{
|
|
if (rc < 0)
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG (HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
|
|
"read()", off, strerror(errno));
|
|
else
|
|
{
|
|
char buf[128];
|
|
MSGBUF( buf, "read incomplete: read %d, expected %d", rc, len );
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"read()", off, buf);
|
|
}
|
|
cckd_print_itrace ();
|
|
return -1;
|
|
}
|
|
|
|
return rc;
|
|
|
|
} /* end function cckd_read */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write to a cckd file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_write( DEVBLK* dev, int sfx, off_t off, void* buf, unsigned int len )
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc = 0; /* Return code */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] write, off 0x%16.16"PRIx64" len %d",
|
|
sfx, cckd->fd[ sfx ], off, len );
|
|
|
|
/* Seek to specified offset */
|
|
if (lseek( cckd->fd[ sfx ], off, SEEK_SET ) < 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"lseek()", off, strerror( errno ));
|
|
cckd_print_itrace();
|
|
return -1;
|
|
}
|
|
|
|
/* Write the data */
|
|
rc = write( cckd->fd[ sfx ], buf, len );
|
|
if (rc < (int)len)
|
|
{
|
|
if (rc < 0)
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"write()", off, strerror( errno ));
|
|
else
|
|
{
|
|
char buf[128];
|
|
MSGBUF( buf, "write incomplete: write %d, expected %d", rc, len );
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"write()", off, buf );
|
|
}
|
|
cckd_print_itrace();
|
|
return -1;
|
|
}
|
|
|
|
return rc;
|
|
|
|
} /* end function cckd_write */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Truncate a cckd file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_ftruncate(DEVBLK *dev, int sfx, off_t off)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] ftruncate, off 0x%16.16"PRIx64,
|
|
sfx, cckd->fd[sfx], off);
|
|
|
|
/* Truncate the file */
|
|
if (ftruncate (cckd->fd[sfx], off) < 0)
|
|
{
|
|
WRMSG (HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
|
|
"ftruncate()", off, strerror(errno));
|
|
cckd_print_itrace ();
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_ftruncate */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* malloc */
|
|
/*-------------------------------------------------------------------*/
|
|
void* cckd_malloc( DEVBLK* dev, char* id, size_t size )
|
|
{
|
|
void* p = NULL;
|
|
|
|
if (size)
|
|
p = malloc( size );
|
|
CCKD_TRACE( "%s malloc %p len %ld", id, p, (long) size );
|
|
|
|
if (!p)
|
|
{
|
|
char buf[64];
|
|
MSGBUF( buf, "malloc( %d )", (int) size );
|
|
// "%1d:%04X CCKD file: error in function %s: %s"
|
|
WRMSG( HHC00303, "E", LCSS_DEVNUM, buf, strerror( errno ));
|
|
cckd_print_itrace();
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* calloc */
|
|
/*-------------------------------------------------------------------*/
|
|
void* cckd_calloc( DEVBLK* dev, char* id, size_t n, size_t size )
|
|
{
|
|
void* p = NULL;
|
|
|
|
if (n && size)
|
|
p = calloc( n, size );
|
|
CCKD_TRACE( "%s calloc %p len %ld", id, p, n * (long) size );
|
|
|
|
if (!p)
|
|
{
|
|
char buf[64];
|
|
MSGBUF( buf, "calloc( %d, %d )", (int) n, (int) size );
|
|
// "%1d:%04X CCKD file: error in function %s: %s"
|
|
WRMSG( HHC00303, "E", LCSS_DEVNUM, buf, strerror( errno ));
|
|
cckd_print_itrace();
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* realloc */
|
|
/*-------------------------------------------------------------------*/
|
|
void* cckd_realloc( DEVBLK *dev, char *id, void* p, size_t size )
|
|
{
|
|
void* p2 = NULL;
|
|
|
|
// shut up GCC 12 warning about using a pointer after realloc()
|
|
char p_string[33];
|
|
MSGBUF( p_string, "%p", p );
|
|
|
|
if (size)
|
|
p2 = realloc( p, size );
|
|
CCKD_TRACE( "%s realloc %s len %ld", id, p_string, (long) size );
|
|
|
|
if (!p2)
|
|
{
|
|
char buf[64];
|
|
MSGBUF( buf, "realloc( %s, %d )", p_string, (int) size );
|
|
// "%1d:%04X CCKD file: error in function %s: %s"
|
|
WRMSG( HHC00303, "E", LCSS_DEVNUM, buf, strerror( errno ));
|
|
cckd_print_itrace();
|
|
}
|
|
|
|
return p2;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* free */
|
|
/*-------------------------------------------------------------------*/
|
|
void* cckd_free( DEVBLK* dev, char* id, void* p )
|
|
{
|
|
CCKD_TRACE( "%s free %p", id, p );
|
|
if (p) free( p );
|
|
return NULL;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed ckd read track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read_track (DEVBLK *dev, int trk, BYTE *unitstat)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int len; /* Compressed length */
|
|
BYTE *newbuf; /* Uncompressed buffer */
|
|
int cache; /* New active cache entry */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Update length if previous image was updated */
|
|
if (dev->bufupd && dev->bufcur >= 0 && dev->cache >= 0)
|
|
{
|
|
dev->buflen = cckd_trklen (dev, dev->buf);
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
}
|
|
|
|
/* Reset buffer offsets */
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
|
|
/* Check if reading the same track image */
|
|
if (trk == dev->bufcur && dev->cache >= 0)
|
|
{
|
|
/* Track image may be compressed */
|
|
if ((dev->buf[0] & CCKD_COMPRESS_MASK) != 0
|
|
&& (dev->buf[0] & dev->comps) == 0)
|
|
{
|
|
len = cache_getval(CACHE_DEVBUF, dev->cache);
|
|
newbuf = cckd_uncompress (dev, dev->buf, len, dev->ckdtrksz, trk);
|
|
if (newbuf == NULL)
|
|
{
|
|
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
cache_setbuf (CACHE_DEVBUF, dev->cache, newbuf, dev->ckdtrksz);
|
|
dev->buf = newbuf;
|
|
dev->buflen = cckd_trklen (dev, newbuf);
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
|
|
dev->bufupd = 0;
|
|
CCKD_TRACE( "read trk %d uncompressed len %d",
|
|
trk, dev->buflen);
|
|
}
|
|
|
|
dev->comp = dev->buf[0] & CCKD_COMPRESS_MASK;
|
|
if (dev->comp != 0) dev->compoff = CKD_TRKHDR_SIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
CCKD_TRACE( "read trk %d (%s)", trk, "asynchronous");
|
|
|
|
/* read the new track */
|
|
dev->bufupd = 0;
|
|
cache = cckd_read_trk (dev, trk, 0, unitstat);
|
|
if (cache < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
|
|
dev->cache = cache;
|
|
dev->buf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
|
|
dev->bufcur = trk;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
dev->buflen = cache_getval (CACHE_DEVBUF, dev->cache);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
|
|
|
|
dev->comp = dev->buf[0] & CCKD_COMPRESS_MASK;
|
|
if (dev->comp != 0) dev->compoff = CKD_TRKHDR_SIZE;
|
|
|
|
/* If the image is compressed then call ourself recursively
|
|
to cause the image to get uncompressed */
|
|
if (dev->comp != 0 && (dev->comp & dev->comps) == 0)
|
|
rc = cckd_read_track (dev, trk, unitstat);
|
|
else
|
|
rc = 0;
|
|
|
|
return rc;
|
|
} /* end function cckd_read_track */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed ckd update track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_update_track (DEVBLK *dev, int trk, int off,
|
|
BYTE *buf, int len, BYTE *unitstat)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_update_track( dev, trk, off, buf, len, unitstat );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Error if opened read-only */
|
|
if (dev->ckdrdonly && cckd->sfn == 0)
|
|
{
|
|
ckd_build_sense (dev, SENSE_EC, SENSE1_WRI, 0,FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
|
|
/* If the track is not current or compressed then read it.
|
|
`dev->comps' is set to zero forcing the read routine to
|
|
uncompress the image. */
|
|
if (trk != dev->bufcur || (dev->buf[0] & CCKD_COMPRESS_MASK) != 0)
|
|
{
|
|
dev->comps = 0;
|
|
rc = (dev->hnd->read) (dev, trk, unitstat);
|
|
if (rc < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Invalid track format if going past buffer end */
|
|
if (off + len > dev->ckdtrksz)
|
|
{
|
|
ckd_build_sense (dev, 0, SENSE1_ITF, 0, 0, 0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
|
|
/* Copy the data into the buffer */
|
|
if (buf && len > 0) memcpy (dev->buf + off, buf, len);
|
|
|
|
CCKD_TRACE( "updt trk %d offset %d length %d",
|
|
trk, off, len);
|
|
|
|
/* Update the cache entry */
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_UPDATED | CCKD_CACHE_USED);
|
|
cckd->updated = 1;
|
|
|
|
/* Notify the shared server of the update */
|
|
if (!dev->bufupd)
|
|
{
|
|
dev->bufupd = 1;
|
|
shared_update_notify (dev, trk);
|
|
}
|
|
|
|
/* Ensure the Dasd Hardener is running, if it's supposed to be. */
|
|
if (cckdblk.dhint > 0)
|
|
cckd_dhstart( false );
|
|
|
|
return len;
|
|
|
|
} /* end function cckd_update_track */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return used cylinders */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_used (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int L1idx, l2x; /* Lookup table indexes */
|
|
int sfx; /* Shadow file suffix */
|
|
CCKD_L2ENT l2; /* Copied level 2 entry */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_used( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* Find the last used level 1 table entry */
|
|
for (L1idx = cckd->cdevhdr[0].num_L1tab - 1; L1idx > 0; L1idx--)
|
|
{
|
|
sfx = cckd->sfn;
|
|
while (cckd->L1tab[sfx][L1idx] == CCKD_MAXSIZE && sfx > 0) sfx--;
|
|
if (cckd->L1tab[sfx][L1idx]) break;
|
|
}
|
|
|
|
/* Find the last used level 2 table entry */
|
|
for (l2x = 255; l2x >= 0; l2x--)
|
|
{
|
|
rc = cckd_read_l2ent (dev, &l2, L1idx * 256 + l2x);
|
|
if (rc < 0 || l2.L2_trkoff != 0) break;
|
|
}
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
return (L1idx * 256 + l2x + dev->ckdheads) / dev->ckdheads;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed fba read block(s) */
|
|
/*-------------------------------------------------------------------*/
|
|
int cfba_read_block (DEVBLK *dev, int blkgrp, BYTE *unitstat)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int cache; /* New active cache entry */
|
|
BYTE *cbuf; /* -> cache buffer */
|
|
BYTE *newbuf; /* Uncompressed buffer */
|
|
int len; /* Compressed length */
|
|
int maxlen; /* Size for cache entry */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (dev->cache >= 0)
|
|
cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
|
|
else
|
|
cbuf = NULL;
|
|
maxlen = CFBA_BLKGRP_SIZE + CKD_TRKHDR_SIZE;
|
|
|
|
/* Return if reading the same track image */
|
|
if (blkgrp == dev->bufcur && dev->cache >= 0)
|
|
{
|
|
/* Block group image may be compressed */
|
|
if ((cbuf[0] & CCKD_COMPRESS_MASK) != 0
|
|
&& (cbuf[0] & dev->comps) == 0)
|
|
{
|
|
len = cache_getval(CACHE_DEVBUF, dev->cache) + CKD_TRKHDR_SIZE;
|
|
newbuf = cckd_uncompress (dev, cbuf, len, maxlen, blkgrp);
|
|
if (newbuf == NULL)
|
|
{
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
cache_setbuf (CACHE_DEVBUF, dev->cache, newbuf, maxlen);
|
|
cbuf = newbuf;
|
|
dev->buf = newbuf + CKD_TRKHDR_SIZE;
|
|
dev->buflen = CFBA_BLKGRP_SIZE;
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
|
|
dev->bufupd = 0;
|
|
CCKD_TRACE( "read bkgrp %d uncompressed len %d",
|
|
blkgrp, dev->buflen);
|
|
}
|
|
|
|
dev->comp = cbuf[0] & CCKD_COMPRESS_MASK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
CCKD_TRACE( "read blkgrp %d (%s)", blkgrp, "asynchronous");
|
|
|
|
/* Read the new blkgrp */
|
|
dev->bufupd = 0;
|
|
cache = cckd_read_trk (dev, blkgrp, 0, unitstat);
|
|
if (cache < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
dev->cache = cache;
|
|
cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
|
|
dev->buf = cbuf + CKD_TRKHDR_SIZE;
|
|
dev->bufcur = blkgrp;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = CFBA_BLKGRP_SIZE;
|
|
dev->buflen = CFBA_BLKGRP_SIZE;
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
|
|
dev->comp = cbuf[0] & CCKD_COMPRESS_MASK;
|
|
|
|
/* If the image is compressed then call ourself recursively
|
|
to cause the image to get uncompressed. This is because
|
|
`bufcur' will match blkgrp and `comps' won't match `comp' */
|
|
if (dev->comp != 0 && (dev->comp & dev->comps) == 0)
|
|
rc = cfba_read_block (dev, blkgrp, unitstat);
|
|
else
|
|
rc = 0;
|
|
|
|
return rc;
|
|
} /* end function cfba_read_block */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed fba write block(s) */
|
|
/*-------------------------------------------------------------------*/
|
|
int cfba_write_block (DEVBLK *dev, int blkgrp, int off,
|
|
BYTE *buf, int len, BYTE *unitstat)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
BYTE *cbuf; /* -> cache buffer */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (dev->cache >= 0)
|
|
cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
|
|
else
|
|
cbuf = NULL;
|
|
|
|
/* Read the block group if it's not current or compressed.
|
|
`dev->comps' is set to zero forcing the read routine to
|
|
uncompress the image. */
|
|
if (blkgrp != dev->bufcur || (cbuf[0] & CCKD_COMPRESS_MASK) != 0)
|
|
{
|
|
dev->comps = 0;
|
|
rc = (dev->hnd->read) (dev, blkgrp, unitstat);
|
|
if (rc < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Copy the data into the buffer */
|
|
if (buf) memcpy (dev->buf + off, buf, len);
|
|
|
|
/* Update the cache entry */
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_UPDATED|CCKD_CACHE_USED);
|
|
cckd->updated = 1;
|
|
|
|
/* Notify the shared server of the update */
|
|
if (!dev->bufupd)
|
|
{
|
|
dev->bufupd = 1;
|
|
shared_update_notify (dev, blkgrp);
|
|
}
|
|
|
|
return len;
|
|
|
|
} /* end function cfba_write_block */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return used blocks */
|
|
/*-------------------------------------------------------------------*/
|
|
int cfba_used (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int L1idx, l2x; /* Lookup table indexes */
|
|
int sfx; /* Shadow file suffix */
|
|
CCKD_L2ENT l2; /* Copied level 2 entry */
|
|
|
|
if (dev->cckd64)
|
|
return cfba64_used( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* Find the last used level 1 table entry */
|
|
for (L1idx = cckd->cdevhdr[0].num_L1tab - 1; L1idx > 0; L1idx--)
|
|
{
|
|
sfx = cckd->sfn;
|
|
while (cckd->L1tab[sfx][L1idx] == CCKD_MAXSIZE && sfx > 0) sfx--;
|
|
if (cckd->L1tab[sfx][L1idx]) break;
|
|
}
|
|
|
|
/* Find the last used level 2 table entry */
|
|
for (l2x = 255; l2x >= 0; l2x--)
|
|
{
|
|
rc = cckd_read_l2ent (dev, &l2, L1idx * 256 + l2x);
|
|
if (rc < 0 || l2.L2_trkoff != 0) break;
|
|
}
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
return (L1idx * 256 + l2x + CFBA_BLKS_PER_GRP) / CFBA_BLKS_PER_GRP;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read a track image */
|
|
/* */
|
|
/* This routine can be called by the i/o thread (`ra' == 0) or */
|
|
/* by readahead threads (0 < `ra' <= cckdblk.ramax). */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read_trk(DEVBLK *dev, int trk, int ra, BYTE *unitstat)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int fnd; /* Cache index for hit */
|
|
int lru; /* Oldest unused cache index */
|
|
int len; /* Length of track image */
|
|
int maxlen; /* Length for buffer */
|
|
int curtrk = -1; /* Current track (at entry) */
|
|
U16 devnum; /* Device number */
|
|
U32 oldtrk; /* Stolen track number */
|
|
U32 flag; /* Cache flag */
|
|
BYTE *buf; /* Read buffer */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_read_trk( dev, trk, ra, unitstat );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "%d rdtrk %d", ra, trk);
|
|
|
|
maxlen = cckd->ckddasd ? dev->ckdtrksz
|
|
: CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE;
|
|
|
|
if (!ra) obtain_lock (&cckd->cckdiolock);
|
|
|
|
cache_lock (CACHE_DEVBUF);
|
|
|
|
/* Inactivate the old entry */
|
|
if (!ra)
|
|
{
|
|
curtrk = dev->bufcur;
|
|
if (dev->cache >= 0)
|
|
cache_setflag(CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_ACTIVE, 0);
|
|
dev->bufcur = dev->cache = -1;
|
|
}
|
|
|
|
cckd_read_trk_retry:
|
|
|
|
/* scan the cache array for the track */
|
|
fnd = cache_lookup (CACHE_DEVBUF, CCKD_CACHE_SETKEY(dev->devnum, trk), &lru);
|
|
|
|
/* check for cache hit */
|
|
if (fnd >= 0)
|
|
{
|
|
if (ra) /* readahead doesn't care about a cache hit */
|
|
{ cache_unlock (CACHE_DEVBUF);
|
|
return fnd;
|
|
}
|
|
|
|
/* Mark the new entry active */
|
|
cache_setflag(CACHE_DEVBUF, fnd, ~0, CCKD_CACHE_ACTIVE | CCKD_CACHE_USED);
|
|
cache_setage(CACHE_DEVBUF, fnd);
|
|
|
|
/* If the entry is pending write then change it to `updated' */
|
|
if (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_WRITE)
|
|
{
|
|
cache_setflag(CACHE_DEVBUF, fnd, ~CCKD_CACHE_WRITE, CCKD_CACHE_UPDATED);
|
|
cckd->wrpending--;
|
|
if (cckd->cckdwaiters && !cckd->wrpending)
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
}
|
|
buf = cache_getbuf(CACHE_DEVBUF, fnd, 0);
|
|
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
CCKD_TRACE( "%d rdtrk[%d] %d cache hit buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
ra, fnd, trk, buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
|
|
|
|
cckdblk.stats_switches++; cckd->switches++;
|
|
cckdblk.stats_cachehits++; cckd->cachehits++;
|
|
|
|
/* if read/write is in progress then wait for it to finish */
|
|
while (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_IOBUSY)
|
|
{
|
|
cckdblk.stats_iowaits++;
|
|
CCKD_TRACE( "%d rdtrk[%d] %d waiting for %s", ra, fnd, trk,
|
|
cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_READING ?
|
|
"read" : "write");
|
|
cache_setflag (CACHE_DEVBUF, fnd, ~0, CCKD_CACHE_IOWAIT);
|
|
cckd->cckdwaiters++;
|
|
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
|
|
cckd->cckdwaiters--;
|
|
cache_setflag (CACHE_DEVBUF, fnd, ~CCKD_CACHE_IOWAIT, 0);
|
|
CCKD_TRACE( "%d rdtrk[%d] %d io wait complete",
|
|
ra, fnd, trk);
|
|
}
|
|
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
/* Asynchronously schedule readaheads */
|
|
if (curtrk > 0 && trk > curtrk && trk <= curtrk + 2)
|
|
cckd_readahead (dev, trk);
|
|
|
|
return fnd;
|
|
|
|
} /* cache hit */
|
|
|
|
CCKD_TRACE( "%d rdtrk[%d] %d cache miss", ra, lru, trk);
|
|
|
|
/* If no cache entry was stolen, then flush all outstanding writes.
|
|
This requires us to release our locks. cache_wait should be
|
|
called with only the cache_lock held. Fortunately, cache waits
|
|
occur very rarely. */
|
|
if (lru < 0) /* No available entry to be stolen */
|
|
{
|
|
CCKD_TRACE( "%d rdtrk[%d] %d no available cache entry",
|
|
ra, lru, trk);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
if (!ra) release_lock (&cckd->cckdiolock);
|
|
cckd_flush_cache_all();
|
|
cache_lock (CACHE_DEVBUF);
|
|
cckdblk.stats_cachewaits++;
|
|
cache_wait (CACHE_DEVBUF);
|
|
if (!ra)
|
|
{
|
|
cache_unlock (CACHE_DEVBUF);
|
|
obtain_lock (&cckd->cckdiolock);
|
|
cache_lock (CACHE_DEVBUF);
|
|
}
|
|
goto cckd_read_trk_retry;
|
|
}
|
|
|
|
CCKD_CACHE_GETKEY(lru, devnum, oldtrk);
|
|
if (devnum != 0)
|
|
{
|
|
CCKD_TRACE( "%d rdtrk[%d] %d dropping %4.4X:%d from cache",
|
|
ra, lru, trk, devnum, oldtrk);
|
|
if (!(cache_getflag(CACHE_DEVBUF, lru) & CCKD_CACHE_USED))
|
|
{
|
|
cckdblk.stats_readaheadmisses++; cckd->misses++;
|
|
}
|
|
}
|
|
|
|
/* Initialize the entry */
|
|
cache_setkey(CACHE_DEVBUF, lru, CCKD_CACHE_SETKEY(dev->devnum, trk));
|
|
cache_setflag(CACHE_DEVBUF, lru, 0, CCKD_CACHE_READING);
|
|
cache_setage(CACHE_DEVBUF, lru);
|
|
cache_setval(CACHE_DEVBUF, lru, 0);
|
|
if (!ra)
|
|
{
|
|
cckdblk.stats_switches++; cckd->switches++;
|
|
cckdblk.stats_cachemisses++;
|
|
cache_setflag(CACHE_DEVBUF, lru, ~0, CCKD_CACHE_ACTIVE|CCKD_CACHE_USED);
|
|
}
|
|
cache_setflag(CACHE_DEVBUF, lru, ~CACHE_TYPE,
|
|
cckd->ckddasd ? DEVBUF_TYPE_CCKD : DEVBUF_TYPE_CFBA);
|
|
buf = cache_getbuf(CACHE_DEVBUF, lru, maxlen);
|
|
|
|
CCKD_TRACE( "%d rdtrk[%d] %d buf %p len %d",
|
|
ra, lru, trk, buf, cache_getlen(CACHE_DEVBUF, lru));
|
|
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
if (!ra) release_lock (&cckd->cckdiolock);
|
|
|
|
/* Asynchronously schedule readaheads */
|
|
if (!ra && curtrk > 0 && trk > curtrk && trk <= curtrk + 2)
|
|
cckd_readahead (dev, trk);
|
|
|
|
/* Clear the buffer if batch mode */
|
|
if (dev->batch) memset(buf, 0, maxlen);
|
|
|
|
/* Read the track image */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
len = cckd_read_trkimg (dev, buf, trk, unitstat);
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
cache_setval (CACHE_DEVBUF, lru, len);
|
|
|
|
obtain_lock (&cckd->cckdiolock);
|
|
|
|
/* Turn off the READING bit */
|
|
cache_lock (CACHE_DEVBUF);
|
|
flag = cache_setflag(CACHE_DEVBUF, lru, ~CCKD_CACHE_READING, 0);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
/* Wakeup other thread waiting for this read */
|
|
if (cckd->cckdwaiters && (flag & CCKD_CACHE_IOWAIT))
|
|
{ CCKD_TRACE( "%d rdtrk[%d] %d signalling read complete",
|
|
ra, lru, trk);
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
}
|
|
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
if (ra)
|
|
{
|
|
cckdblk.stats_readaheads++; cckd->readaheads++;
|
|
}
|
|
|
|
CCKD_TRACE( "%d rdtrk[%d] %d complete buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
ra, lru, trk, buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
|
|
|
|
if (cache_busy_percent(CACHE_DEVBUF) > 80) cckd_flush_cache_all();
|
|
|
|
return lru;
|
|
|
|
} /* end function cckd_read_trk */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Schedule asynchronous readaheads */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_readahead (DEVBLK *dev, int trk)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int i, r; /* Indexes */
|
|
TID tid; /* Readahead thread id */
|
|
int rc;
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (cckdblk.ramax < 1 || cckdblk.readaheads < 1)
|
|
return;
|
|
|
|
obtain_lock (&cckdblk.ralock);
|
|
|
|
/* Scan the cache to see if the tracks are already there */
|
|
memset( cckd->ralkup, 0, sizeof(cckd->ralkup) );
|
|
cckd->ratrk = trk;
|
|
cache_lock(CACHE_DEVBUF);
|
|
cache_scan(CACHE_DEVBUF, cckd_readahead_scan, dev);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
|
|
/* Scan the queue to see if the tracks are already there */
|
|
for (r = cckdblk.ra1st; r >= 0; r = cckdblk.ra[r].ra_idxnxt)
|
|
if (cckdblk.ra[r].ra_dev == dev)
|
|
{
|
|
i = cckdblk.ra[r].ra_trk - trk;
|
|
if (i > 0 && i <= cckdblk.readaheads)
|
|
cckd->ralkup[i-1] = 1;
|
|
}
|
|
|
|
/* Queue the tracks to the readahead queue */
|
|
for (i = 1; i <= cckdblk.readaheads && cckdblk.rafree >= 0; i++)
|
|
{
|
|
if (cckd->ralkup[i-1]) continue;
|
|
if (trk + i >= dev->ckdtrks) break;
|
|
r = cckdblk.rafree;
|
|
cckdblk.rafree = cckdblk.ra[r].ra_idxnxt;
|
|
if (cckdblk.ralast < 0)
|
|
{
|
|
cckdblk.ra1st = cckdblk.ralast = r;
|
|
cckdblk.ra[r].ra_idxprv = cckdblk.ra[r].ra_idxnxt = -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.ra[cckdblk.ralast].ra_idxnxt = r;
|
|
cckdblk.ra[r].ra_idxprv = cckdblk.ralast;
|
|
cckdblk.ra[r].ra_idxnxt = -1;
|
|
cckdblk.ralast = r;
|
|
}
|
|
cckdblk.ra[r].ra_trk = trk + i;
|
|
cckdblk.ra[r].ra_dev = dev;
|
|
}
|
|
|
|
/* Schedule the readahead if any are pending */
|
|
if (cckdblk.ra1st >= 0)
|
|
{
|
|
if (cckdblk.rawaiting)
|
|
signal_condition(&cckdblk.racond);
|
|
else if (cckdblk.ras < cckdblk.ramax)
|
|
{
|
|
/* Schedule a new read-ahead thread */
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Starting thread %s, active=%d, started=%d, max=%d"
|
|
WRMSG(HHC00107, "I", CCKD_RA_THREAD_NAME "()",
|
|
cckdblk.raa, cckdblk.ras, cckdblk.ramax);
|
|
|
|
++cckdblk.ras;
|
|
|
|
/* Release lock across thread create to prevent interlock */
|
|
release_lock(&cckdblk.ralock);
|
|
{
|
|
rc = create_thread( &tid, JOINABLE, cckd_ra, NULL, CCKD_RA_THREAD_NAME );
|
|
}
|
|
obtain_lock(&cckdblk.ralock);
|
|
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread() for %s %d of %d: %s"
|
|
WRMSG(HHC00106, "E", CCKD_RA_THREAD_NAME "()",
|
|
cckdblk.ras-1, cckdblk.ramax, strerror(rc));
|
|
|
|
--cckdblk.ras;
|
|
}
|
|
}
|
|
}
|
|
|
|
release_lock (&cckdblk.ralock);
|
|
|
|
} /* end function cckd_readahead */
|
|
|
|
int cckd_readahead_scan (int *answer, int ix, int i, void *data)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
U16 devnum; /* Cached device number */
|
|
U32 trk; /* Cached track */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
int k; /* Index */
|
|
|
|
UNREFERENCED(answer);
|
|
UNREFERENCED(ix);
|
|
cckd = dev->cckd_ext;
|
|
CCKD_CACHE_GETKEY(i, devnum, trk);
|
|
if (devnum == dev->devnum)
|
|
{
|
|
k = (int)trk - cckd->ratrk;
|
|
if (k > 0 && k <= cckdblk.readaheads)
|
|
cckd->ralkup[k-1] = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Asynchronous readahead thread */
|
|
/*-------------------------------------------------------------------*/
|
|
void* cckd_ra (void* arg)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
DEVBLK *dev; /* Readahead devblk */
|
|
int trk; /* Readahead track */
|
|
int ra; /* Readahead index */
|
|
int r; /* Readahead queue index */
|
|
TID tid; /* Readahead thread id */
|
|
char threadname[40];
|
|
int rc;
|
|
int ras;
|
|
|
|
UNREFERENCED(arg);
|
|
|
|
obtain_lock(&cckdblk.ralock);
|
|
|
|
ra = ++cckdblk.raa; /* increment nr ra threads dispatched */
|
|
MSGBUF( threadname, CCKD_RA_THREAD_NAME " thread %d", ra );
|
|
|
|
/* Return if too many already started */
|
|
if (ra > cckdblk.ramax)
|
|
{
|
|
--cckdblk.ras; /* decrement count started */
|
|
--cckdblk.raa; /* decrement count running */
|
|
|
|
if (!cckdblk.ramax)
|
|
{
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' ended"
|
|
LOG_THREAD_END( threadname );
|
|
}
|
|
else
|
|
if (!cckdblk.batch || cckdblk.batchml > 0)
|
|
// "Ending thread "TIDPAT" %s, pri=%d, started=%d, max=%d exceeded"
|
|
WRMSG( HHC00108, "W", TID_CAST( thread_id()), threadname,
|
|
get_thread_priority(), ra, cckdblk.ramax );
|
|
|
|
release_lock( &cckdblk.ralock );
|
|
signal_condition( &cckdblk.termcond );
|
|
return NULL;
|
|
}
|
|
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' started"
|
|
LOG_THREAD_BEGIN( threadname );
|
|
|
|
while (ra <= cckdblk.ramax) /* continue until ramax=0 (shutdown) or max reduced by command line */
|
|
{
|
|
if (cckdblk.ra1st < 0)
|
|
{
|
|
cckdblk.rawaiting++;
|
|
wait_condition (&cckdblk.racond, &cckdblk.ralock);
|
|
cckdblk.rawaiting--;
|
|
}
|
|
|
|
/* Possibly shutting down if no writes pending */
|
|
if (cckdblk.ra1st < 0)
|
|
continue;
|
|
|
|
r = cckdblk.ra1st;
|
|
trk = cckdblk.ra[r].ra_trk;
|
|
dev = cckdblk.ra[r].ra_dev;
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Requeue the 1st entry to the readahead free queue */
|
|
cckdblk.ra1st = cckdblk.ra[r].ra_idxnxt;
|
|
if (cckdblk.ra[r].ra_idxnxt > -1)
|
|
cckdblk.ra[cckdblk.ra[r].ra_idxnxt].ra_idxprv = -1;
|
|
else cckdblk.ralast = -1;
|
|
cckdblk.ra[r].ra_idxnxt = cckdblk.rafree;
|
|
cckdblk.rafree = r;
|
|
|
|
/* Schedule the other readaheads if any are still pending */
|
|
if (cckdblk.ra1st)
|
|
{
|
|
if (cckdblk.rawaiting)
|
|
signal_condition (&cckdblk.racond);
|
|
else if (cckdblk.ras < cckdblk.ramax)
|
|
{
|
|
/* Schedule a new read-ahead thread */
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Starting thread %s, active=%d, started=%d, max=%d"
|
|
WRMSG(HHC00107, "I", CCKD_RA_THREAD_NAME "() from " CCKD_RA_THREAD_NAME "()",
|
|
cckdblk.raa, cckdblk.ras, cckdblk.ramax);
|
|
|
|
++cckdblk.ras;
|
|
|
|
/* Release lock across thread create to prevent interlock */
|
|
release_lock(&cckdblk.ralock);
|
|
{
|
|
rc = create_thread( &tid, JOINABLE, cckd_ra, NULL, CCKD_RA_THREAD_NAME );
|
|
}
|
|
obtain_lock(&cckdblk.ralock);
|
|
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread() for %s %d of %d: %s"
|
|
WRMSG(HHC00106, "E", CCKD_RA_THREAD_NAME "() from " CCKD_RA_THREAD_NAME "()",
|
|
cckdblk.ras-1, cckdblk.ramax, strerror(rc));
|
|
--cckdblk.ras;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!cckd || cckd->stopping || cckd->merging) continue;
|
|
|
|
cckd->ras++;
|
|
|
|
release_lock (&cckdblk.ralock);
|
|
{
|
|
/* Read the readahead track */
|
|
cckd_read_trk (dev, trk, ra, NULL);
|
|
}
|
|
obtain_lock (&cckdblk.ralock);
|
|
|
|
cckd->ras--;
|
|
}
|
|
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' ended"
|
|
LOG_THREAD_END( threadname );
|
|
|
|
--cckdblk.ras;
|
|
--cckdblk.raa;
|
|
|
|
ras = cckdblk.ras;
|
|
|
|
release_lock( &cckdblk.ralock );
|
|
|
|
/* Last thread to terminate? */
|
|
if (ras <= 0)
|
|
signal_condition( &cckdblk.termcond );
|
|
|
|
return NULL;
|
|
} /* end thread cckd_ra_thread */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Start the Dasd Hardener, if it isn't already running. */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_dhstart( bool by_cmdline )
|
|
{
|
|
TID tid;
|
|
int rc;
|
|
const char* threadname = by_cmdline ? CCKD_DH_THREAD_NAME " (by command line)"
|
|
: CCKD_DH_THREAD_NAME;
|
|
|
|
/* Don't bother if shutting down */
|
|
if (sysblk.shutdown)
|
|
return;
|
|
|
|
obtain_lock( &cckdblk.dhlock ); // Lock the counters.
|
|
{
|
|
if (cckdblk.dhmax > 0 && cckdblk.dhs >= 0 && cckdblk.dhs < cckdblk.dhmax)
|
|
{
|
|
/* Schedule the Dasd Hardener thread */
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
|
|
// "Starting thread %s, active=%d, started=%d, max=%d"
|
|
WRMSG( HHC00107, "I", threadname,
|
|
cckdblk.dha, cckdblk.dhs, cckdblk.dhmax );
|
|
|
|
++cckdblk.dhs;
|
|
|
|
rc = create_thread( &tid, JOINABLE, cckd_dh, NULL, CCKD_DH_THREAD_NAME );
|
|
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread() for %s %d of %d: %s"
|
|
WRMSG( HHC00106, "E", threadname,
|
|
cckdblk.dhs-1, cckdblk.dhmax, strerror( rc ));
|
|
|
|
--cckdblk.dhs;
|
|
}
|
|
}
|
|
}
|
|
release_lock( &cckdblk.dhlock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Dasd Hardener thread (handles both CCKD and CCKD64) */
|
|
/*-------------------------------------------------------------------*/
|
|
void* cckd_dh( void* arg )
|
|
{
|
|
int dhid; /* Identifier */
|
|
struct timeval tv_now; /* Time-of-day (as timeval) */
|
|
time_t tt_now; /* Time-of-day (as time_t) */
|
|
struct timespec tm; /* Time-of-day to wait */
|
|
DEVBLK *dev; /* Device Block pointer */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* generic return code */
|
|
bool cont = true; /* Continue running boolean */
|
|
|
|
UNREFERENCED( arg );
|
|
|
|
/* Don't bother if shutting down */
|
|
if (sysblk.shutdown)
|
|
return NULL;
|
|
|
|
LOCK_DH_GC(); // (prevent Garbage Collector from running)
|
|
|
|
obtain_lock( &cckdblk.dhlock );
|
|
{
|
|
dhid = ++cckdblk.dha;
|
|
|
|
/* Return without messages if too many already started */
|
|
if (cckdblk.dhmax <= 0 || dhid > cckdblk.dhmax || cckdblk.dhint == 0)
|
|
{
|
|
int prev_dhs = --cckdblk.dhs;
|
|
|
|
--cckdblk.dha;
|
|
|
|
if (!prev_dhs)
|
|
{
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
{
|
|
// "Thread id "TIDPAT", prio %d, name '%s' ended"
|
|
LOG_THREAD_END( CCKD_DH_THREAD_NAME );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!cckdblk.batch || cckdblk.batchml > 0)
|
|
{
|
|
// "Ending thread "TIDPAT" %s, pri=%d, started=%d, max=%d exceeded"
|
|
WRMSG( HHC00108, "W", TID_CAST( thread_id()), CCKD_DH_THREAD_NAME,
|
|
get_thread_priority(), prev_dhs, cckdblk.dhmax );
|
|
}
|
|
}
|
|
|
|
signal_condition( &cckdblk.termcond ); /* signal if last thread ending before init. */
|
|
release_lock( &cckdblk.dhlock );
|
|
UNLOCK_DH_GC(); // (allow Garbage Collector to run)
|
|
return NULL; /* too many already started, return */
|
|
}
|
|
}
|
|
release_lock( &cckdblk.dhlock );
|
|
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' started"
|
|
LOG_THREAD_BEGIN( CCKD_DH_THREAD_NAME );
|
|
|
|
while (1)
|
|
{
|
|
/* Exit when asked to do so (dhmax=0) */
|
|
obtain_lock( &cckdblk.dhlock );
|
|
{
|
|
cont =
|
|
(1
|
|
&& cckdblk.dhmax > 0
|
|
&& cckdblk.dhint > 0
|
|
&& dhid <= cckdblk.dhmax
|
|
&& !sysblk.shutdown
|
|
);
|
|
}
|
|
release_lock( &cckdblk.dhlock );
|
|
|
|
if (!cont) // (continue running?)
|
|
break; // (no, time to exit!)
|
|
|
|
// "Starting CCKD Dasd Hardener pass..."
|
|
if (cckdblk.debug)
|
|
WRMSG( HHC00391, "D" );
|
|
|
|
cckdblk.stats_dhpasses++;
|
|
|
|
/* Harden any DASD that have been updated since the last time. */
|
|
cckd_lock_devchain(0);
|
|
{
|
|
for (dev = cckdblk.dev1st; dev; dev = cckd->devnext)
|
|
{
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Flush the cache and wait for the writes to complete. */
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cckd_flush_cache( dev );
|
|
if (cckd->needsdh && !cckd->stopping && !sysblk.shutdown)
|
|
{
|
|
CCKD_TRACE("needs hardening");
|
|
while (!sysblk.shutdown && (cckd->wrpending || cckd->cckdioact))
|
|
{
|
|
cckd->cckdwaiters++;
|
|
CCKD_TRACE("%s waiting 1 second to complete %d pending writes",
|
|
CCKD_DH_THREAD_NAME, cckd->wrpending);
|
|
rc = timed_wait_condition_relative_usecs(
|
|
&cckd->cckdiocond, &cckd->cckdiolock, 1000000, NULL );
|
|
cckd->cckdwaiters--;
|
|
if (EINTR == rc)
|
|
continue;
|
|
}
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
}
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* Harden the disk file. */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
if (cckd->needsdh && !cckd->stopping && !sysblk.shutdown)
|
|
{
|
|
// "Hardening CCKD%s dasd %1d:%04X"
|
|
if (cckdblk.debug)
|
|
WRMSG( HHC00395, "D", dev->cckd64 ? "64" : "", LCSS_DEVNUM );
|
|
|
|
cckd_harden (dev);
|
|
cckd->needsdh = 0;
|
|
CCKD_TRACE("hardened");
|
|
}
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
}
|
|
}
|
|
cckd_unlock_devchain();
|
|
|
|
// "CCKD Dasd Hardener pass complete."
|
|
if (cckdblk.debug)
|
|
WRMSG( HHC00392, "D" );
|
|
|
|
/* Don't bother continuing if shutting down */
|
|
if (sysblk.shutdown)
|
|
break;
|
|
|
|
/* Wait a bit before starting the next cycle */
|
|
gettimeofday (&tv_now, NULL);
|
|
tt_now = tv_now.tv_sec + ((tv_now.tv_usec + 500000)/1000000);
|
|
|
|
if (cckdblk.debug)
|
|
{
|
|
char curtime[128];
|
|
strlcpy( curtime, ctime( &tt_now ), sizeof( curtime ));
|
|
RTRIM( curtime );
|
|
|
|
// "Thread '%s': sleeping for %d seconds at %s..."
|
|
WRMSG( HHC00393, "D", CCKD_DH_THREAD_NAME, cckdblk.dhint, curtime );
|
|
}
|
|
|
|
tm.tv_sec = tv_now.tv_sec + cckdblk.dhint;
|
|
tm.tv_nsec = tv_now.tv_usec * 1000;
|
|
|
|
timed_wait_condition( &cckdblk.dhcond, &DH_GC_LOCK, &tm );
|
|
}
|
|
// end while(1)
|
|
|
|
/* We're exiting... */
|
|
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' ended"
|
|
LOG_THREAD_END( CCKD_DH_THREAD_NAME );
|
|
|
|
obtain_lock( &cckdblk.dhlock );
|
|
{
|
|
cckdblk.dhs--;
|
|
cckdblk.dha--;
|
|
|
|
/* Last thread to terminate? */
|
|
if (cckdblk.dhs <= 0)
|
|
signal_condition( &cckdblk.termcond );
|
|
}
|
|
release_lock( &cckdblk.dhlock );
|
|
|
|
UNLOCK_DH_GC(); // (allow Garbage Collector to run)
|
|
|
|
return NULL;
|
|
} /* end thread cckd_dh */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Flush updated cache entries for a device */
|
|
/* */
|
|
/* Caller holds the cckd->cckdiolock */
|
|
/* cckdblk.wrlock then cache_lock is obtained and released */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_flush_cache(DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
TID tid; /* Writer thread id */
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_flush_cache( dev );
|
|
return;
|
|
}
|
|
|
|
/* Scan cache for updated cache entries */
|
|
obtain_lock (&cckdblk.wrlock);
|
|
cache_lock (CACHE_DEVBUF);
|
|
cache_scan (CACHE_DEVBUF, cckd_flush_cache_scan, dev);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
/* Schedule the writer if any writes are pending */
|
|
if (cckdblk.wrpending)
|
|
{
|
|
if (cckdblk.wrwaiting)
|
|
signal_condition (&cckdblk.wrcond);
|
|
else if (cckdblk.wrs < cckdblk.wrmax)
|
|
{
|
|
/* Schedule a new writer thread */
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Starting thread %s, active=%d, started=%d, max=%d"
|
|
WRMSG(HHC00107, "I", CCKD_WR_THREAD_NAME "()",
|
|
cckdblk.wra, cckdblk.wrs, cckdblk.wrmax);
|
|
|
|
++cckdblk.wrs;
|
|
|
|
/* Release lock across thread create to prevent interlock */
|
|
release_lock(&cckdblk.wrlock);
|
|
{
|
|
rc = create_thread( &tid, JOINABLE, cckd_writer, NULL, CCKD_WR_THREAD_NAME );
|
|
}
|
|
obtain_lock(&cckdblk.wrlock);
|
|
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread() for %s %d of %d: %s"
|
|
WRMSG(HHC00106, "E", CCKD_WR_THREAD_NAME "()",
|
|
cckdblk.wrs-1, cckdblk.wrmax, strerror(rc));
|
|
|
|
--cckdblk.wrs;
|
|
}
|
|
}
|
|
}
|
|
|
|
release_lock (&cckdblk.wrlock);
|
|
}
|
|
|
|
int cckd_flush_cache_scan (int *answer, int ix, int i, void *data)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
U16 devnum; /* Cached device number */
|
|
U32 trk; /* Cached track */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
|
|
UNREFERENCED(answer);
|
|
cckd = dev->cckd_ext;
|
|
CCKD_CACHE_GETKEY(i, devnum, trk);
|
|
if ((cache_getflag(ix,i) & CACHE_BUSY) == CCKD_CACHE_UPDATED
|
|
&& dev->devnum == devnum)
|
|
{
|
|
cache_setflag (ix, i, ~CCKD_CACHE_UPDATED, CCKD_CACHE_WRITE);
|
|
++cckd->wrpending;
|
|
++cckdblk.wrpending;
|
|
CCKD_TRACE( "flush file[%d] cache[%d] %4.4X trk %d",
|
|
cckd->sfn, i, devnum, trk);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void cckd_flush_cache_all()
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
DEVBLK *dev = NULL; /* -> device block */
|
|
|
|
cckd_lock_devchain(0);
|
|
for (dev = cckdblk.dev1st; dev; dev = cckd->devnext)
|
|
{
|
|
cckd = dev->cckd_ext;
|
|
obtain_lock (&cckd->cckdiolock);
|
|
if (!cckd->merging && !cckd->stopping)
|
|
cckd_flush_cache(dev);
|
|
release_lock (&cckd->cckdiolock);
|
|
}
|
|
cckd_unlock_devchain();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Purge cache entries for a device */
|
|
/* */
|
|
/* Caller holds the cckdiolock */
|
|
/* cache_lock is obtained and released */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_purge_cache(DEVBLK *dev)
|
|
{
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_purge_cache( dev );
|
|
return;
|
|
}
|
|
|
|
/* Scan cache and purge entries */
|
|
cache_lock (CACHE_DEVBUF);
|
|
cache_scan (CACHE_DEVBUF, cckd_purge_cache_scan, dev);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
}
|
|
int cckd_purge_cache_scan (int *answer, int ix, int i, void *data)
|
|
{
|
|
U16 devnum; /* Cached device number */
|
|
U32 trk; /* Cached track */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
|
|
UNREFERENCED(answer);
|
|
CCKD_CACHE_GETKEY(i, devnum, trk);
|
|
if (dev->devnum == devnum)
|
|
{
|
|
cache_release (ix, i, 0);
|
|
CCKD_TRACE( "purge cache[%d] %4.4X trk %d purged",
|
|
i, devnum, trk);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Writer thread */
|
|
/*-------------------------------------------------------------------*/
|
|
void* cckd_writer( void* arg )
|
|
{
|
|
int writer; /* Writer identifier */
|
|
int o; /* Cache entry found */
|
|
TID tid; /* Writer thread id */
|
|
char threadname[40];
|
|
int rc;
|
|
int wrs;
|
|
|
|
UNREFERENCED( arg );
|
|
|
|
/* Set the writer thread's priority just BELOW the CPU threads'
|
|
in order to minimize any potential impact from compression.
|
|
*/
|
|
if (!cckdblk.batch)
|
|
{
|
|
cckdblk.wrprio = sysblk.cpuprio - 1;
|
|
SET_THREAD_PRIORITY( cckdblk.wrprio, sysblk.qos_user_initiated );
|
|
}
|
|
|
|
obtain_lock( &cckdblk.wrlock );
|
|
|
|
writer = ++cckdblk.wra;
|
|
MSGBUF( threadname, CCKD_WR_THREAD_NAME " thread %d", writer );
|
|
|
|
/* Return with message if too many already started */
|
|
if (writer > cckdblk.wrmax)
|
|
{
|
|
--cckdblk.wrs; /* decrease threads started */
|
|
--cckdblk.wra; /* decrease threads active */
|
|
|
|
if (cckdblk.termwr) /* choose thread termination message */
|
|
{
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' ended"
|
|
LOG_THREAD_END( threadname );
|
|
}
|
|
else
|
|
if (!cckdblk.batch || cckdblk.batchml > 0)
|
|
// "Ending thread "TIDPAT" %s, pri=%d, started=%d, max=%d exceeded"
|
|
WRMSG( HHC00108, "W", TID_CAST( thread_id()), threadname,
|
|
get_thread_priority(), writer, cckdblk.wrmax );
|
|
|
|
release_lock( &cckdblk.wrlock );
|
|
signal_condition( &cckdblk.termcond );/* shutting down */
|
|
return NULL;
|
|
}
|
|
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' started"
|
|
LOG_THREAD_BEGIN( threadname );
|
|
|
|
while (!cckdblk.termwr && (writer <= cckdblk.wrmax || cckdblk.wrpending))
|
|
{
|
|
/* Wait (but not forever!) for work */
|
|
if (cckdblk.wrpending == 0)
|
|
{
|
|
cckdblk.wrwaiting++;
|
|
{
|
|
timed_wait_condition_relative_usecs( &cckdblk.wrcond, &cckdblk.wrlock, 1000000, NULL );
|
|
}
|
|
cckdblk.wrwaiting--;
|
|
}
|
|
|
|
/* Scan the cache for the oldest pending write */
|
|
cache_lock( CACHE_DEVBUF );
|
|
{
|
|
o = cache_scan( CACHE_DEVBUF, cckd_writer_scan, NULL );
|
|
|
|
/* Possibly shutting down if no writes pending */
|
|
if (o < 0)
|
|
{
|
|
cache_unlock( CACHE_DEVBUF );
|
|
cckdblk.wrpending = 0;
|
|
continue;
|
|
}
|
|
|
|
/* We will process this cache entry. Clear flags to prevent
|
|
any other writer threads from trying to process it too. */
|
|
cache_setflag( CACHE_DEVBUF, o, ~CCKD_CACHE_WRITE, CCKD_CACHE_WRITING );
|
|
}
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
/* Schedule the other writers if any writes are still pending */
|
|
|
|
cckdblk.wrpending--;
|
|
|
|
if (cckdblk.wrpending)
|
|
{
|
|
if (cckdblk.wrwaiting)
|
|
{
|
|
signal_condition( &cckdblk.wrcond );
|
|
}
|
|
else if (cckdblk.wrs < cckdblk.wrmax)
|
|
{
|
|
/* Schedule a new writer thread */
|
|
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Starting thread %s, active=%d, started=%d, max=%d"
|
|
WRMSG( HHC00107, "I", CCKD_WR_THREAD_NAME "() from " CCKD_WR_THREAD_NAME "()",
|
|
cckdblk.wra, cckdblk.wrs, cckdblk.wrmax );
|
|
|
|
++cckdblk.wrs;
|
|
|
|
/* Release lock across thread create to prevent interlock */
|
|
release_lock( &cckdblk.wrlock );
|
|
{
|
|
rc = create_thread( &tid, JOINABLE, cckd_writer, NULL, CCKD_WR_THREAD_NAME );
|
|
}
|
|
obtain_lock( &cckdblk.wrlock );
|
|
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread() for %s %d of %d: %s"
|
|
WRMSG( HHC00106, "E", CCKD_WR_THREAD_NAME "() from " CCKD_WR_THREAD_NAME "()",
|
|
cckdblk.wrs-1, cckdblk.wrmax, strerror( rc ));
|
|
|
|
--cckdblk.wrs;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write the updated track image */
|
|
release_lock( &cckdblk.wrlock );
|
|
{
|
|
cckd_writer_write( writer, o );
|
|
}
|
|
obtain_lock( &cckdblk.wrlock );
|
|
}
|
|
/* end while (writer <= cckdblk.wrmax || cckdblk.wrpending) */
|
|
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' ended"
|
|
LOG_THREAD_END( threadname );
|
|
|
|
cckdblk.wrs--;
|
|
cckdblk.wra--;
|
|
|
|
wrs = cckdblk.wrs;
|
|
|
|
release_lock( &cckdblk.wrlock );
|
|
|
|
/* Last thread to terminate? */
|
|
if (wrs <= 0)
|
|
signal_condition( &cckdblk.termcond );
|
|
|
|
return NULL;
|
|
} /* end thread cckd_writer */
|
|
|
|
int cckd_writer_scan( int* o, int ix, int i, void* data )
|
|
{
|
|
UNREFERENCED( data );
|
|
|
|
if (1
|
|
&& (cache_getflag( ix, i ) & DEVBUF_TYPE_COMP)
|
|
&& (cache_getflag( ix, i ) & CCKD_CACHE_WRITE)
|
|
&& (0
|
|
|| *o == -1
|
|
|| cache_getage( ix, i ) < cache_getage( ix, *o )
|
|
)
|
|
)
|
|
*o = i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd writer thread helper: write the cached track image */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_writer_write( int writer, int o )
|
|
{
|
|
CCKD_EXT* cckd; /* -> cckd extension */
|
|
DEVBLK* dev; /* Device block */
|
|
U16 devnum; /* Device number */
|
|
int trk; /* Track number */
|
|
BYTE* buf; /* Buffer */
|
|
BYTE* bufp; /* Buffer to be written */
|
|
int len, bufl; /* Buffer lengths */
|
|
int comp; /* Compression algorithm */
|
|
int parm; /* Compression parameter */
|
|
U32 flag; /* Cache flag */
|
|
BYTE buf2[ 64*1024 ]; /* 64K Compress buffer */
|
|
|
|
/* Prepare to compress */
|
|
CCKD_CACHE_GETKEY( o, devnum, trk );
|
|
dev = cckd_find_device_by_devnum( devnum );
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_writer_write( writer, o );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
buf = cache_getbuf( CACHE_DEVBUF, o, 0 );
|
|
len = cckd_trklen( dev, buf );
|
|
|
|
comp = len < CCKD_COMPRESS_MIN ? CCKD_COMPRESS_NONE :
|
|
cckdblk.comp == 0xff ? cckd->cdevhdr[ cckd->sfn ].cmp_algo
|
|
: cckdblk.comp;
|
|
|
|
parm = cckdblk.compparm < 0 ? cckd->cdevhdr[ cckd->sfn ].cmp_parm
|
|
: cckdblk.compparm;
|
|
|
|
CCKD_TRACE( "%d wrtrk[%d] %d len %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
writer, o, trk, len, buf, buf[0], buf[1],buf[2],buf[3],buf[4] );
|
|
|
|
/* Compress the image if not null */
|
|
if ((len = cckd_check_null_trk( dev, buf, trk, len )) > CKD_NULLTRK_FMTMAX)
|
|
{
|
|
/* Stress adjustments */
|
|
if (1
|
|
&& !cckdblk.nostress
|
|
&& (0
|
|
|| cache_waiters ( CACHE_DEVBUF )
|
|
|| cache_busy ( CACHE_DEVBUF ) > 90
|
|
)
|
|
)
|
|
{
|
|
cckdblk.stats_stresswrites++;
|
|
|
|
comp = len < CCKD_STRESS_MINLEN ? CCKD_COMPRESS_NONE
|
|
: CCKD_STRESS_COMP;
|
|
|
|
parm = cache_busy(CACHE_DEVBUF) <= 95 ? CCKD_STRESS_PARM1
|
|
: CCKD_STRESS_PARM2;
|
|
}
|
|
|
|
/* Compress the track image */
|
|
CCKD_TRACE( "%d wrtrk[%d] %d comp %s parm %d",
|
|
writer, o, trk, compname[ comp ], parm );
|
|
|
|
bufp = (BYTE*) &buf2;
|
|
bufl = cckd_compress( dev, &bufp, buf, len, comp, parm );
|
|
|
|
CCKD_TRACE( "%d wrtrk[%d] %d compressed length %d",
|
|
writer, o, trk, bufl );
|
|
}
|
|
else
|
|
{
|
|
bufp = buf;
|
|
bufl = len;
|
|
}
|
|
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* Turn on read-write header bits if not already on */
|
|
if (!(cckd->cdevhdr[ cckd->sfn ].cdh_opts & CCKD_OPT_OPENED))
|
|
{
|
|
cckd->cdevhdr[ cckd->sfn ].cdh_opts |= (CCKD_OPT_OPENED | CCKD_OPT_OPENRW);
|
|
cckd_write_chdr( dev );
|
|
}
|
|
|
|
/* Write the track image */
|
|
cckd_write_trkimg( dev, bufp, bufl, trk, CCKD_SIZE_ANY );
|
|
cckd->needsdh = 1; /* We've updated the file. */
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cache_lock( CACHE_DEVBUF );
|
|
{
|
|
flag = cache_setflag( CACHE_DEVBUF, o, ~CCKD_CACHE_WRITING, 0 );
|
|
}
|
|
cache_unlock( CACHE_DEVBUF );
|
|
|
|
cckd->wrpending--;
|
|
|
|
if (1
|
|
&& cckd->cckdwaiters
|
|
&& (0
|
|
|| (flag & CCKD_CACHE_IOWAIT)
|
|
|| !cckd->wrpending
|
|
)
|
|
)
|
|
{
|
|
CCKD_TRACE( "writer[%d] cache[%2.2d] %d signalling write complete",
|
|
writer, o, trk );
|
|
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
}
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
CCKD_TRACE( "%d wrtrk[%2.2d] %d complete flags:%8.8x",
|
|
writer, o, trk, cache_getflag( CACHE_DEVBUF, o ));
|
|
|
|
/* Schedule the garbage collector, if it's supposed to be running. */
|
|
if (cckdblk.gcint > 0)
|
|
cckd_gcstart( false );
|
|
|
|
/* Ensure the Dasd Hardener is running, if it's supposed to be. */
|
|
if (cckdblk.dhint > 0)
|
|
cckd_dhstart( false );
|
|
|
|
} /* end function cckd_writer_write */
|
|
|
|
#if defined( DEBUG_FREESPACE )
|
|
/*-------------------------------------------------------------------*/
|
|
/* Debug routine for checking the free space array */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_chk_space(DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* Shadow file index */
|
|
int err = 0, n = 0, i, p;
|
|
size_t largest=0;
|
|
size_t total=0;
|
|
off_t fpos;
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_chk_space( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
p = -1;
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
n++; total += cckd->ifb[i].ifb_len;
|
|
if (n > cckd->free_count) break;
|
|
if (cckd->ifb[i].ifb_idxprv != p)
|
|
err = 1;
|
|
if (cckd->ifb[i].ifb_idxnxt >= 0)
|
|
{
|
|
if (fpos + cckd->ifb[i].ifb_len > cckd->ifb[i].ifb_offnxt)
|
|
err = 1;
|
|
}
|
|
else
|
|
{
|
|
if (fpos + cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].cdh_size)
|
|
err = 1;
|
|
}
|
|
if (cckd->ifb[i].ifb_pending == 0 && cckd->ifb[i].ifb_len > largest)
|
|
largest = cckd->ifb[i].ifb_len;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
p = i;
|
|
}
|
|
|
|
if (err
|
|
|| (cckd->cdevhdr[sfx].free_off != 0 && cckd->cdevhdr[sfx].free_num == 0)
|
|
|| (cckd->cdevhdr[sfx].free_off == 0 && cckd->cdevhdr[sfx].free_num != 0)
|
|
|| (n != cckd->cdevhdr[sfx].free_num)
|
|
|| (total != cckd->cdevhdr[sfx].free_total - cckd->cdevhdr[sfx].free_imbed)
|
|
|| (cckd->free_idxlast != p)
|
|
|| (largest != cckd->cdevhdr[sfx].free_largest)
|
|
)
|
|
{
|
|
CCKD_TRACE( "cdevhdr[%d] size %10d used %10d free 0x%8.8x",
|
|
sfx, cckd->cdevhdr[sfx].cdh_size, cckd->cdevhdr[sfx].cdh_used,
|
|
cckd->cdevhdr[sfx].free_off);
|
|
CCKD_TRACE( " nbr %10d total %10d imbed %10d largest %10d",
|
|
cckd->cdevhdr[sfx].free_num,
|
|
cckd->cdevhdr[sfx].free_total, cckd->cdevhdr[sfx].free_imbed,
|
|
cckd->cdevhdr[sfx].free_largest);
|
|
CCKD_TRACE( "free %p nbr %d 1st %d last %d avail %d",
|
|
cckd->ifb,cckd->free_count, cckd->free_idx1st,
|
|
cckd->free_idxlast, cckd->free_idxavail);
|
|
CCKD_TRACE( "found nbr %d total %ld largest %ld",n,(long)total,(long)largest);
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
for (n = 0, i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (++n > cckd->free_count) break;
|
|
CCKD_TRACE( "%4d: [%4d] prev[%4d] next[%4d] pos %16.16"PRIx64" len %8d %16.16"PRIx64" pend %d",
|
|
n, i, cckd->ifb[i].ifb_idxprv, cckd->ifb[i].ifb_idxnxt,
|
|
fpos, cckd->ifb[i].ifb_len,
|
|
fpos + cckd->ifb[i].ifb_len, cckd->ifb[i].ifb_pending);
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
cckd_print_itrace();
|
|
}
|
|
} /* end function cckd_chk_space */
|
|
#endif // defined( DEBUG_FREESPACE )
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Get file space */
|
|
/*-------------------------------------------------------------------*/
|
|
off_t cckd_get_space(DEVBLK *dev, int *size, int flags)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int i,p,n; /* Free space indexes */
|
|
int len2; /* Other lengths */
|
|
off_t fpos; /* Free space offset */
|
|
unsigned int flen; /* Free space size */
|
|
int sfx; /* Shadow file index */
|
|
int len; /* Requested length */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_get_space( dev, size, flags );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
len = *size;
|
|
|
|
if (flags & CCKD_L2SPACE)
|
|
{
|
|
flags |= CCKD_SIZE_EXACT;
|
|
len = *size = CCKD_L2TAB_SIZE;
|
|
}
|
|
|
|
CCKD_TRACE( "get_space len %d largest %d flags 0x%2.2x",
|
|
len, cckd->cdevhdr[sfx].free_largest, flags);
|
|
|
|
if (len <= CKD_NULLTRK_FMTMAX)
|
|
return 0;
|
|
|
|
if (!cckd->ifb)
|
|
cckd_read_fsp (dev);
|
|
|
|
len2 = len + CCKD_FREEBLK_SIZE;
|
|
|
|
/* Get space at the end if no space is large enough */
|
|
if (len2 > (int)cckd->cdevhdr[sfx].free_largest
|
|
&& len != (int)cckd->cdevhdr[sfx].free_largest)
|
|
{
|
|
|
|
cckd_get_space_atend:
|
|
|
|
fpos = (off_t)cckd->cdevhdr[sfx].cdh_size;
|
|
if ((U64)fpos > (cckd->cckd_maxsize - len))
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: get space error, size exceeds %"PRId64"M"
|
|
WRMSG (HHC00304, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
|
|
(S64) (cckd->cckd_maxsize >> 20) + 1);
|
|
return -1;
|
|
}
|
|
cckd->cdevhdr[sfx].cdh_size += len;
|
|
cckd->cdevhdr[sfx].cdh_used += len;
|
|
|
|
CCKD_TRACE( "get_space atend 0x%16.16"PRIx64" len %d",fpos, len);
|
|
|
|
return fpos;
|
|
}
|
|
|
|
/* Scan free space chain */
|
|
fpos = (off_t)cckd->cdevhdr[sfx].free_off;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (cckd->ifb[i].ifb_pending == 0
|
|
&& (len2 <= (int)cckd->ifb[i].ifb_len || len == (int)cckd->ifb[i].ifb_len)
|
|
&& ((flags & CCKD_L2SPACE) || (U64)fpos >= cckd->L2_bounds))
|
|
break;
|
|
fpos = (off_t)cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
|
|
/* This can happen if largest comes before L2_bounds */
|
|
if (i < 0) goto cckd_get_space_atend;
|
|
|
|
flen = cckd->ifb[i].ifb_len;
|
|
p = cckd->ifb[i].ifb_idxprv;
|
|
n = cckd->ifb[i].ifb_idxnxt;
|
|
|
|
/*
|
|
* If `CCKD_SIZE_ANY' bit is set and the left over space is small
|
|
* enough, then use the entire free space
|
|
*/
|
|
if ((flags & CCKD_SIZE_ANY) && flen <= cckd->free_minsize)
|
|
*size = (int)flen;
|
|
|
|
/* Remove the new space from free space */
|
|
if (*size < (int)flen)
|
|
{
|
|
cckd->ifb[i].ifb_len -= *size;
|
|
if (p >= 0)
|
|
cckd->ifb[p].ifb_offnxt += *size;
|
|
else
|
|
cckd->cdevhdr[sfx].free_off += *size;
|
|
}
|
|
else
|
|
{
|
|
cckd->cdevhdr[sfx].free_num--;
|
|
|
|
/* Remove the free space entry from the chain */
|
|
if (p >= 0)
|
|
{
|
|
cckd->ifb[p].ifb_offnxt = cckd->ifb[i].ifb_offnxt;
|
|
cckd->ifb[p].ifb_idxnxt = n;
|
|
}
|
|
else
|
|
{
|
|
cckd->cdevhdr[sfx].free_off = cckd->ifb[i].ifb_offnxt;
|
|
cckd->free_idx1st = n;
|
|
}
|
|
|
|
if (n >= 0)
|
|
cckd->ifb[n].ifb_idxprv = p;
|
|
else
|
|
cckd->free_idxlast = p;
|
|
|
|
/* Add entry to the available queue */
|
|
cckd->ifb[i].ifb_idxnxt = cckd->free_idxavail;
|
|
cckd->free_idxavail = i;
|
|
}
|
|
|
|
/* Find the largest free space if we got the largest */
|
|
if (flen >= cckd->cdevhdr[sfx].free_largest)
|
|
{
|
|
int i;
|
|
cckd->cdevhdr[sfx].free_largest = 0;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
if (cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].free_largest
|
|
&& cckd->ifb[i].ifb_pending == 0)
|
|
cckd->cdevhdr[sfx].free_largest = cckd->ifb[i].ifb_len;
|
|
}
|
|
|
|
/* Update free space stats */
|
|
cckd->cdevhdr[sfx].cdh_used += len;
|
|
cckd->cdevhdr[sfx].free_total -= len;
|
|
|
|
cckd->cdevhdr[sfx].free_imbed += *size - len;
|
|
|
|
CCKD_TRACE( "get_space found 0x%16.16"PRIx64" len %d size %d",
|
|
fpos, len, *size);
|
|
|
|
return fpos;
|
|
|
|
} /* end function cckd_get_space */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Release file space */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_rel_space(DEVBLK *dev, off_t pos, int len, int size)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* Shadow file index */
|
|
off_t ppos, npos; /* Prev/next free offsets */
|
|
int i, p, n; /* Free space indexes */
|
|
int pending; /* Calculated pending value */
|
|
int fsize = size; /* Free space size */
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_rel_space( dev, pos, len, size );
|
|
return;
|
|
}
|
|
|
|
if (len <= CKD_NULLTRK_FMTMAX || pos == CCKD_NOSIZE || pos == CCKD_MAXSIZE)
|
|
return;
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "rel_space offset 0x%16.16"PRIx64" len %d size %d",
|
|
pos, len, size);
|
|
|
|
if (!cckd->ifb) cckd_read_fsp (dev);
|
|
|
|
CCKD_CHK_SPACE(dev);
|
|
|
|
/* Scan free space chain */
|
|
ppos = -1;
|
|
npos = cckd->cdevhdr[sfx].free_off;
|
|
for (p = -1, n = cckd->free_idx1st; n >= 0; n = cckd->ifb[n].ifb_idxnxt)
|
|
{
|
|
if (pos < npos) break;
|
|
ppos = npos;
|
|
npos = cckd->ifb[n].ifb_offnxt;
|
|
p = n;
|
|
}
|
|
|
|
/* Calculate the `pending' value */
|
|
pending = cckdblk.freepend >= 0 ? cckdblk.freepend : 1 + (1 - cckdblk.fsync);
|
|
|
|
/* If possible use previous adjacent free space otherwise get an available one */
|
|
if (p >= 0 && ppos + cckd->ifb[p].ifb_len == pos && cckd->ifb[p].ifb_pending == pending)
|
|
{
|
|
cckd->ifb[p].ifb_len += size;
|
|
fsize = cckd->ifb[p].ifb_len;
|
|
}
|
|
else
|
|
{
|
|
/* Increase the size of the free space array if necessary */
|
|
if (cckd->free_idxavail < 0)
|
|
{
|
|
int new_free_count = cckd->free_count + CCKD_IFB_ENTS_INCR;
|
|
if (!(cckd->ifb = cckd_realloc( dev, "ifb", cckd->ifb, new_free_count * CCKD_IFREEBLK_SIZE )))
|
|
return;
|
|
cckd->free_idxavail = cckd->free_count;
|
|
cckd->free_count = new_free_count;
|
|
for (i = cckd->free_idxavail; i < cckd->free_count; i++)
|
|
cckd->ifb[i].ifb_idxnxt = i + 1;
|
|
cckd->ifb[i-1].ifb_idxnxt = -1;
|
|
cckd->free_minsize = CCKD_MIN_FREESIZE( cckd->free_count );
|
|
}
|
|
|
|
/* Get an available free space entry */
|
|
i = cckd->free_idxavail;
|
|
cckd->free_idxavail = cckd->ifb[i].ifb_idxnxt;
|
|
cckd->cdevhdr[sfx].free_num++;
|
|
|
|
/* Update the new entry */
|
|
cckd->ifb[i].ifb_idxprv = p;
|
|
cckd->ifb[i].ifb_idxnxt = n;
|
|
cckd->ifb[i].ifb_len = size;
|
|
cckd->ifb[i].ifb_pending = pending;
|
|
|
|
/* Update the previous entry */
|
|
if (p >= 0)
|
|
{
|
|
cckd->ifb[i].ifb_offnxt = cckd->ifb[p].ifb_offnxt;
|
|
cckd->ifb[p].ifb_offnxt = (U32)pos;
|
|
cckd->ifb[p].ifb_idxnxt = i;
|
|
}
|
|
else
|
|
{
|
|
cckd->ifb[i].ifb_offnxt = cckd->cdevhdr[sfx].free_off;
|
|
cckd->cdevhdr[sfx].free_off = (U32)pos;
|
|
cckd->free_idx1st = i;
|
|
}
|
|
|
|
/* Update the next entry */
|
|
if (n >= 0)
|
|
cckd->ifb[n].ifb_idxprv = i;
|
|
else
|
|
cckd->free_idxlast = i;
|
|
}
|
|
|
|
/* Update the free space statistics */
|
|
cckd->cdevhdr[sfx].cdh_used -= len;
|
|
cckd->cdevhdr[sfx].free_total += len;
|
|
cckd->cdevhdr[sfx].free_imbed -= size - len;
|
|
if (!pending && (U32)fsize > cckd->cdevhdr[sfx].free_largest)
|
|
cckd->cdevhdr[sfx].free_largest = (U32)fsize;
|
|
|
|
CCKD_CHK_SPACE(dev);
|
|
|
|
} /* end function cckd_rel_space */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Flush pending free space */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_flush_space(DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int p,i,n; /* Free free space indexes */
|
|
int sfx; /* Shadow file index */
|
|
U32 ppos, pos; /* Free space offsets */
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_flush_space( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "flush_space nbr %d",cckd->cdevhdr[sfx].free_num);
|
|
|
|
/* Make sure the free space chain is built */
|
|
if (!cckd->ifb) cckd_read_fsp (dev);
|
|
|
|
CCKD_CHK_SPACE(dev);
|
|
|
|
if (cckd->cdevhdr[sfx].free_num == 0 || cckd->cdevhdr[sfx].free_off == 0)
|
|
{
|
|
cckd->cdevhdr[sfx].free_num = cckd->cdevhdr[sfx].free_off = 0;
|
|
cckd->free_idx1st = cckd->free_idxlast = cckd->free_idxavail = -1;
|
|
}
|
|
|
|
pos = cckd->cdevhdr[sfx].free_off;
|
|
ppos = p = -1;
|
|
cckd->cdevhdr[sfx].free_num = cckd->cdevhdr[sfx].free_largest = 0;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
/* Decrement the pending count */
|
|
if (cckd->ifb[i].ifb_pending)
|
|
--cckd->ifb[i].ifb_pending;
|
|
|
|
/* Combine adjacent free spaces */
|
|
while (pos + cckd->ifb[i].ifb_len == cckd->ifb[i].ifb_offnxt)
|
|
{
|
|
n = cckd->ifb[i].ifb_idxnxt;
|
|
if (cckd->ifb[n].ifb_pending > cckd->ifb[i].ifb_pending + 1
|
|
|| cckd->ifb[n].ifb_pending < cckd->ifb[i].ifb_pending)
|
|
break;
|
|
cckd->ifb[i].ifb_offnxt = cckd->ifb[n].ifb_offnxt;
|
|
cckd->ifb[i].ifb_len += cckd->ifb[n].ifb_len;
|
|
cckd->ifb[i].ifb_idxnxt = cckd->ifb[n].ifb_idxnxt;
|
|
cckd->ifb[n].ifb_idxnxt = cckd->free_idxavail;
|
|
cckd->free_idxavail = n;
|
|
n = cckd->ifb[i].ifb_idxnxt;
|
|
if (n >= 0)
|
|
cckd->ifb[n].ifb_idxprv = i;
|
|
|
|
}
|
|
ppos = pos;
|
|
pos = cckd->ifb[i].ifb_offnxt;
|
|
cckd->cdevhdr[sfx].free_num++;
|
|
if (cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].free_largest
|
|
&& !cckd->ifb[i].ifb_pending)
|
|
cckd->cdevhdr[sfx].free_largest = cckd->ifb[i].ifb_len;
|
|
p = i;
|
|
}
|
|
cckd->free_idxlast = p;
|
|
|
|
CCKD_TRACE( "rel_flush_space nbr %d (after merge)",
|
|
cckd->cdevhdr[sfx].free_num);
|
|
|
|
/* If the last free space is at the end of the file then release it */
|
|
if (p >= 0 && ppos + cckd->ifb[p].ifb_len == cckd->cdevhdr[sfx].cdh_size
|
|
&& !cckd->ifb[p].ifb_pending)
|
|
{
|
|
i = p;
|
|
p = cckd->ifb[i].ifb_idxprv;
|
|
|
|
CCKD_TRACE( "file[%d] rel_flush_space atend 0x%16.16"PRIx64" len %d",
|
|
sfx, ppos, cckd->ifb[i].ifb_len);
|
|
|
|
/* Remove the entry from the chain */
|
|
if (p >= 0)
|
|
{
|
|
cckd->ifb[p].ifb_offnxt = 0;
|
|
cckd->ifb[p].ifb_idxnxt = -1;
|
|
}
|
|
else
|
|
{
|
|
cckd->cdevhdr[sfx].free_off = 0;
|
|
cckd->free_idx1st = -1;
|
|
}
|
|
cckd->free_idxlast = p;
|
|
|
|
/* Add the entry to the available chain */
|
|
cckd->ifb[i].ifb_idxnxt = cckd->free_idxavail;
|
|
cckd->free_idxavail = i;
|
|
|
|
/* Update the device header */
|
|
cckd->cdevhdr[sfx].cdh_size -= cckd->ifb[i].ifb_len;
|
|
cckd->cdevhdr[sfx].free_total -= cckd->ifb[i].ifb_len;
|
|
cckd->cdevhdr[sfx].free_num--;
|
|
if (cckd->ifb[i].ifb_len >= cckd->cdevhdr[sfx].free_largest)
|
|
{
|
|
cckd->cdevhdr[sfx].free_largest = 0;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
if (cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].free_largest
|
|
&& cckd->ifb[i].ifb_pending == 0)
|
|
cckd->cdevhdr[sfx].free_largest = cckd->ifb[i].ifb_len;
|
|
}
|
|
|
|
/* Truncate the file */
|
|
cckd_ftruncate (dev, sfx, (off_t)cckd->cdevhdr[sfx].cdh_size);
|
|
|
|
} /* Release space at end of the file */
|
|
|
|
CCKD_CHK_SPACE(dev);
|
|
|
|
} /* end function cckd_flush_space */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read compressed dasd header */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read_chdr (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_read_chdr( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] read_chdr", sfx);
|
|
|
|
memset(&cckd->cdevhdr[sfx], 0, CCKD_DEVHDR_SIZE);
|
|
|
|
/* Read the device header */
|
|
if (cckd_read (dev, sfx, CKD_DEVHDR_SIZE, &cckd->cdevhdr[sfx], CCKD_DEVHDR_SIZE) < 0)
|
|
return -1;
|
|
|
|
/* Check endian format */
|
|
cckd->swapend[sfx] = 0;
|
|
if ((cckd->cdevhdr[sfx].cdh_opts & CCKD_OPT_BIGEND) != cckd_def_opt_bigend())
|
|
{
|
|
if (cckd->open[sfx] == CCKD_OPEN_RW)
|
|
{
|
|
if (cckd_swapend (dev) < 0)
|
|
return -1;
|
|
cckd_swapend_chdr (&cckd->cdevhdr[sfx]);
|
|
}
|
|
else
|
|
{
|
|
cckd->swapend[sfx] = 1;
|
|
cckd_swapend_chdr (&cckd->cdevhdr[sfx]);
|
|
}
|
|
}
|
|
|
|
/* Set default null format */
|
|
if (cckd->cdevhdr[sfx].cdh_nullfmt > CKD_NULLTRK_FMTMAX)
|
|
cckd->cdevhdr[sfx].cdh_nullfmt = 0;
|
|
|
|
if (cckd->cdevhdr[sfx].cdh_nullfmt == 0 && dev->oslinux && dev->devtype == 0x3390)
|
|
cckd->cdevhdr[sfx].cdh_nullfmt = CKD_NULLTRK_FMT2;
|
|
|
|
if (cckd->cdevhdr[sfx].cdh_nullfmt == CKD_NULLTRK_FMT2)
|
|
dev->oslinux = 1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_read_chdr */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write compressed dasd header */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_write_chdr (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_write_chdr( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] write_chdr", sfx);
|
|
|
|
/* Set version.release.modlvl */
|
|
cckd->cdevhdr[sfx].cdh_vrm[0] = CCKD_VERSION;
|
|
cckd->cdevhdr[sfx].cdh_vrm[1] = CCKD_RELEASE;
|
|
cckd->cdevhdr[sfx].cdh_vrm[2] = CCKD_MODLVL;
|
|
|
|
if (cckd_write (dev, sfx, CCKD_DEVHDR_POS, &cckd->cdevhdr[sfx], CCKD_DEVHDR_SIZE) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_write_chdr */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read the level 1 table */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read_l1 (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
int len; /* Length of level 1 table */
|
|
int i; /* Work integer */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_read_l1( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] read_l1 offset 0x%"PRIx64,
|
|
sfx, (U64)CCKD_L1TAB_POS);
|
|
|
|
/* Free the old level 1 table if it exists */
|
|
cckd->L1tab[sfx] = cckd_free (dev, "l1", cckd->L1tab[sfx]);
|
|
|
|
/* Allocate the level 1 table */
|
|
len = cckd->cdevhdr[sfx].num_L1tab * CCKD_L1ENT_SIZE;
|
|
if ((cckd->L1tab[sfx] = cckd_malloc (dev, "l1", len)) == NULL)
|
|
return -1;
|
|
if ( sfx )
|
|
memset(cckd->L1tab[sfx], 0xFF, len);
|
|
else
|
|
memset(cckd->L1tab[sfx], 0, len);
|
|
|
|
/* Read the level 1 table */
|
|
if (cckd_read (dev, sfx, CCKD_L1TAB_POS, cckd->L1tab[sfx], len) < 0)
|
|
return -1;
|
|
|
|
/* Fix endianness */
|
|
if (cckd->swapend[sfx])
|
|
cckd_swapend_l1 (cckd->L1tab[sfx], cckd->cdevhdr[sfx].num_L1tab);
|
|
|
|
/* Determine bounds */
|
|
cckd->L2_bounds = CCKD_L1TAB_POS + len;
|
|
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
if (cckd->L1tab[sfx][i] != CCKD_NOSIZE && cckd->L1tab[sfx][i] != CCKD_MAXSIZE)
|
|
cckd->L2_bounds += CCKD_L2TAB_SIZE;
|
|
|
|
/* Check if all l2 tables are within bounds */
|
|
cckd->L2ok = 1;
|
|
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab && cckd->L2ok; i++)
|
|
if (cckd->L1tab[sfx][i] != CCKD_NOSIZE && cckd->L1tab[sfx][i] != CCKD_MAXSIZE)
|
|
if (cckd->L1tab[sfx][i] > cckd->L2_bounds - CCKD_L2TAB_SIZE)
|
|
cckd->L2ok = 0;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_read_l1 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write the level 1 table */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_write_l1 (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
int len; /* Length of level 1 table */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_write_l1( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
len = cckd->cdevhdr[sfx].num_L1tab * CCKD_L1ENT_SIZE;
|
|
|
|
CCKD_TRACE( "file[%d] write_l1 0x%"PRIx64" len %d",
|
|
sfx, (U64)CCKD_L1TAB_POS, len);
|
|
|
|
if (cckd_write (dev, sfx, CCKD_L1TAB_POS, cckd->L1tab[sfx], len) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_write_l1 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Update a level 1 table entry */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_write_l1ent (DEVBLK *dev, int L1idx)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
off_t off; /* Offset to l1 entry */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_write_l1ent( dev, L1idx );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
off = (off_t)(CCKD_L1TAB_POS + L1idx * CCKD_L1ENT_SIZE);
|
|
|
|
CCKD_TRACE( "file[%d] write_l1ent[%d] , 0x%16.16"PRIx64,
|
|
sfx, L1idx, off);
|
|
|
|
if (cckd_write (dev, sfx, off, &cckd->L1tab[sfx][L1idx], CCKD_L1ENT_SIZE) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_write_l1ent */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initial read */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read_init (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT* cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
CKD_DEVHDR devhdr; /* Device header */
|
|
U32 imgtyp; /* Dasd device image type */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_read_init( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] read_init", sfx );
|
|
|
|
/* Read the device header */
|
|
if (cckd_read( dev, sfx, 0, &devhdr, CKD_DEVHDR_SIZE ) < 0)
|
|
return -1;
|
|
|
|
/* Check the device hdr */
|
|
imgtyp = dh_devid_typ( devhdr.dh_devid );
|
|
|
|
if (!sfx && (imgtyp & CKD_C370_TYP)) cckd->ckddasd = 1;
|
|
else if (!sfx && (imgtyp & FBA_C370_TYP)) cckd->fbadasd = 1;
|
|
else if (1
|
|
&& !(sfx && (imgtyp & CKD32_SF_TYP) && cckd->ckddasd)
|
|
&& !(sfx && (imgtyp & FBA32_SF_TYP) && cckd->fbadasd)
|
|
)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: device header id error"
|
|
WRMSG( HHC00305, "E", LCSS_DEVNUM,
|
|
sfx, cckd_sf_name( dev, sfx ));
|
|
return -1;
|
|
}
|
|
|
|
/* Read the compressed header */
|
|
if (cckd_read_chdr( dev ) < 0)
|
|
return -1;
|
|
|
|
/* Read the level 1 table */
|
|
if (cckd_read_l1( dev ) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
} /* end function cckd_read_init */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read free space */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read_fsp( DEVBLK* dev )
|
|
{
|
|
CCKD_EXT* cckd; /* -> cckd extension */
|
|
off_t fpos; /* Free space offset */
|
|
int sfx; /* File index */
|
|
int i; /* Index */
|
|
CCKD_FREEBLK freeblk; /* First freeblk read */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_read_fsp( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] read_fsp number %d",
|
|
sfx, cckd->cdevhdr[sfx].free_num );
|
|
|
|
cckd->ifb = cckd_free( dev, "ifb", cckd->ifb );
|
|
|
|
cckd->free_count = 0;
|
|
cckd->free_idx1st = -1;
|
|
cckd->free_idxlast = -1;
|
|
cckd->free_idxavail = -1;
|
|
|
|
/* Get storage for the internal free space chain
|
|
* in a multiple of 1024 entries
|
|
*/
|
|
i = (int) ROUND_UP( cckd->cdevhdr[sfx].free_num, CCKD_IFB_ENTS_INCR );
|
|
if (!(cckd->ifb = cckd_calloc( dev, "ifb", i, CCKD_IFREEBLK_SIZE )))
|
|
return -1;
|
|
|
|
cckd->free_count = i;
|
|
|
|
/* Build the doubly linked internal free space chain */
|
|
if (cckd->cdevhdr[sfx].free_num)
|
|
{
|
|
cckd->free_idx1st = 0;
|
|
|
|
/* Read the first freeblk to determine old/new format */
|
|
fpos = (off_t)cckd->cdevhdr[sfx].free_off;
|
|
if (cckd_read (dev, sfx, fpos, &freeblk, CCKD_FREEBLK_SIZE) < 0)
|
|
return -1;
|
|
|
|
if (memcmp(&freeblk, "FREE_BLK", 8) == 0)
|
|
{
|
|
/* new format free space */
|
|
CCKD_FREEBLK *fsp;
|
|
U32 ofree = cckd->cdevhdr[sfx].free_off;
|
|
int n = cckd->cdevhdr[sfx].free_num * CCKD_FREEBLK_SIZE;
|
|
if ((fsp = cckd_malloc (dev, "fsp", n)) == NULL)
|
|
return -1;
|
|
fpos += CCKD_FREEBLK_SIZE;
|
|
if (cckd_read (dev, sfx, fpos, fsp, n) < 0)
|
|
return -1;
|
|
for (i = 0; i < cckd->cdevhdr[sfx].free_num; i++)
|
|
{
|
|
if (i == 0)
|
|
cckd->cdevhdr[sfx].free_off = fsp[i].fb_offnxt;
|
|
else
|
|
cckd->ifb[i-1].ifb_offnxt = fsp[i].fb_offnxt;
|
|
cckd->ifb[i].ifb_offnxt = 0;
|
|
cckd->ifb[i].ifb_len = fsp[i].fb_len;
|
|
cckd->ifb[i].ifb_idxprv = i - 1;
|
|
cckd->ifb[i].ifb_idxnxt = i + 1;
|
|
}
|
|
cckd->ifb[i-1].ifb_idxnxt = -1;
|
|
cckd->free_idxlast = i-1;
|
|
fsp = cckd_free (dev, "fsp", fsp);
|
|
|
|
/* truncate if new format free space was at the end */
|
|
if (ofree == cckd->cdevhdr[sfx].cdh_size)
|
|
{
|
|
fpos = (off_t)cckd->cdevhdr[sfx].cdh_size;
|
|
cckd_ftruncate(dev, sfx, fpos);
|
|
}
|
|
} /* new format free space */
|
|
else
|
|
{
|
|
/* old format free space */
|
|
fpos = (off_t)cckd->cdevhdr[sfx].free_off;
|
|
for (i = 0; i < cckd->cdevhdr[sfx].free_num; i++)
|
|
{
|
|
if (cckd_read (dev, sfx, fpos, &cckd->ifb[i], CCKD_FREEBLK_SIZE) < 0)
|
|
return -1;
|
|
cckd->ifb[i].ifb_idxprv = i - 1;
|
|
cckd->ifb[i].ifb_idxnxt = i + 1;
|
|
fpos = (off_t)cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
cckd->ifb[i-1].ifb_idxnxt = -1;
|
|
cckd->free_idxlast = i-1;
|
|
} /* old format free space */
|
|
} /* if (cckd->cdevhdr[sfx].free_num) */
|
|
|
|
/* Build singly linked chain of available free space entries */
|
|
if (cckd->cdevhdr[sfx].free_num < cckd->free_count)
|
|
{
|
|
cckd->free_idxavail = cckd->cdevhdr[sfx].free_num;
|
|
for (i = cckd->free_idxavail; i < cckd->free_count; i++)
|
|
cckd->ifb[i].ifb_idxnxt = i + 1;
|
|
cckd->ifb[i-1].ifb_idxnxt = -1;
|
|
}
|
|
|
|
/* Set minimum free space size */
|
|
cckd->free_minsize = CCKD_MIN_FREESIZE( cckd->free_count );
|
|
return 0;
|
|
|
|
} /* end function cckd_read_fsp */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write the free space */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_write_fsp (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
off_t fpos; /* Free space offset */
|
|
U32 ppos; /* Previous free space offset*/
|
|
int sfx; /* File index */
|
|
int i, j, n; /* Work variables */
|
|
int rc; /* Return code */
|
|
CCKD_FREEBLK *fsp = NULL; /* -> new format free space */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_write_fsp( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
if (!cckd->ifb)
|
|
return 0;
|
|
|
|
CCKD_TRACE( "file[%d] write_fsp number %d",
|
|
sfx, cckd->cdevhdr[sfx].free_num);
|
|
|
|
/* get rid of pending free space */
|
|
for (i = 0; i < CCKD_MAX_FREEPEND; i++)
|
|
cckd_flush_space(dev);
|
|
|
|
/* sanity checks */
|
|
if (cckd->cdevhdr[sfx].free_num == 0 || cckd->cdevhdr[sfx].free_off == 0)
|
|
{
|
|
cckd->cdevhdr[sfx].free_num = cckd->cdevhdr[sfx].free_off = 0;
|
|
cckd->free_idx1st = cckd->free_idxlast = cckd->free_idxavail = -1;
|
|
}
|
|
|
|
/* Write any free spaces */
|
|
if (cckd->cdevhdr[sfx].free_off)
|
|
{
|
|
/* size needed for new format free space */
|
|
n = (cckd->cdevhdr[sfx].free_num+1) * CCKD_FREEBLK_SIZE;
|
|
|
|
/* look for existing free space to fit new format free space */
|
|
fpos = 0;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
if (n <= (int)cckd->ifb[i].ifb_len)
|
|
break;
|
|
if (i >= 0)
|
|
fpos = cckd->ifb[i].ifb_idxprv < 0
|
|
? (off_t)cckd->cdevhdr[sfx].free_off
|
|
: (off_t)cckd->ifb[cckd->ifb[i].ifb_idxprv].ifb_offnxt;
|
|
|
|
/* if no applicable space see if we can append to the file */
|
|
if (fpos == 0 && (cckd->cckd_maxsize - cckd->cdevhdr[sfx].cdh_size) >= (U64)n)
|
|
fpos = (off_t)cckd->cdevhdr[sfx].cdh_size;
|
|
|
|
if (fpos && (fsp = cckd_malloc (dev, "fsp", n)) == NULL)
|
|
fpos = 0;
|
|
|
|
if (fpos)
|
|
{
|
|
/* New format free space */
|
|
memcpy (&fsp[0], "FREE_BLK", 8);
|
|
ppos = cckd->cdevhdr[sfx].free_off;
|
|
for (i = cckd->free_idx1st, j = 1; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
fsp[j].fb_offnxt = ppos;
|
|
fsp[j++].fb_len = cckd->ifb[i].ifb_len;
|
|
ppos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
rc = cckd_write (dev, sfx, fpos, fsp, n);
|
|
fsp = cckd_free (dev, "fsp", fsp);
|
|
if (rc < 0)
|
|
return -1;
|
|
cckd->cdevhdr[sfx].free_off = (U32)fpos;
|
|
} /* new format free space */
|
|
else
|
|
{
|
|
/* Old format free space */
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (cckd_write (dev, sfx, fpos, &cckd->ifb[i], CCKD_FREEBLK_SIZE) < 0)
|
|
return -1;
|
|
fpos = (off_t)cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
} /* old format free space */
|
|
} /* if (cckd->cdevhdr[sfx].free_off) */
|
|
|
|
/* Free the free space array */
|
|
cckd->ifb = cckd_free (dev, "ifb", cckd->ifb);
|
|
cckd->free_count = 0;
|
|
cckd->free_idx1st = cckd->free_idxlast = cckd->free_idxavail = -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_write_fsp */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read a new level 2 table */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read_l2 (DEVBLK *dev, int sfx, int L1idx)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
off_t off; /* L2 file offset */
|
|
int fnd; /* Found cache */
|
|
int lru; /* Oldest available cache */
|
|
CCKD_L2ENT *buf; /* -> Cache buffer */
|
|
int i; /* Loop index */
|
|
int nullfmt; /* Null track format */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_read_l2( dev, sfx, L1idx );
|
|
|
|
cckd = dev->cckd_ext;
|
|
nullfmt = cckd->cdevhdr[cckd->sfn].cdh_nullfmt;
|
|
|
|
CCKD_TRACE( "file[%d] read_l2 %d active %d %d %d",
|
|
sfx, L1idx, cckd->sfx, cckd->L1idx, cckd->L2_active);
|
|
|
|
/* Return if table is already active */
|
|
if (sfx == cckd->sfx && L1idx == cckd->L1idx) return 0;
|
|
|
|
cache_lock(CACHE_L2);
|
|
|
|
/* Inactivate the previous entry */
|
|
if (cckd->L2_active >= 0)
|
|
cache_setflag(CACHE_L2, cckd->L2_active, ~L2_CACHE_ACTIVE, 0);
|
|
cckd->L2tab = NULL;
|
|
cckd->L2_active = cckd->sfx = cckd->L1idx = -1;
|
|
|
|
/* scan the cache array for the l2tab */
|
|
fnd = cache_lookup (CACHE_L2, L2_CACHE_SETKEY(sfx, dev->devnum, L1idx), &lru);
|
|
|
|
/* check for level 2 cache hit */
|
|
if (fnd >= 0)
|
|
{
|
|
CCKD_TRACE( "l2[%d,%d] cache[%d] hit", sfx, L1idx, fnd);
|
|
cache_setflag (CACHE_L2, fnd, 0, L2_CACHE_ACTIVE);
|
|
cache_setage (CACHE_L2, fnd);
|
|
cckdblk.stats_l2cachehits++;
|
|
cache_unlock (CACHE_L2);
|
|
cckd->sfx = sfx;
|
|
cckd->L1idx = L1idx;
|
|
cckd->L2tab = cache_getbuf(CACHE_L2, fnd, 0);
|
|
cckd->L2_active = fnd;
|
|
return 1;
|
|
}
|
|
|
|
CCKD_TRACE( "l2[%d,%d] cache[%d] miss", sfx, L1idx, lru);
|
|
|
|
/* Steal an entry if all are busy */
|
|
if (lru < 0) lru = cckd_steal_l2();
|
|
|
|
/* Make the entry active */
|
|
cache_setkey (CACHE_L2, lru, L2_CACHE_SETKEY(sfx, dev->devnum, L1idx));
|
|
cache_setflag (CACHE_L2, lru, 0, L2_CACHE_ACTIVE);
|
|
cache_setage (CACHE_L2, lru);
|
|
buf = cache_getbuf(CACHE_L2, lru, CCKD_L2TAB_SIZE);
|
|
cckdblk.stats_l2cachemisses++;
|
|
cache_unlock (CACHE_L2);
|
|
if (buf == NULL) return -1;
|
|
|
|
/* Check for null table */
|
|
if (cckd->L1tab[sfx][L1idx] == 0)
|
|
{
|
|
memset(buf, 0, CCKD_L2TAB_SIZE);
|
|
if (nullfmt)
|
|
for (i = 0; i < 256; i++)
|
|
buf[i].L2_len = buf[i].L2_size = nullfmt;
|
|
CCKD_TRACE( "l2[%d,%d] cache[%d] null fmt[%d]", sfx, L1idx, lru, nullfmt);
|
|
}
|
|
else if (cckd->L1tab[sfx][L1idx] == CCKD_MAXSIZE)
|
|
{
|
|
memset(buf, 0xff, CCKD_L2TAB_SIZE);
|
|
CCKD_TRACE( "l2[%d,%d] cache[%d] null 0xff", sfx, L1idx, lru);
|
|
}
|
|
/* Read the new level 2 table */
|
|
else
|
|
{
|
|
off = (off_t)cckd->L1tab[sfx][L1idx];
|
|
if (cckd_read (dev, sfx, off, buf, CCKD_L2TAB_SIZE) < 0)
|
|
{
|
|
cache_lock(CACHE_L2);
|
|
cache_setflag(CACHE_L2, lru, 0, 0);
|
|
cache_unlock(CACHE_L2);
|
|
return -1;
|
|
}
|
|
|
|
if (cckd->swapend[sfx])
|
|
cckd_swapend_l2 (buf);
|
|
|
|
CCKD_TRACE( "file[%d] cache[%d] l2[%d] read offset 0x%8.8"PRIx32,
|
|
sfx, lru, L1idx, cckd->L1tab[sfx][L1idx]);
|
|
|
|
cckd->L2_reads[sfx]++;
|
|
cckd->totl2reads++;
|
|
cckdblk.stats_l2reads++;
|
|
}
|
|
|
|
cckd->sfx = sfx;
|
|
cckd->L1idx = L1idx;
|
|
cckd->L2tab = buf;
|
|
cckd->L2_active = lru;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_read_l2 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Purge all l2tab cache entries for a given device */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_purge_l2 (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_purge_l2( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "purge_l2%s", "");
|
|
|
|
cache_lock (CACHE_L2);
|
|
cckd->L2_active = cckd->sfx = cckd->L1idx = -1;
|
|
cckd->L2tab = NULL;
|
|
cache_scan (CACHE_L2, cckd_purge_l2_scan, dev);
|
|
cache_unlock (CACHE_L2);
|
|
}
|
|
int cckd_purge_l2_scan (int *answer, int ix, int i, void *data)
|
|
{
|
|
U16 sfx; /* Cached suffix */
|
|
U16 devnum; /* Cached device number */
|
|
U32 L1idx; /* Cached level 1 index */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
|
|
UNREFERENCED(answer);
|
|
L2_CACHE_GETKEY(i, sfx, devnum, L1idx);
|
|
if (dev == NULL || devnum == dev->devnum)
|
|
{
|
|
CCKD_TRACE( "purge l2cache[%d] %4.4X sfx %d ix %d purged",
|
|
i, devnum, sfx, L1idx);
|
|
cache_release(ix, i, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Steal an l2tab cache entry */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_steal_l2 ()
|
|
{
|
|
DEVBLK *dev; /* -> device block */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int i; /* Stolen cache index */
|
|
U16 sfx; /* Cached suffix */
|
|
U16 devnum; /* Cached device number */
|
|
U32 L1idx; /* Cached level 1 index */
|
|
|
|
i = cache_scan (CACHE_L2, cckd_steal_l2_scan, NULL);
|
|
L2_CACHE_GETKEY(i, sfx, devnum, L1idx);
|
|
dev = cckd_find_device_by_devnum(devnum);
|
|
if (dev->cckd64)
|
|
return cckd64_steal_l2();
|
|
cckd = dev->cckd_ext;
|
|
cckd->L2_active = cckd->sfx = cckd->L1idx = -1;
|
|
cckd->L2tab = NULL;
|
|
cache_release(CACHE_L2, i, 0);
|
|
return i;
|
|
}
|
|
int cckd_steal_l2_scan (int *answer, int ix, int i, void *data)
|
|
{
|
|
UNREFERENCED(data);
|
|
if (*answer < 0) *answer = i;
|
|
else if (cache_getage(ix, i) < cache_getage(ix, *answer))
|
|
*answer = i;
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write the current level 2 table */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_write_l2 (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx,L1idx; /* Lookup table indices */
|
|
off_t off, old_off; /* New/old L2 file offsets */
|
|
int size = CCKD_L2TAB_SIZE; /* L2 table size */
|
|
int fix; /* Null format type */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_write_l2( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
L1idx = cckd->L1idx;
|
|
fix = cckd->cdevhdr[sfx].cdh_nullfmt;
|
|
cckd->L2ok = 0;
|
|
|
|
CCKD_TRACE( "file[%d] write_l2 %d", sfx, L1idx);
|
|
|
|
if (sfx < 0 || L1idx < 0) return -1;
|
|
|
|
old_off = (off_t)cckd->L1tab[sfx][L1idx];
|
|
|
|
if (cckd->L1tab[sfx][L1idx] == CCKD_NOSIZE || cckd->L1tab[sfx][L1idx] == CCKD_MAXSIZE)
|
|
cckd->L2_bounds += CCKD_L2TAB_SIZE;
|
|
|
|
/* Write the L2 table if it's not empty */
|
|
if (memcmp( cckd->L2tab, &empty_l2[fix], CCKD_L2TAB_SIZE ))
|
|
{
|
|
if ((off = cckd_get_space( dev, &size, CCKD_L2SPACE )) < 0)
|
|
return -1;
|
|
if (cckd_write( dev, sfx, off, cckd->L2tab, CCKD_L2TAB_SIZE ) < 0)
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
off = 0;
|
|
cckd->L2_bounds -= CCKD_L2TAB_SIZE;
|
|
}
|
|
|
|
/* Free the old L2 space */
|
|
cckd_rel_space( dev, old_off, CCKD_L2TAB_SIZE, CCKD_L2TAB_SIZE );
|
|
|
|
/* Update level 1 table */
|
|
cckd->L1tab[sfx][L1idx] = (U32)off;
|
|
|
|
if (cckd_write_l1ent( dev, L1idx ) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_write_l2 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return a level 2 entry */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read_l2ent (DEVBLK *dev, CCKD_L2ENT *l2, int trk)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx,L1idx,l2x; /* Lookup table indices */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_read_l2ent( dev, (CCKD64_L2ENT*) l2, trk );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
L1idx = trk >> 8;
|
|
l2x = trk & 0xff;
|
|
|
|
if (l2 != NULL) l2->L2_trkoff = l2->L2_len = l2->L2_size = 0;
|
|
|
|
for (sfx = cckd->sfn; sfx >= 0; sfx--)
|
|
{
|
|
CCKD_TRACE( "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%x",
|
|
sfx, L1idx, l2x, trk, cckd->L1tab[sfx][L1idx]);
|
|
|
|
/* Continue if l2 table not in this file */
|
|
if (cckd->L1tab[sfx][L1idx] == CCKD_MAXSIZE)
|
|
continue;
|
|
|
|
/* Read l2 table from this file */
|
|
if (cckd_read_l2 (dev, sfx, L1idx) < 0)
|
|
return -1;
|
|
|
|
/* Exit loop if track is in this file */
|
|
if (cckd->L2tab[l2x].L2_trkoff != CCKD_MAXSIZE)
|
|
break;
|
|
}
|
|
|
|
CCKD_TRACE( "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%x %d %d",
|
|
sfx, L1idx, l2x, trk, sfx >= 0 ? cckd->L2tab[l2x].L2_trkoff : 0,
|
|
sfx >= 0 ? cckd->L2tab[l2x].L2_len : 0,
|
|
sfx >= 0 ? cckd->L2tab[l2x].L2_size : 0);
|
|
|
|
if (l2 != NULL && sfx >= 0)
|
|
{
|
|
l2->L2_trkoff = cckd->L2tab[l2x].L2_trkoff;
|
|
l2->L2_len = cckd->L2tab[l2x].L2_len;
|
|
l2->L2_size = cckd->L2tab[l2x].L2_size;
|
|
}
|
|
|
|
return sfx;
|
|
|
|
} /* end function cckd_read_l2ent */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Update a level 2 entry */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_write_l2ent (DEVBLK *dev, CCKD_L2ENT *l2, int trk)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx,L1idx,l2x; /* Lookup table indices */
|
|
off_t off; /* L2 entry offset */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_write_l2ent( dev, (CCKD64_L2ENT*) l2, trk );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Error return if no available level 2 table */
|
|
if (!cckd->L2tab) return -1;
|
|
|
|
sfx = cckd->sfn;
|
|
L1idx = trk >> 8;
|
|
l2x = trk & 0xff;
|
|
|
|
/* Copy the new entry if passed */
|
|
if (l2) memcpy (&cckd->L2tab[l2x], l2, CCKD_L2ENT_SIZE);
|
|
|
|
CCKD_TRACE( "file[%d] l2[%d,%d] trk[%d] write_l2ent 0x%x %d %d",
|
|
sfx, L1idx, l2x, trk,
|
|
cckd->L2tab[l2x].L2_trkoff, cckd->L2tab[l2x].L2_len, cckd->L2tab[l2x].L2_size);
|
|
|
|
/* If no level 2 table for this file, then write a new one */
|
|
if (cckd->L1tab[sfx][L1idx] == CCKD_NOSIZE || cckd->L1tab[sfx][L1idx] == CCKD_MAXSIZE)
|
|
return cckd_write_l2 (dev);
|
|
|
|
/* Write the level 2 table entry */
|
|
off = (off_t)(cckd->L1tab[sfx][L1idx] + l2x * CCKD_L2ENT_SIZE);
|
|
if (cckd_write (dev, sfx, off, &cckd->L2tab[l2x], CCKD_L2ENT_SIZE) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
} /* end function cckd_write_l2ent */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read a track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_read_trkimg (DEVBLK *dev, BYTE *buf, int trk, BYTE *unitstat)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int sfx; /* File index */
|
|
CCKD_L2ENT l2; /* Level 2 entry */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "trk[%d] read_trkimg", trk);
|
|
|
|
/* Read level 2 entry for the track */
|
|
if ((sfx = cckd_read_l2ent (dev, &l2, trk)) < 0)
|
|
goto cckd_read_trkimg_error;
|
|
|
|
/* Read the track image or build a null track image */
|
|
if (l2.L2_trkoff != 0)
|
|
{
|
|
rc = cckd_read (dev, sfx, (off_t)l2.L2_trkoff, buf, (size_t)l2.L2_len);
|
|
if (rc < 0)
|
|
goto cckd_read_trkimg_error;
|
|
|
|
cckd->reads[sfx]++;
|
|
cckd->totreads++;
|
|
cckdblk.stats_reads++;
|
|
cckdblk.stats_readbytes += rc;
|
|
if (cckd->notnull == 0 && trk > 1) cckd->notnull = 1;
|
|
}
|
|
else
|
|
rc = cckd_null_trk (dev, buf, trk, l2.L2_len);
|
|
|
|
/* Validate the track image */
|
|
if (cckd_cchh (dev, buf, trk) < 0)
|
|
goto cckd_read_trkimg_error;
|
|
|
|
return rc;
|
|
|
|
cckd_read_trkimg_error:
|
|
|
|
if (unitstat)
|
|
{
|
|
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
}
|
|
|
|
return cckd_null_trk (dev, buf, trk, 0);
|
|
|
|
} /* end function cckd_read_trkimg */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write a track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_write_trkimg (DEVBLK *dev, BYTE *buf, int len, int trk, int flags)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
off_t off; /* File offset */
|
|
CCKD_L2ENT l2, oldl2; /* Level 2 entries */
|
|
int sfx,L1idx,l2x; /* Lookup table indices */
|
|
int after = 0; /* 1=New track after old */
|
|
int size; /* Size of new track */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_write_trkimg( dev, buf, len, trk, flags );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
sfx = cckd->sfn;
|
|
L1idx = trk >> 8;
|
|
l2x = trk & 0xff;
|
|
|
|
CCKD_TRACE( "file[%d] trk[%d] write_trkimg len %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
sfx, trk, len, buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
|
|
|
|
/* Validate the new track image */
|
|
if (cckd_cchh (dev, buf, trk) < 0)
|
|
return -1;
|
|
|
|
/* Get the level 2 table for the track in the active file */
|
|
if (cckd_read_l2 (dev, sfx, L1idx) < 0)
|
|
return -1;
|
|
|
|
/* Save the level 2 entry for the track */
|
|
oldl2.L2_trkoff = cckd->L2tab[l2x].L2_trkoff;
|
|
oldl2.L2_len = cckd->L2tab[l2x].L2_len;
|
|
oldl2.L2_size = cckd->L2tab[l2x].L2_size;
|
|
CCKD_TRACE( "file[%d] trk[%d] write_trkimg oldl2 0x%x %d %d",
|
|
sfx, trk, oldl2.L2_trkoff, oldl2.L2_len, oldl2.L2_size);
|
|
|
|
/* Check if writing a null track */
|
|
len = cckd_check_null_trk(dev, buf, trk, len);
|
|
|
|
if (len > CKD_NULLTRK_FMTMAX)
|
|
{
|
|
/* Get space for the track image */
|
|
size = len;
|
|
if ((off = cckd_get_space (dev, &size, flags)) < 0)
|
|
return -1;
|
|
|
|
l2.L2_trkoff = (U32)off;
|
|
l2.L2_len = (U16)len;
|
|
l2.L2_size = (U16)size;
|
|
|
|
if (1
|
|
&& oldl2.L2_trkoff != CCKD_NOSIZE
|
|
&& oldl2.L2_trkoff != CCKD_MAXSIZE
|
|
&& oldl2.L2_trkoff < l2.L2_trkoff
|
|
)
|
|
after = 1;
|
|
|
|
/* Write the track image */
|
|
if ((rc = cckd_write (dev, sfx, off, buf, len)) < 0)
|
|
return -1;
|
|
|
|
cckd->writes[sfx]++;
|
|
cckd->totwrites++;
|
|
cckdblk.stats_writes++;
|
|
cckdblk.stats_writebytes += rc;
|
|
}
|
|
else
|
|
{
|
|
l2.L2_trkoff = 0;
|
|
l2.L2_len = l2.L2_size = (U16)len;
|
|
}
|
|
|
|
/* Update the level 2 entry */
|
|
if (cckd_write_l2ent (dev, &l2, trk) < 0)
|
|
return -1;
|
|
|
|
/* Release the previous space */
|
|
cckd_rel_space (dev, (off_t)oldl2.L2_trkoff, (int)oldl2.L2_len, (int)oldl2.L2_size);
|
|
|
|
/* `after' is 1 if the new offset is after the old offset */
|
|
return after;
|
|
|
|
} /* end function cckd_write_trkimg */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Harden the file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_harden(DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc=0; /* Return code */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_harden( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if ((dev->ckdrdonly && cckd->sfn == 0)
|
|
|| cckd->open[cckd->sfn] != CCKD_OPEN_RW)
|
|
return 0;
|
|
|
|
CCKD_TRACE( "file[%d] harden", cckd->sfn);
|
|
|
|
/* Write the compressed device header */
|
|
if (cckd_write_chdr (dev) < 0)
|
|
rc = -1;
|
|
|
|
/* Write the level 1 table */
|
|
if (cckd_write_l1 (dev) < 0)
|
|
rc = -1;
|
|
|
|
/* Write the free space chain */
|
|
if (cckd_write_fsp (dev) < 0)
|
|
rc = -1;
|
|
|
|
/* Re-write the compressed device header */
|
|
cckd->cdevhdr[cckd->sfn].cdh_opts &= ~CCKD_OPT_OPENED;
|
|
if (cckd_write_chdr (dev) < 0)
|
|
rc = -1;
|
|
|
|
if (cckdblk.fsync)
|
|
{
|
|
fdatasync (cckd->fd[cckd->sfn]);
|
|
cckdblk.stats_fsyncs++;
|
|
}
|
|
cckdblk.stats_hardens++;
|
|
|
|
return rc;
|
|
} /* cckd_harden */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return length of an uncompressed track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_trklen( DEVBLK* dev, BYTE* buf )
|
|
{
|
|
CCKD_EXT* cckd; /* -> cckd extension */
|
|
int size; /* Track size */
|
|
|
|
if ((cckd = dev->cckd_ext)->fbadasd)
|
|
return CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE;
|
|
|
|
size = ckd_tracklen( dev, buf );
|
|
|
|
/* check for missing end-of-track indicator */
|
|
if (0
|
|
|| size > dev->ckdtrksz
|
|
|| memcmp( &buf[ size - CKD_ENDTRK_SIZE ], &CKD_ENDTRK, CKD_ENDTRK_SIZE ) != 0
|
|
)
|
|
{
|
|
CKD_TRKHDR* trkhdr = (CKD_TRKHDR*) buf;
|
|
|
|
// "%1d:%04X CCKD file[%d] %s: trklen error for BCCHH = %2.2x%4.4x%4.4x"
|
|
WRMSG( HHC00306, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ),
|
|
trkhdr->bin, fetch_hw( trkhdr->cyl ), fetch_hw( trkhdr->head ));
|
|
|
|
size = -1;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Build a null track */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_null_trk(DEVBLK *dev, BYTE *buf, int trk, int nullfmt)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int i; /* Loop counter */
|
|
CKD_TRKHDR *trkhdr; /* -> Track header */
|
|
CKD_RECHDR *rechdr; /* -> Record header */
|
|
U32 cyl; /* Cylinder number */
|
|
U32 head; /* Head number */
|
|
BYTE r; /* Record number */
|
|
BYTE *pos; /* -> Next position in buffer*/
|
|
int len; /* Length of null track */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_null_trk( dev, buf, trk, nullfmt );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (nullfmt < 0 || nullfmt > CKD_NULLTRK_FMTMAX)
|
|
nullfmt = cckd->cdevhdr[cckd->sfn].cdh_nullfmt;
|
|
|
|
// FIXME
|
|
// Compatibility check for nullfmt bug and linux -- 18 May 2005
|
|
// Remove at some reasonable date in the future
|
|
else if (nullfmt == 0
|
|
&& cckd->cdevhdr[cckd->sfn].cdh_nullfmt == CKD_NULLTRK_FMT2)
|
|
nullfmt = CKD_NULLTRK_FMT2;
|
|
|
|
if (cckd->ckddasd)
|
|
{
|
|
|
|
/* cylinder and head calculations */
|
|
cyl = trk / dev->ckdheads;
|
|
head = trk % dev->ckdheads;
|
|
|
|
/* Build the track header */
|
|
trkhdr = (CKD_TRKHDR*)buf;
|
|
trkhdr->bin = 0;
|
|
store_hw( &trkhdr->cyl, (U16) cyl );
|
|
store_hw( &trkhdr->head, (U16) head );
|
|
pos = buf + CKD_TRKHDR_SIZE;
|
|
|
|
/* Build record zero (R0): KL=0, DL=8 */
|
|
r = 0;
|
|
rechdr = (CKD_RECHDR*)pos;
|
|
pos += CKD_RECHDR_SIZE;
|
|
store_hw( &rechdr->cyl, (U16) cyl );
|
|
store_hw( &rechdr->head, (U16) head );
|
|
rechdr->rec = r;
|
|
rechdr->klen = 0;
|
|
store_hw( &rechdr->dlen, CKD_R0_DLEN );
|
|
memset( pos, 0, CKD_R0_DLEN );
|
|
pos += CKD_R0_DLEN;
|
|
r++;
|
|
|
|
/* Specific null track formatting */
|
|
if (nullfmt == CKD_NULLTRK_FMT0)
|
|
{
|
|
rechdr = (CKD_RECHDR*)pos;
|
|
pos += CKD_RECHDR_SIZE;
|
|
|
|
/* Build record one (R1): EOF record (KL=0, DL=0) */
|
|
store_hw( &rechdr->cyl, (U16) cyl );
|
|
store_hw( &rechdr->head, (U16) head );
|
|
rechdr->rec = r;
|
|
rechdr->klen = 0;
|
|
store_hw(&rechdr->dlen, 0);
|
|
r++;
|
|
}
|
|
else if (nullfmt == CKD_NULLTRK_FMT2)
|
|
{
|
|
/* Build 12 binary-0 data blocks, 4K each (KL=0, DL=4K) */
|
|
for (i = 0; i < 12; i++)
|
|
{
|
|
rechdr = (CKD_RECHDR*)pos;
|
|
pos += CKD_RECHDR_SIZE;
|
|
|
|
store_hw( &rechdr->cyl, (U16) cyl);
|
|
store_hw( &rechdr->head, (U16) head);
|
|
rechdr->rec = r;
|
|
rechdr->klen = 0;
|
|
store_hw( &rechdr->dlen, CKD_NULL_FMT2_DLEN );
|
|
r++;
|
|
memset( pos, 0, CKD_NULL_FMT2_DLEN );
|
|
pos += CKD_NULL_FMT2_DLEN;
|
|
}
|
|
}
|
|
|
|
/* Build the end of track marker */
|
|
memcpy( pos, &CKD_ENDTRK, CKD_ENDTRK_SIZE );
|
|
pos += CKD_ENDTRK_SIZE;
|
|
len = (int)(pos - buf);
|
|
}
|
|
else
|
|
{
|
|
len = CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE;
|
|
memset( buf, 0, len );
|
|
store_fw( buf+1, trk );
|
|
}
|
|
|
|
CCKD_TRACE( "null_trk %s %d format %d size %d",
|
|
cckd->ckddasd ? "trk" : "blkgrp", trk, nullfmt, len);
|
|
|
|
return len;
|
|
|
|
} /* end function cckd_null_trk */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return a number 0 .. CKD_NULLTRK_FMTMAX if track is null */
|
|
/* else return the original length */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_check_null_trk (DEVBLK *dev, BYTE *buf, int trk, int len)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
BYTE buf2[65536]; /* Null track buffer */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_check_null_trk( dev, buf, trk, len );
|
|
|
|
cckd = dev->cckd_ext;
|
|
rc = len;
|
|
|
|
if (len == CKD_NULLTRK_SIZE0)
|
|
rc = CKD_NULLTRK_FMT0;
|
|
else if (len == CKD_NULLTRK_SIZE1)
|
|
rc = CKD_NULLTRK_FMT1;
|
|
else if (len == CKD_NULLTRK_SIZE2 && dev->oslinux
|
|
&& (!cckd->notnull || cckdblk.linuxnull))
|
|
{
|
|
cckd_null_trk (dev, buf2, trk, 0);
|
|
if (memcmp(buf, buf2, len) == 0)
|
|
rc = CKD_NULLTRK_FMT2;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Verify a track/block header and return track/block number */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_cchh( DEVBLK* dev, BYTE* buf, int trk )
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
U16 cyl; /* Cylinder */
|
|
U16 head; /* Head */
|
|
int t; /* Calculated track */
|
|
BYTE badcomp=0; /* 1=Unsupported compression */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* CKD dasd header verification */
|
|
if (cckd->ckddasd)
|
|
{
|
|
cyl = fetch_hw( buf + 1 );
|
|
head = fetch_hw( buf + 3 );
|
|
t = cyl * dev->ckdheads + head;
|
|
|
|
if (1
|
|
&& cyl < dev->ckdcyls
|
|
&& head < dev->ckdheads
|
|
&& (trk == -1 || t == trk)
|
|
)
|
|
{
|
|
if (buf[0] & ~cckdblk.comps)
|
|
{
|
|
if (buf[0] & ~CCKD_COMPRESS_MASK)
|
|
{
|
|
if (cckdblk.bytemsgs++ < 10)
|
|
// "%1d:%04X CCKD file[%d] %s: invalid byte 0 trk %d, buf %2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
WRMSG( HHC00307, "E", LCSS_DEVNUM, cckd->sfn,
|
|
cckd_sf_name( dev, cckd->sfn ), t, buf[0],buf[1],buf[2],buf[3],buf[4]);
|
|
buf[0] &= CCKD_COMPRESS_MASK;
|
|
}
|
|
}
|
|
if (buf[0] & ~cckdblk.comps)
|
|
badcomp = 1;
|
|
else
|
|
return t;
|
|
}
|
|
}
|
|
/* FBA dasd header verification */
|
|
else
|
|
{
|
|
t = fetch_fw (buf + 1);
|
|
if (t < dev->fbanumblk && (trk == -1 || t == trk))
|
|
{
|
|
if (buf[0] & ~cckdblk.comps)
|
|
{
|
|
if (buf[0] & ~CCKD_COMPRESS_MASK)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: invalid byte 0 blkgrp %d, buf %2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
WRMSG( HHC00308, "E", LCSS_DEVNUM, cckd->sfn,
|
|
cckd_sf_name( dev, cckd->sfn ), t, buf[0],buf[1],buf[2],buf[3],buf[4]);
|
|
buf[0] &= CCKD_COMPRESS_MASK;
|
|
}
|
|
}
|
|
if (buf[0] & ~cckdblk.comps)
|
|
badcomp = 1;
|
|
else
|
|
return t;
|
|
}
|
|
}
|
|
|
|
if (badcomp)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: invalid %s hdr %s %d: %s compression unsupported"
|
|
WRMSG( HHC00309, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ),
|
|
cckd->ckddasd ? "trk" : "blk",
|
|
cckd->ckddasd ? "trk" : "blk", t, compname[buf[0]]);
|
|
}
|
|
else
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: invalid %s hdr %s %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
WRMSG( HHC00310, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ),
|
|
cckd->ckddasd ? "trk" : "blk",
|
|
cckd->ckddasd ? "trk" : "blk", trk,
|
|
buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
|
|
cckd_print_itrace();
|
|
}
|
|
|
|
return -1;
|
|
} /* end function cckd_cchh */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Validate a track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_validate (DEVBLK *dev, BYTE *buf, int trk, int len)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int r; /* Record number */
|
|
int sz; /* Track size */
|
|
int vlen; /* Validation length */
|
|
int kl, dl; /* Key/Data lengths */
|
|
CKD_RECHDR rn; /* Record-n (r0, r1 ... rn) */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (!buf || len < 0)
|
|
return -1;
|
|
|
|
CCKD_TRACE( "validating %s %d len %d %2.2x%2.2x%2.2x%2.2x%2.2x "
|
|
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
cckd->ckddasd ? "trk" : "blkgrp", trk, len,
|
|
buf[0], buf[ 1], buf[ 2], buf[ 3], buf[4], // trkhdr
|
|
buf[5], buf[ 6], buf[ 7], buf[ 8], // r0 (0-3)
|
|
buf[9], buf[10], buf[11], buf[12] ); // r0 (4-7)
|
|
|
|
/* FBA dasd check */
|
|
if (cckd->fbadasd)
|
|
{
|
|
if (!len || len == CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE)
|
|
return len;
|
|
CCKD_TRACE( "validation failed: bad length%s", "" );
|
|
return -1;
|
|
}
|
|
|
|
/* validate record 0 */
|
|
|
|
memcpy( &rn, &buf[CKD_TRKHDR_SIZE], CKD_R0_SIZE );
|
|
rn.cyl[0] &= 0x7f; /* fix for ovflow */
|
|
|
|
if (0
|
|
|| rn.rec != 0
|
|
|| rn.klen != 0
|
|
|| fetch_hw( rn.dlen ) != CKD_R0_DLEN
|
|
)
|
|
{
|
|
CCKD_TRACE( "validation failed: bad r0%s", "" );
|
|
return -1;
|
|
}
|
|
|
|
/* validate records 1 thru n */
|
|
|
|
vlen = len > 0 ? len : dev->ckdtrksz;
|
|
|
|
for (r = 1, sz = CKD_TRKHDR_SIZE + (CKD_R0_SIZE + CKD_R0_DLEN);
|
|
sz + CKD_ENDTRK_SIZE <= vlen;
|
|
sz += CKD_RECHDR_SIZE + kl + dl, r++)
|
|
{
|
|
memcpy( &rn, &buf[sz], CKD_RECHDR_SIZE );
|
|
|
|
if (memcmp( &rn, &CKD_ENDTRK, CKD_ENDTRK_SIZE ) == 0)
|
|
break;
|
|
|
|
kl = rn.klen;
|
|
dl = fetch_hw( rn.dlen );
|
|
|
|
if (rn.rec == 0 || sz + CKD_RECHDR_SIZE + kl + dl >= vlen)
|
|
{
|
|
CCKD_TRACE( "validation failed: bad r%d "
|
|
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
r, buf[sz+0], buf[sz+1], buf[sz+2], buf[sz+3],
|
|
buf[sz+4], buf[sz+5], buf[sz+6], buf[sz+7] );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
sz += CKD_ENDTRK_SIZE;
|
|
|
|
if ((len > 0 && sz != len) || sz > vlen)
|
|
{
|
|
CCKD_TRACE( "validation failed: no eot%s", "" );
|
|
return -1;
|
|
}
|
|
|
|
return sz;
|
|
|
|
} /* end function cckd_validate */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return shadow file name */
|
|
/*-------------------------------------------------------------------*/
|
|
char *cckd_sf_name (DEVBLK *dev, int sfx)
|
|
{
|
|
/* Return base file name if index is 0 */
|
|
if (sfx == 0)
|
|
return dev->filename;
|
|
|
|
/* Error if no shadow file name specified or number exceeded */
|
|
if (dev->dasdsfn == NULL || sfx > CCKD_MAX_SF)
|
|
return NULL;
|
|
|
|
/* Set the suffix character in the shadow file name */
|
|
if (sfx > 0)
|
|
*dev->dasdsfx = (char)('0' + sfx);
|
|
else
|
|
*dev->dasdsfx = '*';
|
|
|
|
return dev->dasdsfn;
|
|
|
|
} /* end function cckd_sf_name */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Parse the shadow file name option */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_sf_parse_sfn( DEVBLK* dev, char* sfn )
|
|
{
|
|
char pathname[MAX_PATH];
|
|
if ('\"' == sfn[0]) sfn++;
|
|
hostpath(pathname, sfn, sizeof(pathname));
|
|
|
|
if (dev->dasdsfn) free (dev->dasdsfn);
|
|
dev->dasdsfn = strdup(pathname);
|
|
if (dev->dasdsfn)
|
|
{
|
|
/*
|
|
** Set the pointer to the suffix character
|
|
**
|
|
** The shadow file name should have spot where the shadow file
|
|
** number will be set. This is either the character preceding
|
|
** the last period after the last slash (i.e. the last character
|
|
** of the filename, immediately before the file extension), or
|
|
** the very last character of the filename itself if the file
|
|
** extension was not specified.
|
|
*/
|
|
char* fn;
|
|
if ((fn = strrchr( dev->dasdsfn, PATHSEPC )) != NULL)
|
|
fn++;
|
|
else
|
|
fn = dev->dasdsfn;
|
|
|
|
dev->dasdsfx = strrchr( fn, '.' ); /* Find last period */
|
|
|
|
if (dev->dasdsfx == NULL)
|
|
dev->dasdsfx = dev->dasdsfn + strlen(dev->dasdsfn);
|
|
|
|
dev->dasdsfx--; /* Shadow file number will go here */
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize shadow files */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_sf_init (DEVBLK *dev)
|
|
{
|
|
CKD_DEVHDR devhdr; /* Device header */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int i; /* Index */
|
|
struct stat st; /* stat() buffer */
|
|
char pathname[MAX_PATH]; /* file path in host format */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_sf_init( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* return if no shadow files */
|
|
if (dev->dasdsfn == NULL) return 0;
|
|
|
|
/* Check for shadow file name collision */
|
|
for (i = 1; i <= CCKD_MAX_SF && dev->dasdsfn != NULL; i++)
|
|
{
|
|
DEVBLK *dev2;
|
|
CCKD_EXT *cckd2;
|
|
int j;
|
|
|
|
for (dev2 = cckdblk.dev1st; dev2; dev2 = cckd2->devnext)
|
|
{
|
|
cckd2 = dev2->cckd_ext;
|
|
if (dev2 == dev) continue;
|
|
for (j = 0; j <= CCKD_MAX_SF && dev2->dasdsfn != NULL; j++)
|
|
{
|
|
if (strcmp (cckd_sf_name(dev, i),cckd_sf_name(dev2, j)) == 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file name collides with %1d:%04X file[%d] %s"
|
|
WRMSG (HHC00311, "E", LCSS_DEVNUM, i, cckd_sf_name(dev, i),
|
|
SSID_TO_LCSS(dev2->ssid), dev2->devnum, j, cckd_sf_name(dev2, j));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* open all existing shadow files */
|
|
for (cckd->sfn = 1; cckd->sfn <= CCKD_MAX_SF; cckd->sfn++)
|
|
{
|
|
/* If no more shadow files remaining then we're done */
|
|
hostpath( pathname, cckd_sf_name( dev, cckd->sfn ), sizeof( pathname ));
|
|
if (stat( pathname, &st ) < 0)
|
|
break;
|
|
|
|
/* Try to open the shadow file read-write then read-only */
|
|
if ( cckd_open( dev, cckd->sfn, O_RDWR | O_BINARY, 1 ) < 0)
|
|
if (cckd_open( dev, cckd->sfn, O_RDONLY | O_BINARY, 0 ) < 0)
|
|
break;
|
|
|
|
/* Make sure shadow file type matches base image type */
|
|
if (cckd_read( dev, cckd->sfn, 0, &devhdr, CKD_DEVHDR_SIZE ) < 0)
|
|
return -1;
|
|
if (!is_dh_devid_typ( devhdr.dh_devid, ANY32_SF_TYP ))
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: cckd/64 format differs from base"
|
|
WRMSG( HHC00351, "E", LCSS_DEVNUM, cckd->sfn, pathname );
|
|
return -1;
|
|
}
|
|
|
|
/* Call the chkdsk function */
|
|
if ((rc = cckd_chkdsk( dev, 0 )) < 0)
|
|
return -1;
|
|
|
|
/* Perform initial read */
|
|
rc = cckd_read_init( dev );
|
|
}
|
|
|
|
/* Backup to the last opened file number */
|
|
cckd->sfn--;
|
|
|
|
/* If the last file was opened read-only then create a new one */
|
|
if (cckd->open[cckd->sfn] == CCKD_OPEN_RO)
|
|
{
|
|
/* but ONLY IF not explicit batch utility READ-ONLY open */
|
|
if (!(1
|
|
&& dev->batch
|
|
&& dev->ckdrdonly
|
|
))
|
|
{
|
|
/* NOT explicit batch utility read-only open: create new shadow file */
|
|
if (cckd_sf_new(dev) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Re-open previous rdwr files rdonly */
|
|
for (i = 0; i < cckd->sfn; i++)
|
|
{
|
|
if (cckd->open[i] == CCKD_OPEN_RO) continue;
|
|
if (cckd_open (dev, i, O_RDONLY|O_BINARY, 0) < 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: error re-opening readonly: %s"
|
|
WRMSG (HHC00312, "E", LCSS_DEVNUM, i, cckd_sf_name(dev, i), strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd_sf_init */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Create a new shadow file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_sf_new (DEVBLK *dev)
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int l1size; /* Size of level 1 table */
|
|
CKD_DEVHDR devhdr; /* Device header */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_sf_new( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] sf_new %s", cckd->sfn+1,
|
|
cckd_sf_name(dev, cckd->sfn+1) ?
|
|
(char *)cckd_sf_name(dev, cckd->sfn+1) : "(none)");
|
|
|
|
/* Error if no shadow file name */
|
|
if (dev->dasdsfn == NULL)
|
|
{
|
|
WRMSG (HHC00313, "E", LCSS_DEVNUM, cckd->sfn+1);
|
|
return -1;
|
|
}
|
|
|
|
/* Error if max number of shadow files exceeded */
|
|
if (cckd->sfn+1 == CCKD_MAX_SF)
|
|
{
|
|
WRMSG (HHC00314, "E", LCSS_DEVNUM, cckd->sfn+1, dev->dasdsfn);
|
|
return -1;
|
|
}
|
|
|
|
/* Harden the current file */
|
|
cckd_harden (dev);
|
|
|
|
/* Open the new shadow file */
|
|
if (cckd_open(dev, cckd->sfn+1, O_RDWR|O_CREAT|O_EXCL|O_BINARY,
|
|
S_IRUSR | S_IWUSR | S_IRGRP) < 0)
|
|
return -1;
|
|
|
|
/* Read previous file's device header */
|
|
if (cckd_read (dev, cckd->sfn, 0, &devhdr, CKD_DEVHDR_SIZE) < 0)
|
|
goto sf_new_error;
|
|
|
|
/* Make sure identifier is CKD_S370 or FBA_S370 */
|
|
devhdr.dh_devid[4] = 'S';
|
|
|
|
/* Write new file's device header */
|
|
if (cckd_write (dev, cckd->sfn+1, 0, &devhdr, CKD_DEVHDR_SIZE) < 0)
|
|
goto sf_new_error;
|
|
|
|
/* Build the compressed device header */
|
|
memcpy (&cckd->cdevhdr[cckd->sfn+1], &cckd->cdevhdr[cckd->sfn], CCKD_DEVHDR_SIZE);
|
|
l1size = cckd->cdevhdr[cckd->sfn+1].num_L1tab * CCKD_L1ENT_SIZE;
|
|
cckd->cdevhdr[cckd->sfn+1].cdh_size =
|
|
cckd->cdevhdr[cckd->sfn+1].cdh_used = CKD_DEVHDR_SIZE + CCKD_DEVHDR_SIZE + l1size;
|
|
cckd->cdevhdr[cckd->sfn+1].free_off =
|
|
cckd->cdevhdr[cckd->sfn+1].free_total =
|
|
cckd->cdevhdr[cckd->sfn+1].free_largest =
|
|
cckd->cdevhdr[cckd->sfn+1].free_num =
|
|
cckd->cdevhdr[cckd->sfn+1].free_imbed = 0;
|
|
|
|
/* Init the level 1 table */
|
|
if ((cckd->L1tab[cckd->sfn+1] = cckd_malloc (dev, "l1", l1size)) == NULL)
|
|
goto sf_new_error;
|
|
memset (cckd->L1tab[cckd->sfn+1], 0xff, l1size);
|
|
|
|
/* Make the new file active */
|
|
cckd->sfn++;
|
|
|
|
/* Harden the new file */
|
|
if (cckd_harden (dev) < 0)
|
|
{
|
|
cckd->sfn--;
|
|
goto sf_new_error;
|
|
}
|
|
|
|
/* Re-read the l1 to set L2_bounds, L2ok */
|
|
cckd_read_l1 (dev);
|
|
|
|
return 0;
|
|
|
|
sf_new_error:
|
|
cckd->L1tab[cckd->sfn+1] = cckd_free(dev, "l1", cckd->L1tab[cckd->sfn+1]);
|
|
cckd_close (dev, cckd->sfn+1);
|
|
cckd->open[cckd->sfn+1] = CCKD_OPEN_NONE;
|
|
unlink (cckd_sf_name (dev, cckd->sfn+1));
|
|
|
|
/* Re-read the l1 to set L2_bounds, L2ok */
|
|
cckd_read_l1 (dev);
|
|
|
|
return -1;
|
|
|
|
} /* end function cckd_sf_new */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Add a shadow file (sf+) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* cckd_sf_add( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
|
|
if (dev == NULL)
|
|
{
|
|
int n = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
if (dev->cckd_ext)
|
|
{
|
|
WRMSG (HHC00315, "I", LCSS_DEVNUM );
|
|
cckd_sf_add (dev);
|
|
n++;
|
|
}
|
|
WRMSG(HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
if (!cckd)
|
|
{
|
|
WRMSG (HHC00317, "E", LCSS_DEVNUM);
|
|
return NULL;
|
|
}
|
|
|
|
/* Schedule updated track entries to be written */
|
|
obtain_lock (&cckd->cckdiolock);
|
|
if (cckd->merging)
|
|
{
|
|
release_lock (&cckd->cckdiolock);
|
|
WRMSG (HHC00318, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name (dev, cckd->sfn));
|
|
return NULL;
|
|
}
|
|
cckd->merging = 1;
|
|
cckd_flush_cache (dev);
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
|
|
cckd->cckdwaiters--;
|
|
cckd_flush_cache (dev);
|
|
}
|
|
cckd_purge_cache (dev); cckd_purge_l2 (dev);
|
|
dev->bufcur = dev->cache = -1;
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
/* Obtain control of the file */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* Harden the current file */
|
|
cckd_harden (dev);
|
|
|
|
/* Create a new shadow file */
|
|
if (cckd_sf_new (dev) < 0)
|
|
{
|
|
WRMSG (HHC00319, "E", LCSS_DEVNUM, cckd->sfn+1,
|
|
cckd_sf_name(dev, cckd->sfn+1)?cckd_sf_name(dev, cckd->sfn+1):"(null)");
|
|
goto cckd_sf_add_exit;
|
|
}
|
|
|
|
/* Re-open the previous file if opened read-write */
|
|
if (cckd->open[cckd->sfn-1] == CCKD_OPEN_RW)
|
|
cckd_open (dev, cckd->sfn-1, O_RDONLY|O_BINARY, 0);
|
|
|
|
WRMSG (HHC00320, "I", LCSS_DEVNUM, cckd->sfn, cckd_sf_name (dev, cckd->sfn));
|
|
|
|
cckd_sf_add_exit:
|
|
|
|
/* Re-read the l1 to set L2_bounds, L2ok */
|
|
cckd_read_l1 (dev);
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock (&cckd->cckdiolock);
|
|
cckd->merging = 0;
|
|
if (cckd->cckdwaiters)
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
cckd_sf_stats (dev);
|
|
return NULL;
|
|
} /* end function cckd_sf_add */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Remove a shadow file (sf-) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* cckd_sf_remove( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int from_sfx, to_sfx; /* From/to file index */
|
|
int fix; /* nullfmt index */
|
|
int add = 0; /* 1=Add shadow file back */
|
|
int l2updated = 0; /* 1=L2 table was updated */
|
|
int i,j; /* Loop indexes */
|
|
int merge, force; /* Flags */
|
|
off_t pos; /* File offset */
|
|
unsigned int len; /* Length to read/write */
|
|
int size; /* Image size */
|
|
int trk = -1; /* Track being read/written */
|
|
CCKD_L2ENT from_l2[256], /* Level 2 tables */
|
|
to_l2[256];
|
|
CCKD_L2ENT new_l2; /* New level 2 table entry */
|
|
BYTE buf[64*1024]; /* Buffer */
|
|
|
|
/* NULL DEVBLK == all devices? */
|
|
if (!dev)
|
|
{
|
|
int n = 0;
|
|
merge = cckdblk.sfmerge;
|
|
force = cckdblk.sfforce;
|
|
cckdblk.sfmerge = cckdblk.sfforce = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
{
|
|
if ((cckd = dev->cckd_ext)) /* Compressed device? */
|
|
{
|
|
if (cckd->sfn) /* Any active shadow file(s)? */
|
|
{
|
|
// "%1d:%04X CCKD file: merging shadow files..."
|
|
WRMSG( HHC00321, "I", LCSS_DEVNUM );
|
|
cckd->sfmerge = merge;
|
|
cckd->sfforce = force;
|
|
cckd_sf_remove( dev );
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
// "CCKD file number of devices processed: %d"
|
|
WRMSG( HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
/* Specific device... */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_sf_remove( data );
|
|
|
|
if (!(cckd = dev->cckd_ext))
|
|
{
|
|
// "%1d:%04X CCKD file: device is not a cckd device"
|
|
WRMSG( HHC00317, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
if (!cckd->sfn)
|
|
{
|
|
// "%1d:%04X CCKD file: device has no shadow files"
|
|
WRMSG( HHC00390, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
/* Set flags */
|
|
merge = cckd->sfmerge || cckd->sfforce;
|
|
force = cckd->sfforce;
|
|
cckd->sfmerge = cckd->sfforce = 0;
|
|
|
|
CCKD_TRACE( "merge starting: %s %s",
|
|
merge ? "merge" : "nomerge", force ? "force" : "");
|
|
|
|
/* Schedule updated track entries to be written */
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
if (cckd->merging)
|
|
{
|
|
release_lock( &cckd->cckdiolock );
|
|
// "%1d:%04X CCKD file[%d] %s: error merging shadow file, sf command busy on device"
|
|
WRMSG( HHC00322, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn) );
|
|
return NULL;
|
|
}
|
|
cckd->merging = 1;
|
|
cckd_flush_cache (dev);
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
wait_condition( &cckd->cckdiocond, &cckd->cckdiolock );
|
|
cckd->cckdwaiters--;
|
|
cckd_flush_cache( dev );
|
|
}
|
|
cckd_purge_cache( dev );
|
|
cckd_purge_l2( dev );
|
|
dev->bufcur = dev->cache = -1;
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
obtain_lock( &cckd->filelock );
|
|
|
|
if (!cckd->sfn)
|
|
{
|
|
release_lock( &cckd->filelock );
|
|
// "%1d:%04X CCKD file[%d] %s: cannot remove base file"
|
|
WRMSG( HHC00323, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn) );
|
|
cckd->merging = 0;
|
|
return NULL;
|
|
}
|
|
|
|
from_sfx = cckd->sfn;
|
|
to_sfx = cckd->sfn - 1;
|
|
fix = cckd->cdevhdr[ to_sfx ].cdh_nullfmt;
|
|
|
|
/* Harden the `from' file */
|
|
if (cckd_harden( dev ) < 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged: file[%d] %s%s"
|
|
WRMSG( HHC00324, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, cckd->sfx ),
|
|
from_sfx, "not hardened", "" );
|
|
goto sf_remove_exit;
|
|
}
|
|
|
|
/* Attempt to re-open the `to' file read-write */
|
|
cckd_close( dev, to_sfx );
|
|
|
|
if (to_sfx > 0 || !dev->ckdrdonly || force)
|
|
cckd_open( dev, to_sfx, O_RDWR | O_BINARY, 1 );
|
|
|
|
if (cckd->fd[to_sfx] < 0)
|
|
{
|
|
/* `from' file can't be opened read-write */
|
|
cckd_open( dev, to_sfx, O_RDONLY | O_BINARY, 0 );
|
|
|
|
if (merge)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged: file[%d] %s%s"
|
|
WRMSG( HHC00324, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ),
|
|
to_sfx, "cannot be opened read-write",
|
|
to_sfx == 0 && dev->ckdrdonly && !force ? ", try 'force'" : "" );
|
|
goto sf_remove_exit;
|
|
}
|
|
else
|
|
add = 1;
|
|
}
|
|
else
|
|
{
|
|
/* `from' file opened read-write */
|
|
cckd->sfn = to_sfx;
|
|
|
|
if (cckd_chkdsk( dev, 0 ) < 0)
|
|
{
|
|
cckd->sfn = from_sfx;
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged: file[%d] %s%s"
|
|
WRMSG( HHC00324, "E", LCSS_DEVNUM, to_sfx, cckd_sf_name( dev, to_sfx ),
|
|
to_sfx, "check failed", "" );
|
|
goto sf_remove_exit;
|
|
}
|
|
}
|
|
|
|
cckd->sfn = to_sfx;
|
|
|
|
/* Perform backwards merge */
|
|
if (merge)
|
|
{
|
|
CCKD_TRACE( "merging to file[%d]", to_sfx );
|
|
|
|
/* Make the target file the active file */
|
|
cckd->sfn = to_sfx;
|
|
cckd->cdevhdr[to_sfx].cdh_opts |= (CCKD_OPT_OPENED | CCKD_OPT_OPENRW);
|
|
|
|
/* Loop for each level 1 table entry */
|
|
for (i = 0; i < cckd->cdevhdr[from_sfx].num_L1tab; i++)
|
|
{
|
|
l2updated = 0;
|
|
/* Continue if from L2 doesn't exist */
|
|
if (cckd->L1tab[from_sfx][i] == CCKD_MAXSIZE
|
|
|| (cckd->L1tab[from_sfx][i] == 0 && cckd->L1tab[to_sfx][i] == 0))
|
|
continue;
|
|
|
|
trk = i*256;
|
|
|
|
/* Read `from' l2 table */
|
|
if (cckd->L1tab[from_sfx][i] == 0)
|
|
memset( &from_l2, 0, CCKD_L2TAB_SIZE );
|
|
else
|
|
{
|
|
pos = (off_t)cckd->L1tab[from_sfx][i];
|
|
if (cckd_read( dev, from_sfx, pos, &from_l2, CCKD_L2TAB_SIZE ) < 0)
|
|
goto sf_merge_error;
|
|
}
|
|
|
|
/* Read `to' l2 table */
|
|
if (cckd->L1tab[to_sfx][i] == 0)
|
|
memset( &to_l2, 0, CCKD_L2TAB_SIZE );
|
|
else if (cckd->L1tab[to_sfx][i] == CCKD_MAXSIZE)
|
|
memset (&to_l2, 0xff, CCKD_L2TAB_SIZE);
|
|
else
|
|
{
|
|
pos = (off_t)cckd->L1tab[to_sfx][i];
|
|
if (cckd_read( dev, to_sfx, pos, &to_l2, CCKD_L2TAB_SIZE ) < 0)
|
|
goto sf_merge_error;
|
|
}
|
|
|
|
/* Loop for each level 2 table entry */
|
|
for (j = 0; j < 256; j++)
|
|
{
|
|
trk = i*256 + j;
|
|
/* Continue if from L2 entry doesn't exist */
|
|
if (from_l2[j].L2_trkoff == CCKD_MAXSIZE
|
|
|| (from_l2[j].L2_trkoff == 0 && to_l2[j].L2_trkoff == 0))
|
|
continue;
|
|
|
|
/* Read the `from' track/blkgrp image */
|
|
len = (int)from_l2[j].L2_len;
|
|
if (len > CKD_NULLTRK_FMTMAX)
|
|
{
|
|
pos = (off_t)from_l2[j].L2_trkoff;
|
|
if (cckd_read( dev, from_sfx, pos, buf, len ) < 0)
|
|
goto sf_merge_error;
|
|
|
|
/* Get space for the `to' track/blkgrp image */
|
|
size = len;
|
|
if ((pos = cckd_get_space (dev, &size, CCKD_SIZE_EXACT)) < 0)
|
|
goto sf_merge_error;
|
|
|
|
new_l2.L2_trkoff = (U32)pos;
|
|
new_l2.L2_len = (U16)len;
|
|
new_l2.L2_size = (U16)size;
|
|
|
|
/* Write the `to' track/blkgrp image */
|
|
if (cckd_write( dev, to_sfx, pos, buf, len ) < 0)
|
|
goto sf_merge_error;
|
|
}
|
|
else
|
|
{
|
|
new_l2.L2_trkoff = 0;
|
|
new_l2.L2_len = new_l2.L2_size = (U16)len;
|
|
}
|
|
|
|
/* Release space occupied by old `to' entry */
|
|
cckd_rel_space( dev, (off_t)to_l2[j].L2_trkoff, (int)to_l2[j].L2_len,
|
|
(int)to_l2[j].L2_size );
|
|
|
|
/* Update `to' l2 table entry */
|
|
l2updated = 1;
|
|
to_l2[j].L2_trkoff = new_l2.L2_trkoff;
|
|
to_l2[j].L2_len = new_l2.L2_len;
|
|
to_l2[j].L2_size = new_l2.L2_size;
|
|
} /* for each level 2 table entry */
|
|
|
|
/* Update the `to' level 2 table */
|
|
if (l2updated)
|
|
{
|
|
l2updated = 0;
|
|
pos = (off_t)cckd->L1tab[to_sfx][i];
|
|
if (memcmp( &to_l2, &empty_l2[fix], CCKD_L2TAB_SIZE ) == 0)
|
|
{
|
|
cckd_rel_space( dev, pos, CCKD_L2TAB_SIZE, CCKD_L2TAB_SIZE );
|
|
pos = 0;
|
|
}
|
|
else
|
|
{
|
|
size = CCKD_L2TAB_SIZE;
|
|
if (pos == CCKD_NOSIZE || pos == CCKD_MAXSIZE)
|
|
if ((pos = cckd_get_space( dev, &size, CCKD_L2SPACE )) < 0)
|
|
goto sf_merge_error;
|
|
if (cckd_write( dev, to_sfx, pos, &to_l2, CCKD_L2TAB_SIZE ) < 0)
|
|
goto sf_merge_error;
|
|
} /* `to' level 2 table not null */
|
|
|
|
/* Update the level 1 table index */
|
|
cckd->L1tab[to_sfx][i] = (U32)pos;
|
|
|
|
/* Flush free space */
|
|
cckd_flush_space( dev );
|
|
|
|
} /* Update level 2 table */
|
|
|
|
} /* For each level 1 table entry */
|
|
|
|
/* Validate the merge */
|
|
cckd_harden( dev );
|
|
cckd_chkdsk( dev, 0 );
|
|
cckd_read_init( dev );
|
|
|
|
} /* if merge */
|
|
|
|
/* Remove the old file */
|
|
cckd_close( dev, from_sfx );
|
|
cckd->L1tab[from_sfx] = cckd_free( dev, "l1", cckd->L1tab[ from_sfx ]);
|
|
memset( &cckd->cdevhdr[from_sfx], 0, CCKD_DEVHDR_SIZE );
|
|
rc = unlink (cckd_sf_name (dev, from_sfx));
|
|
|
|
/* adjust the stats */
|
|
cckd->reads [to_sfx] += cckd->reads [from_sfx];
|
|
cckd->writes [to_sfx] += cckd->writes [from_sfx];
|
|
cckd->L2_reads[to_sfx] += cckd->L2_reads[from_sfx];
|
|
|
|
cckd->reads [from_sfx] = 0;
|
|
cckd->writes [from_sfx] = 0;
|
|
cckd->L2_reads[from_sfx] = 0;
|
|
|
|
/* Add the file back if necessary */
|
|
if (add) rc = cckd_sf_new( dev ) ;
|
|
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file successfully %s"
|
|
WRMSG( HHC00325, "I", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ),
|
|
merge ? "merged" : add ? "re-added" : "removed" );
|
|
|
|
sf_remove_exit:
|
|
|
|
/* Re-read the l1 to set L2_bounds, L2ok */
|
|
cckd_read_l1( dev );
|
|
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cckd_purge_cache( dev );
|
|
cckd_purge_l2( dev );
|
|
dev->bufcur = dev->cache = -1;
|
|
cckd->merging = 0;
|
|
if (cckd->cckdwaiters)
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
CCKD_TRACE( "merge complete%s", "" );
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
cckd_sf_stats( dev );
|
|
return NULL;
|
|
|
|
sf_merge_error:
|
|
|
|
if (trk < 0)
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged, error during merge"
|
|
WRMSG( HHC00326, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ));
|
|
else
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged, error processing trk(%d)"
|
|
WRMSG( HHC00327, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ), trk );
|
|
|
|
if (l2updated && cckd->L1tab[ to_sfx ][i] && cckd->L1tab[ to_sfx ][i] != CCKD_SHADOW_NO_OFFSET)
|
|
{
|
|
l2updated = 0;
|
|
pos = (off_t)cckd->L1tab[to_sfx][i];
|
|
cckd_write( dev, to_sfx, pos, &to_l2, CCKD_L2TAB_SIZE );
|
|
}
|
|
cckd_harden( dev );
|
|
cckd_chkdsk( dev, 2 );
|
|
cckd->sfn = from_sfx;
|
|
cckd_harden( dev );
|
|
cckd_chkdsk( dev, 2 );
|
|
goto sf_remove_exit;
|
|
|
|
} /* end function cckd_sf_remove */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check and compress a shadow file (sfc) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* cckd_sf_comp( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
|
|
/* NULL DEVBLK == all devices? */
|
|
if (!dev)
|
|
{
|
|
int n = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
{
|
|
if (dev->cckd_ext) /* Is this a compressed device? */
|
|
{
|
|
// "%1d:%04X CCKD file: compressing shadow files..."
|
|
WRMSG( HHC00328, "I", LCSS_DEVNUM );
|
|
cckd_sf_comp( dev );
|
|
n++;
|
|
}
|
|
}
|
|
// "CCKD file number of devices processed: %d"
|
|
WRMSG ( HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_sf_comp( data );
|
|
|
|
if (!(cckd = dev->cckd_ext))
|
|
{
|
|
// "%1d:%04X CCKD file: device is not a cckd device"
|
|
WRMSG( HHC00317, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
/* schedule updated track entries to be written */
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
if (cckd->merging)
|
|
{
|
|
release_lock( &cckd->cckdiolock );
|
|
// "%1d:%04X CCKD file[%d] %s: error compressing shadow file, sf command busy on device"
|
|
WRMSG( HHC00329, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn) );
|
|
return NULL;
|
|
}
|
|
cckd->merging = 1;
|
|
cckd_flush_cache( dev );
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
wait_condition( &cckd->cckdiocond, &cckd->cckdiolock );
|
|
cckd->cckdwaiters--;
|
|
cckd_flush_cache( dev );
|
|
}
|
|
cckd_purge_cache( dev );
|
|
cckd_purge_l2( dev );
|
|
dev->bufcur = dev->cache = -1;
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* obtain control of the file */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* harden the current file */
|
|
cckd_harden( dev );
|
|
|
|
/* Call the compress function */
|
|
rc = cckd_comp( dev );
|
|
|
|
/* Perform initial read */
|
|
rc = cckd_read_init( dev );
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cckd->merging = 0;
|
|
if (cckd->cckdwaiters)
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* Display the shadow file statistics */
|
|
cckd_sf_stats( dev );
|
|
|
|
return NULL;
|
|
} /* end function cckd_sf_comp */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check a shadow file (sfk) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* cckd_sf_chk( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int level = 2; /* Check level */
|
|
|
|
/* NULL DEVBLK == all devices? */
|
|
if (!dev)
|
|
{
|
|
int n = 0;
|
|
level = cckdblk.sflevel;
|
|
cckdblk.sflevel = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
{
|
|
if ((cckd = dev->cckd_ext)) /* Compressed device? */
|
|
{
|
|
// "%1d:%04X CCKD file: checking level %d..."
|
|
WRMSG( HHC00330, "I", LCSS_DEVNUM, level );
|
|
cckd->sflevel = level;
|
|
cckd_sf_chk( dev );
|
|
n++;
|
|
}
|
|
}
|
|
// "CCKD file number of devices processed: %d"
|
|
WRMSG( HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_sf_chk( data );
|
|
|
|
if (!(cckd = dev->cckd_ext))
|
|
{
|
|
// "%1d:%04X CCKD file: device is not a cckd device"
|
|
WRMSG( HHC00317, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
level = cckd->sflevel;
|
|
cckd->sflevel = 0;
|
|
|
|
/* schedule updated track entries to be written */
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
if (cckd->merging)
|
|
{
|
|
release_lock( &cckd->cckdiolock );
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file check failed, sf command busy on device"
|
|
WRMSG( HHC00331, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ));
|
|
return NULL;
|
|
}
|
|
|
|
cckd->merging = 1;
|
|
cckd_flush_cache( dev );
|
|
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
{
|
|
wait_condition( &cckd->cckdiocond, &cckd->cckdiolock );
|
|
}
|
|
cckd->cckdwaiters--;
|
|
cckd_flush_cache( dev );
|
|
}
|
|
cckd_purge_cache( dev );
|
|
cckd_purge_l2( dev );
|
|
dev->bufcur = -1;
|
|
dev->cache = -1;
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* obtain control of the file */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* harden the current file */
|
|
cckd_harden( dev );
|
|
|
|
/* Call the chkdsk function */
|
|
rc = cckd_chkdsk( dev, level );
|
|
|
|
/* Perform initial read */
|
|
rc = cckd_read_init( dev );
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cckd->merging = 0;
|
|
if (cckd->cckdwaiters)
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* Display the shadow file statistics */
|
|
cckd_sf_stats( dev );
|
|
|
|
return NULL;
|
|
} /* end function cckd_sf_chk */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Display shadow file statistics (sfd) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* cckd_sf_stats( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
struct stat st = {0}; /* File information */
|
|
int i; /* Index */
|
|
char *ost[] = {" ", "ro", "rd", "rw"};
|
|
U64 usize=0,ufree=0; /* Total size, free space */
|
|
int free_count=0; /* Total number free spaces */
|
|
|
|
/* NULL DEVBLK == all devices? */
|
|
if (!dev)
|
|
{
|
|
int n = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
{
|
|
if (dev->cckd_ext) /* Is this a compressed device? */
|
|
{
|
|
// "%1d:%04X CCKD file: display cckd statistics"
|
|
WRMSG( HHC00332, "I", LCSS_DEVNUM );
|
|
cckd_sf_stats( dev );
|
|
n++;
|
|
}
|
|
}
|
|
// "CCKD file number of devices processed: %d"
|
|
WRMSG( HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_sf_stats( data );
|
|
|
|
if (!(cckd = dev->cckd_ext))
|
|
{
|
|
// "%1d:%04X CCKD file: device is not a cckd device"
|
|
WRMSG( HHC00317, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
/* Calculate totals */
|
|
if (fstat( cckd->fd[0], &st ) != 0)
|
|
{
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC00075, "E", "fstat()", strerror( errno ));
|
|
memset( &st, 0, sizeof( st ));
|
|
}
|
|
for (i=0; i <= cckd->sfn; i++)
|
|
{
|
|
if (!i) usize = st.st_size;
|
|
else usize += cckd->cdevhdr[i].cdh_size;
|
|
ufree += cckd->cdevhdr[i].free_total;
|
|
free_count += cckd->cdevhdr[i].free_num;
|
|
}
|
|
|
|
/* header */
|
|
|
|
// "%1d:%04X 32/64 size free nbr st reads writes l2reads hits switches"
|
|
WRMSG (HHC00333, "I", LCSS_DEVNUM);
|
|
|
|
if (cckd->readaheads || cckd->misses)
|
|
// "%1d:%04X readaheads misses"
|
|
WRMSG (HHC00334, "I", LCSS_DEVNUM);
|
|
|
|
// "%1d:%04X ------------------------------------------------------------------------"
|
|
WRMSG (HHC00335, "I", LCSS_DEVNUM);
|
|
|
|
/* total statistics */
|
|
|
|
// "%1d:%04X [*] %s %11.11"PRId64" %3.3"PRId64"%% %4.4"PRId64" %7.7d %7.7d %7.7d %7.7d %7.7d"
|
|
WRMSG( HHC00336, "I", LCSS_DEVNUM, dev->cckd64 ? "64" : "32", usize
|
|
|
|
, usize ? (ufree * 100) / usize : 999
|
|
, (S64)free_count
|
|
, cckd->totreads
|
|
, cckd->totwrites
|
|
, cckd->totl2reads
|
|
, cckd->cachehits
|
|
, cckd->switches
|
|
);
|
|
|
|
if (cckd->readaheads || cckd->misses)
|
|
// "%1d:%04X %7.7d %7.7d"
|
|
WRMSG (HHC00337, "I", LCSS_DEVNUM,
|
|
cckd->readaheads, cckd->misses);
|
|
|
|
/* base file statistics */
|
|
|
|
// "%1d:%04X %s"
|
|
WRMSG (HHC00338, "I", LCSS_DEVNUM, dev->filename);
|
|
|
|
// "%1d:%04X [0] %s %11.11"PRId64" %3.3"PRId64"%% %4.4"PRId64" %s %7.7d %7.7d %7.7d"
|
|
WRMSG( HHC00339, "I", LCSS_DEVNUM, dev->cckd64 ? "64" : "32", st.st_size
|
|
|
|
, st.st_size ?
|
|
((S64)(cckd->cdevhdr[0].free_total * 100) / st.st_size) : 999
|
|
, (S64) cckd->cdevhdr[0].free_num
|
|
, ost[cckd->open[0]]
|
|
, cckd->reads[0]
|
|
, cckd->writes[0]
|
|
, cckd->L2_reads[0]
|
|
);
|
|
|
|
if (dev->dasdsfn != NULL && CCKD_MAX_SF > 0)
|
|
// "%1d:%04X %s"
|
|
WRMSG (HHC00340, "I", LCSS_DEVNUM, cckd_sf_name(dev, -1));
|
|
|
|
/* shadow file statistics */
|
|
for (i = 1; i <= cckd->sfn; i++)
|
|
{
|
|
// "%1d:%04X [%d] %s %11.11"PRId64" %3.3"PRId64"%% %4.4"PRId64" %s %7.7d %7.7d %7.7d"
|
|
WRMSG( HHC00341, "I", LCSS_DEVNUM, i, dev->cckd64 ? "64" : "32", (S64)cckd->cdevhdr[i].cdh_size
|
|
|
|
, cckd->cdevhdr[i].cdh_size ?
|
|
((S64)(cckd->cdevhdr[i].free_total * 100) / cckd->cdevhdr[i].cdh_size) : 999
|
|
, (S64) cckd->cdevhdr[i].free_num
|
|
, ost[cckd->open[i]]
|
|
, cckd->reads[i]
|
|
, cckd->writes[i]
|
|
, cckd->L2_reads[i]
|
|
);
|
|
}
|
|
|
|
return NULL;
|
|
} /* end function cckd_sf_stats */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Lock CCKD device chain */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_lock_devchain( int flag )
|
|
{
|
|
obtain_lock( &cckdblk.devlock );
|
|
{
|
|
while (( flag && cckdblk.devusers != 0)
|
|
|| (!flag && cckdblk.devusers < 0))
|
|
{
|
|
cckdblk.devwaiters++;
|
|
{
|
|
#if 0
|
|
{
|
|
struct timespec tm;
|
|
struct timeval now;
|
|
int timeout;
|
|
|
|
gettimeofday( &now, NULL );
|
|
tm.tv_sec = now.tv_sec + 2;
|
|
tm.tv_nsec = now.tv_usec * 1000;
|
|
timeout = timed_wait_condition( &cckdblk.devcond, &cckdblk.devlock, &tm );
|
|
if (timeout) cckd_print_itrace();
|
|
}
|
|
#else
|
|
wait_condition( &cckdblk.devcond, &cckdblk.devlock );
|
|
#endif
|
|
}
|
|
cckdblk.devwaiters--;
|
|
}
|
|
|
|
if (flag) cckdblk.devusers--;
|
|
else cckdblk.devusers++;
|
|
}
|
|
release_lock( &cckdblk.devlock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Unlock CCKD device chain */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_unlock_devchain()
|
|
{
|
|
obtain_lock( &cckdblk.devlock );
|
|
{
|
|
if (cckdblk.devusers < 0) cckdblk.devusers++;
|
|
else cckdblk.devusers--;
|
|
|
|
if (!cckdblk.devusers && cckdblk.devwaiters)
|
|
signal_condition( &cckdblk.devcond );
|
|
}
|
|
release_lock( &cckdblk.devlock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* ADD this device to our cckd device chain */
|
|
/*-------------------------------------------------------------------*/
|
|
/* */
|
|
/* PROGRAMMING NOTE: it doesn't matter whether the CCKD EXTENSION */
|
|
/* pointer we use is CCKD_EXT or CCKD64_EXT, since the 'devnext' */
|
|
/* field is always the very first field in both, and is always the */
|
|
/* same size too, being a simple pointer to the next device's DEVBLK.*/
|
|
/* */
|
|
/* Caller MUST hold the devchain lock before calling! */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
void add_dev_to_cckd_chain_locked( DEVBLK* new_dev )
|
|
{
|
|
DEVBLK* dev;
|
|
CCKD_EXT* cckd;
|
|
|
|
// New devices are always added to the end of the existing chain,
|
|
// becoming the new end-of-chain device.
|
|
|
|
cckd = new_dev->cckd_ext; // (point to our CCKD_EXT)
|
|
cckd->devnext = NULL; // (indicate end-of-chain)
|
|
|
|
if (!cckdblk.dev1st) // (does chain exist yet?)
|
|
{
|
|
cckdblk.dev1st = new_dev; // (we're the first link!)
|
|
}
|
|
else
|
|
{
|
|
// Chase the existing device chain until NULL (end-of-chain)
|
|
// is reached, saving the ptr to each device's CCKD_EXT along
|
|
// the way.
|
|
//
|
|
// When we're done, our CCKD_EXT pointer will always point to
|
|
// the CCKD_EXT of the device that was previously AS the last
|
|
// device in the chain (as its 'devnext' is currently NULL).
|
|
|
|
for (dev = cckdblk.dev1st; dev; cckd = dev->cckd_ext,
|
|
dev = cckd->devnext)
|
|
{
|
|
; // (do nothing)
|
|
}
|
|
|
|
ASSERT( !dev && cckd && !cckd->devnext ); // (sanity check)
|
|
|
|
// Now update the old/previous last-device-in-the-chain to
|
|
// point to us instead, so that WE then become the new
|
|
// end-of-chain device (since OUR 'devnext' is NULL).
|
|
|
|
cckd->devnext = new_dev; // (point old last to us)
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* REMOVE this device from our cckd device chain */
|
|
/*-------------------------------------------------------------------*/
|
|
/* */
|
|
/* PROGRAMMING NOTE: it doesn't matter whether the CCKD EXTENSION */
|
|
/* pointer we use is CCKD_EXT or CCKD64_EXT, since the 'devnext' */
|
|
/* field is always the very first field in both, and is always the */
|
|
/* same size too, being a simple pointer to the next device's DEVBLK.*/
|
|
/* */
|
|
/* Caller MUST hold the devchain lock before calling! */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
void remove_dev_from_cckd_chain_locked( DEVBLK* old_dev )
|
|
{
|
|
DEVBLK* prev_dev; // (device that currently points to US)
|
|
CCKD_EXT* prev_ext; // (prev_dev's ext))
|
|
|
|
DEVBLK* our_next_dev; // (device that WE currently point to)
|
|
CCKD_EXT* ext; // (work)
|
|
|
|
// Save pointer to device that WE currently point to (our next)
|
|
|
|
ext = old_dev->cckd_ext; // (pointer to OUR extension)
|
|
our_next_dev = ext->devnext; // (save pointer to OUR next)
|
|
|
|
// If we're currently the very first entry in the chain (the
|
|
// head of the chain), then just set the first-in-chain pointer
|
|
// to whatever device our next-in-chain happens to point to, and
|
|
// we're done!
|
|
|
|
if (cckdblk.dev1st == old_dev) // (head of chain == US?)
|
|
{
|
|
cckdblk.dev1st = our_next_dev; // (our next dev or NULL)
|
|
return;
|
|
}
|
|
|
|
// Otherwise we need to locate the device in the chain that
|
|
// currently points to US (our PREVIOUS link in the chain).
|
|
|
|
for (prev_dev = cckdblk.dev1st, // (start at beginning)
|
|
prev_ext = prev_dev->cckd_ext; // (save its ext pointer)
|
|
|
|
prev_ext->devnext != old_dev; // (until our prev found)
|
|
|
|
prev_dev = prev_ext->devnext, // (next device in chain)
|
|
prev_ext = prev_dev->cckd_ext ) // (save its ext pointer)
|
|
{
|
|
; // (do nothing)
|
|
}
|
|
|
|
ASSERT( prev_ext->devnext == old_dev ); // (quick sanity check)
|
|
|
|
// Update our PREVIOUS device's next-in-chain pointer to NOT point
|
|
// to US (since we're going away!), but rather to point to OUR
|
|
// next-device-in-chain value instead (thus effectively removing
|
|
// us from the chain by causing it to completely skip over us).
|
|
|
|
prev_ext->devnext = our_next_dev; // (our next dev or NULL)
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Start garbage collector */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_gcstart( bool by_cmdline )
|
|
{
|
|
DEVBLK* dev;
|
|
CCKD_EXT* cckd;
|
|
CCKD64_EXT* cckd64;
|
|
|
|
bool freespace = false;
|
|
TID tid;
|
|
int rc;
|
|
const char* threadname = by_cmdline ? CCKD_GC_THREAD_NAME " (by command line)"
|
|
: CCKD_GC_THREAD_NAME;
|
|
|
|
/* Don't bother if shutting down */
|
|
if (sysblk.shutdown)
|
|
return;
|
|
|
|
/* Check if Garbage Collection is even needed! (i.e. if any
|
|
compressed dasds have any free space at all, meaning the
|
|
Garbage Collector has some work to do to try and remove it)
|
|
*/
|
|
cckd_lock_devchain(0);
|
|
{
|
|
/* Search all CCKD devices... */
|
|
|
|
for (dev = cckdblk.dev1st; dev; dev = cckd->devnext)
|
|
{
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (dev->cckd64)
|
|
continue;
|
|
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* Is there any free that can possibly be removed? */
|
|
if (cckd->cdevhdr[ cckd->sfn ].free_total)
|
|
{
|
|
/* Yes, update its compressed device header */
|
|
freespace = true;
|
|
cckd->cdevhdr[ cckd->sfn ].cdh_opts |= (CCKD_OPT_OPENED | CCKD_OPT_OPENRW);
|
|
cckd_write_chdr( dev );
|
|
}
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
}
|
|
|
|
/* Search all CCKD64 devices... */
|
|
|
|
for (dev = cckdblk.dev1st; dev; dev = cckd64->devnext)
|
|
{
|
|
cckd64 = dev->cckd_ext;
|
|
|
|
if (!dev->cckd64)
|
|
continue;
|
|
|
|
obtain_lock( &cckd64->filelock );
|
|
{
|
|
/* Is there any free that can possibly be removed? */
|
|
if (cckd64->cdevhdr[ cckd64->sfn ].free_total)
|
|
{
|
|
/* Yes, update its compressed device header */
|
|
freespace = true;
|
|
cckd64->cdevhdr[ cckd64->sfn ].cdh_opts |= (CCKD_OPT_OPENED | CCKD_OPT_OPENRW);
|
|
cckd64_write_chdr( dev );
|
|
}
|
|
}
|
|
release_lock( &cckd64->filelock );
|
|
}
|
|
}
|
|
cckd_unlock_devchain();
|
|
|
|
/* Schedule the garbage collector, if it's
|
|
needed, possible, and not already running
|
|
*/
|
|
if (freespace && cckdblk.gcs < cckdblk.gcmax)
|
|
{
|
|
/* (ensure read integrity for gc count) */
|
|
obtain_lock( &cckdblk.gclock );
|
|
{
|
|
if (cckdblk.gcs < cckdblk.gcmax)
|
|
{
|
|
/* Schedule the garbage collector thread */
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Starting thread %s, active=%d, started=%d, max=%d"
|
|
WRMSG( HHC00107, "I", threadname,
|
|
cckdblk.gca, cckdblk.gcs, cckdblk.gcmax );
|
|
|
|
++cckdblk.gcs;
|
|
|
|
rc = create_thread( &tid, JOINABLE, cckd_gcol, NULL, CCKD_GC_THREAD_NAME );
|
|
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread() for %s %d of %d: %s"
|
|
WRMSG( HHC00106, "E", threadname,
|
|
cckdblk.gcs-1, cckdblk.gcmax, strerror( rc ));
|
|
|
|
--cckdblk.gcs;
|
|
}
|
|
}
|
|
}
|
|
release_lock( &cckdblk.gclock );
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* The one and ONLY Garbage Collection thread */
|
|
/*-------------------------------------------------------------------*/
|
|
void* cckd_gcol(void* arg)
|
|
{
|
|
int gcol; /* GC thread Identifier */
|
|
DEVBLK *dev; /* -> device block */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
struct timeval tv_now; /* Time-of-day (as timeval) */
|
|
time_t tt_now; /* Time-of-day (as time_t) */
|
|
struct timespec tm; /* Time-of-day to wait */
|
|
int gcs; /* #of gc threads started */
|
|
bool cont = true; /* Continue running boolean */
|
|
|
|
UNREFERENCED( arg );
|
|
|
|
/* Don't bother if shutting down */
|
|
if (sysblk.shutdown)
|
|
return NULL;
|
|
|
|
LOCK_DH_GC(); // (prevent Dasd Hardener from running)
|
|
|
|
gettimeofday( &tv_now, NULL );
|
|
|
|
obtain_lock (&cckdblk.gclock);
|
|
{
|
|
gcol = ++cckdblk.gca; // (gc thread identifier)
|
|
|
|
/* Return without messages if too many already started */
|
|
if (gcol > cckdblk.gcmax)
|
|
{
|
|
--cckdblk.gcs;
|
|
--cckdblk.gca;
|
|
|
|
if (!cckdblk.gcs)
|
|
{
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' ended"
|
|
LOG_THREAD_END( CCKD_GC_THREAD_NAME );
|
|
}
|
|
else
|
|
if (!cckdblk.batch || cckdblk.batchml > 0)
|
|
// "Ending thread "TIDPAT" %s, pri=%d, started=%d, max=%d exceeded"
|
|
WRMSG( HHC00108, "W", TID_CAST( thread_id()), CCKD_GC_THREAD_NAME,
|
|
get_thread_priority(), cckdblk.gcs, cckdblk.gcmax );
|
|
|
|
release_lock( &cckdblk.gclock );
|
|
signal_condition( &cckdblk.termcond ); /* signal if last gcol thread ending before init. */
|
|
UNLOCK_DH_GC(); // (allow Dasd Hardener to run)
|
|
return NULL; /* too many already started, return */
|
|
}
|
|
}
|
|
release_lock( &cckdblk.gclock );
|
|
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' started"
|
|
LOG_THREAD_BEGIN( CCKD_GC_THREAD_NAME );
|
|
|
|
while (1)
|
|
{
|
|
/* Exit when asked to do so (gcmax=0) */
|
|
obtain_lock( &cckdblk.gclock );
|
|
{
|
|
cont =
|
|
(1
|
|
&& cckdblk.gcmax > 0
|
|
&& cckdblk.gcint > 0
|
|
&& gcol <= cckdblk.gcmax
|
|
&& !sysblk.shutdown
|
|
);
|
|
}
|
|
release_lock( &cckdblk.gclock );
|
|
|
|
if (!cont) // (continue running?)
|
|
break; // (no, time to exit!)
|
|
|
|
// "Begin CCKD garbage collection"
|
|
if (cckdblk.gcmsgs)
|
|
WRMSG( HHC00382, "I" );
|
|
|
|
/* Perform collection on each device */
|
|
cckd_lock_devchain(0);
|
|
{
|
|
for (dev = cckdblk.dev1st; dev && !sysblk.shutdown; dev = cckd->devnext)
|
|
{
|
|
cckd = dev->cckd_ext;
|
|
if (!sysblk.shutdown)
|
|
cckd_gcol_dev( dev, &tv_now );
|
|
}
|
|
}
|
|
cckd_unlock_devchain();
|
|
|
|
// "End CCKD garbage collection"
|
|
if (cckdblk.gcmsgs)
|
|
WRMSG( HHC00383, "I" );
|
|
|
|
/* If we're in manual on-demand mode, then we're done. */
|
|
if (cckdblk.gcint <= 0 || sysblk.shutdown)
|
|
break;
|
|
|
|
/* Otherwise, wait a bit before starting the next cycle */
|
|
|
|
// Get the time of day again for cckd_gcol_dev's file sync check
|
|
gettimeofday (&tv_now, NULL);
|
|
tt_now = tv_now.tv_sec + ((tv_now.tv_usec + 500000)/1000000);
|
|
|
|
CCKD_TRACE( CCKD_GC_THREAD_NAME " wait %d seconds at %s",
|
|
cckdblk.gcint, ctime (&tt_now));
|
|
|
|
// "Waiting %d seconds to start next CCKD garbage collection cycle..."
|
|
if (cckdblk.gcmsgs)
|
|
WRMSG( HHC00394, "I", cckdblk.gcint );
|
|
|
|
tm.tv_sec = tv_now.tv_sec + cckdblk.gcint;
|
|
tm.tv_nsec = tv_now.tv_usec * 1000;
|
|
|
|
timed_wait_condition( &cckdblk.gccond, &DH_GC_LOCK, &tm );
|
|
}
|
|
// end while(1)
|
|
|
|
/* We're exiting... */
|
|
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Thread id "TIDPAT", prio %d, name '%s' ended"
|
|
LOG_THREAD_END( CCKD_GC_THREAD_NAME );
|
|
|
|
obtain_lock (&cckdblk.gclock);
|
|
{
|
|
cckdblk.gcs--;
|
|
cckdblk.gca--;
|
|
|
|
gcs = cckdblk.gcs;
|
|
|
|
/* Last thread to terminate? */
|
|
if (gcs <= 0)
|
|
signal_condition( &cckdblk.termcond );
|
|
}
|
|
release_lock( &cckdblk.gclock );
|
|
|
|
UNLOCK_DH_GC(); // (allow Dasd Hardener to run)
|
|
|
|
return NULL;
|
|
} /* end thread cckd_gcol */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Report compression ratios for all CCKD devices */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void cckd_gc_rpt_states()
|
|
{
|
|
if (cckdblk.gcmsgs) // (only if they're interested)
|
|
{
|
|
CCKD_EXT* cckd; /* -> cckd extension */
|
|
DEVBLK* dev; /* -> device */
|
|
|
|
obtain_lock( &cckdblk.devlock );
|
|
{
|
|
for (dev = cckdblk.dev1st; dev; dev = cckd->devnext)
|
|
{
|
|
cckd = dev->cckd_ext;
|
|
cckd_gc_rpt_state( dev );
|
|
}
|
|
}
|
|
release_lock( &cckdblk.devlock );
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Report Garbage Collection state for a given CCKD device */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_gc_rpt_state( DEVBLK* dev )
|
|
{
|
|
CCKD_EXT* cckd;
|
|
int gc;
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_gc_rpt_state( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Retrieve and report garbage collector state, but ONLY if
|
|
the image is over 100MB in size. This prevents "scaring"
|
|
the user about very fragmented files when the file is
|
|
too small to be much of a concern, as is usually the case
|
|
with e.g. shadow files.
|
|
*/
|
|
if (cckd->cdevhdr->cdh_size < (100 * _1M))
|
|
return;
|
|
|
|
gc = cckd_gc_state( dev );
|
|
|
|
switch (gc)
|
|
{
|
|
case 0: // critical!
|
|
case 1: // severe
|
|
|
|
// "%1d:%04X CCKD%s image %s is very fragmented."
|
|
WRMSG( HHC00387, "W", LCSS_DEVNUM, "",
|
|
TRIMLOC( cckd_sf_name( dev, cckd->sfn )));
|
|
break;
|
|
|
|
case 2: // moderate
|
|
|
|
// "%1d:%04X CCKD%s image %s is moderately fragmented."
|
|
WRMSG( HHC00388, "I", LCSS_DEVNUM, "",
|
|
TRIMLOC( cckd_sf_name( dev, cckd->sfn )));
|
|
break;
|
|
|
|
case 3: // light
|
|
|
|
// "%1d:%04X CCKD%s image %s is slightly fragmented."
|
|
WRMSG( HHC00389, "I", LCSS_DEVNUM, "",
|
|
TRIMLOC( cckd_sf_name( dev, cckd->sfn )));
|
|
break;
|
|
|
|
default: // less than light
|
|
|
|
break; // (don't bother reporting it)
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return Garbage Collection State for a given CCKD device */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_gc_state( DEVBLK* dev )
|
|
{
|
|
CCKD_EXT* cckd; /* -> cckd extension */
|
|
S64 size, fsiz; /* File size, free size */
|
|
int gc; /* Garbage collection state */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_gc_state( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Determine garbage state */
|
|
size = (S64) cckd->cdevhdr[ cckd->sfn ].cdh_size;
|
|
fsiz = (S64) cckd->cdevhdr[ cckd->sfn ].free_total;
|
|
|
|
if (fsiz >= (size = size/2)) gc = 0; // critical 50% - 100%
|
|
else if (fsiz >= (size = size/2)) gc = 1; // severe 25% - 50%
|
|
else if (fsiz >= (size = size/2)) gc = 2; // moderate 12.5% - 25%
|
|
else if (fsiz >= (size = size/2)) gc = 3; // light 6.3% - 12.5%
|
|
else gc = 4; // none 0% - 6.3%
|
|
|
|
/* Adjust the state based on the number of free spaces */
|
|
if (cckd->cdevhdr[ cckd->sfn ].free_num > 800 && gc > 0) gc--;
|
|
if (cckd->cdevhdr[ cckd->sfn ].free_num > 1800 && gc > 0) gc--;
|
|
if (cckd->cdevhdr[ cckd->sfn ].free_num > 3000) gc = 0;
|
|
|
|
return gc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Perform garbage collection for a given CCKD device */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_gcol_dev( DEVBLK* dev, struct timeval* tv_now )
|
|
{
|
|
int rc; /* Return code */
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
U64 size; /* Percolate size */
|
|
int gc; /* Garbage collection state */
|
|
|
|
if (dev->cckd64)
|
|
{
|
|
cckd64_gcol_dev( dev, tv_now );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
obtain_lock (&cckd->cckdiolock);
|
|
{
|
|
/* Bypass if merging or stopping */
|
|
if (cckd->merging || cckd->stopping)
|
|
{
|
|
release_lock (&cckd->cckdiolock);
|
|
return;
|
|
}
|
|
|
|
/* Bypass if not opened read-write */
|
|
if (cckd->open[cckd->sfn] != CCKD_OPEN_RW)
|
|
{
|
|
release_lock (&cckd->cckdiolock);
|
|
return;
|
|
}
|
|
|
|
/* Free newbuf if it hasn't been used */
|
|
if (!cckd->cckdioact && !cckd->bufused && cckd->newbuf)
|
|
cckd->newbuf = cckd_free (dev, "newbuf", cckd->newbuf);
|
|
|
|
cckd->bufused = 0;
|
|
|
|
/* If OPENED bit not on then flush if updated */
|
|
if (!(cckd->cdevhdr[cckd->sfn].cdh_opts & CCKD_OPT_OPENED))
|
|
{
|
|
if (cckd->updated) cckd_flush_cache (dev);
|
|
release_lock (&cckd->cckdiolock);
|
|
return;
|
|
}
|
|
|
|
/* Determine garbage state */
|
|
gc = cckd_gc_state( dev );
|
|
|
|
/* Set the size */
|
|
if (cckdblk.gcparm > 0) size = gctab[gc] << cckdblk.gcparm;
|
|
else if (cckdblk.gcparm < 0) size = gctab[gc] >> abs(cckdblk.gcparm);
|
|
else size = gctab[gc];
|
|
|
|
if (size > cckd->cdevhdr[cckd->sfn].cdh_used >> SHIFT_1K)
|
|
size = cckd->cdevhdr[cckd->sfn].cdh_used >> SHIFT_1K;
|
|
if (size < 64)
|
|
size = 64;
|
|
}
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
/* Call the garbage collector */
|
|
cckd_gc_percolate( dev, size );
|
|
|
|
/* Schedule any updated tracks to be written */
|
|
obtain_lock (&cckd->cckdiolock);
|
|
{
|
|
cckd_flush_cache (dev);
|
|
|
|
while (cckdblk.fsync && cckd->wrpending)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
{
|
|
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
|
|
}
|
|
cckd->cckdwaiters--;
|
|
}
|
|
}
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
/* Sync the file */
|
|
if (cckdblk.fsync && cckd->lastsync + 10 <= tv_now->tv_sec)
|
|
{
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
rc = fdatasync (cckd->fd[cckd->sfn]);
|
|
cckd->lastsync = tv_now->tv_sec;
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
}
|
|
|
|
/* Flush the free space */
|
|
if (cckd->cdevhdr[cckd->sfn].free_num)
|
|
{
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
cckd_flush_space (dev);
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Garbage Collection error handler helper functions/macros */
|
|
/*-------------------------------------------------------------------*/
|
|
static int cckd_gc_perc_error( DEVBLK* dev, CCKD_EXT* cckd, unsigned int moved,
|
|
const char* file, int line )
|
|
{
|
|
cckd_trace( file, line, dev, "gcperc exiting due to error, moved %u", moved );
|
|
release_lock( &cckd->filelock );
|
|
return moved;
|
|
}
|
|
#define GC_PERC_ERROR() cckd_gc_perc_error( dev, cckd, moved, __FILE__, __LINE__ )
|
|
|
|
static int cckd_gc_perc_space_error( DEVBLK* dev, CCKD_EXT* cckd, off_t upos, int i, BYTE* buf, int moved,
|
|
const char* file, int line )
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: %s(%d): offset 0x%16.16"PRIx64" unknown space %2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
|
|
WRMSG( HHC00342, "E", LCSS_DEVNUM,
|
|
cckd->sfn, cckd_sf_name( dev, cckd->sfn ), TRIMLOC( file ), line,
|
|
upos + i, buf[i], buf[i+1],buf[i+2], buf[i+3], buf[i+4]);
|
|
|
|
cckd->cdevhdr[ cckd->sfn ].cdh_opts |= CCKD_OPT_SPERRS;
|
|
cckd_print_itrace();
|
|
return cckd_gc_perc_error( dev, cckd, moved, file, line );
|
|
}
|
|
#define GC_PERC_SPACE_ERROR() cckd_gc_perc_space_error( dev, cckd, upos, i, buf, moved, __FILE__, __LINE__)
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Garbage Collection -- Percolate algorithm */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_gc_percolate( DEVBLK* dev, U64 size )
|
|
{
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
bool didmsg = false; /* HHC00384 issued */
|
|
int rc; /* Return code */
|
|
unsigned int moved = 0; /* Space moved */
|
|
int after = 0, a; /* New space after old */
|
|
int sfx; /* File index */
|
|
int i, j, k; /* Indexes */
|
|
int flags; /* Write trkimg flags */
|
|
off_t fpos, upos; /* File offsets */
|
|
unsigned int flen, ulen, len; /* Lengths */
|
|
int trk; /* Track number */
|
|
int L1idx, l2x; /* Table Indexes */
|
|
CCKD_L2ENT l2; /* Copied level 2 entry */
|
|
BYTE buf[256*1024]; /* Buffer */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_gc_percolate( dev, size );
|
|
|
|
cckd = dev->cckd_ext;
|
|
size = size << SHIFT_1K;
|
|
|
|
if (cckd->cdevhdr[ cckd->sfn ].cdh_opts & CCKD_OPT_SPERRS)
|
|
{
|
|
// "Skipping garbage collection for CCKD%s file[%d] %1d:%04X %s due to space errors"
|
|
if (cckdblk.gcmsgs)
|
|
WRMSG( HHC00385, "I", "", cckd->sfn, LCSS_DEVNUM, cckd_sf_name( dev, cckd->sfn ));
|
|
return moved;
|
|
}
|
|
|
|
/* Debug */
|
|
OBTAIN_TRACE_LOCK();
|
|
if (cckdblk.itracen)
|
|
{
|
|
RELEASE_TRACE_LOCK();
|
|
|
|
CCKD_TRACE( "gcperc size %d 1st 0x%x nbr %d largest %u",
|
|
size, cckd->cdevhdr[cckd->sfn].free_off,
|
|
cckd->cdevhdr[cckd->sfn].free_num,
|
|
cckd->cdevhdr[cckd->sfn].free_largest );
|
|
|
|
fpos = (off_t) cckd->cdevhdr[cckd->sfn].free_off;
|
|
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
CCKD_TRACE( "gcperc free[%4d]:%8.8x end %8.8x len %10d%cpend %d",
|
|
i,(int)fpos,(int)(fpos+cckd->ifb[i].ifb_len),(int)cckd->ifb[i].ifb_len,
|
|
fpos+(int)cckd->ifb[i].ifb_len == (int)cckd->ifb[i].ifb_offnxt ?
|
|
'*' : ' ',cckd->ifb[i].ifb_pending );
|
|
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
}
|
|
else
|
|
RELEASE_TRACE_LOCK();
|
|
|
|
if (!cckd->L2ok)
|
|
cckd_gc_l2(dev, buf);
|
|
|
|
/* garbage collection cycle... */
|
|
while (moved < size && after < 4)
|
|
{
|
|
obtain_lock( &cckd->filelock );
|
|
sfx = cckd->sfn;
|
|
|
|
/* Exit if no more free space */
|
|
if (cckd->cdevhdr[sfx].free_total == 0)
|
|
{
|
|
release_lock( &cckd->filelock );
|
|
return moved;
|
|
}
|
|
|
|
/* Make sure the free space chain is built */
|
|
if (!cckd->ifb) cckd_read_fsp (dev);
|
|
|
|
/* Find a space to start with */
|
|
k = -1;
|
|
upos = ulen = flen = 0;
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
|
|
/* First non-pending free space */
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (!cckd->ifb[i].ifb_pending)
|
|
{
|
|
flen += cckd->ifb[i].ifb_len;
|
|
break;
|
|
}
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
|
|
/* Continue to largest if non-zero `after' */
|
|
for ( ; i >= 0 && after; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
k = i;
|
|
if (!cckd->ifb[i].ifb_pending) flen += cckd->ifb[i].ifb_len;
|
|
if (cckd->ifb[i].ifb_len == cckd->cdevhdr[sfx].free_largest)
|
|
break;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
|
|
/* Skip following free spaces */
|
|
for ( ; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (!cckd->ifb[i].ifb_pending) flen += cckd->ifb[i].ifb_len;
|
|
if (fpos + cckd->ifb[i].ifb_len != cckd->ifb[i].ifb_offnxt) break;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
|
|
/* Space preceding largest if largest is at the end */
|
|
if (i < 0 && k >= 0)
|
|
{
|
|
if (!cckd->ifb[k].ifb_pending) flen -= cckd->ifb[i].ifb_len;
|
|
for (i = cckd->ifb[k].ifb_idxprv; i >= 0; i = cckd->ifb[i].ifb_idxprv)
|
|
{
|
|
fpos = cckd->ifb[i].ifb_idxprv >= 0
|
|
? cckd->ifb[cckd->ifb[i].ifb_idxprv].ifb_offnxt
|
|
: cckd->cdevhdr[sfx].free_off;
|
|
if (fpos + cckd->ifb[i].ifb_len < cckd->ifb[i].ifb_offnxt) break;
|
|
if (!cckd->ifb[i].ifb_pending) flen -= cckd->ifb[i].ifb_len;
|
|
}
|
|
}
|
|
|
|
/* Calculate the offset/length of the used space.
|
|
* If only imbedded free space is left, then start
|
|
* with the first used space that is not an l2 table.
|
|
*/
|
|
if (i >= 0)
|
|
{
|
|
upos = fpos + cckd->ifb[i].ifb_len;
|
|
ulen = (unsigned int)((cckd->ifb[i].ifb_offnxt ? cckd->ifb[i].ifb_offnxt : cckd->cdevhdr[sfx].cdh_size) - upos);
|
|
}
|
|
else if (!cckd->cdevhdr[sfx].free_num && cckd->cdevhdr[sfx].free_imbed)
|
|
{
|
|
upos = (off_t)(CCKD_L1TAB_POS + cckd->cdevhdr[sfx].num_L1tab * CCKD_L1ENT_SIZE);
|
|
while (1)
|
|
{
|
|
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
if (cckd->L1tab[sfx][i] == (U32)upos)
|
|
break;
|
|
if (i >= cckd->cdevhdr[sfx].num_L1tab)
|
|
break;
|
|
upos += CCKD_L2TAB_SIZE;
|
|
}
|
|
ulen = (unsigned int)(cckd->cdevhdr[sfx].cdh_size - upos);
|
|
}
|
|
|
|
/* Return if no applicable used space */
|
|
if (ulen == 0)
|
|
{
|
|
CCKD_TRACE( "gcperc no applicable space, moved %u", moved);
|
|
release_lock( &cckd->filelock );
|
|
return moved;
|
|
}
|
|
|
|
/* Reduce ulen size to minimize `after' relocations */
|
|
if (ulen > flen + 65536) ulen = flen + 65536;
|
|
if (ulen > sizeof(buf)) ulen = sizeof(buf);
|
|
|
|
CCKD_TRACE( "gcperc selected space 0x%16.16"PRIx64" len %d", upos, ulen);
|
|
|
|
if (cckd_read (dev, sfx, upos, buf, ulen) < 0)
|
|
return GC_PERC_ERROR();
|
|
|
|
/* Process each space in the buffer */
|
|
flags = cckd->cdevhdr[sfx].free_num < 100 ? CCKD_SIZE_EXACT : CCKD_SIZE_ANY;
|
|
for (i = a = 0; i + CKD_TRKHDR_SIZE <= (int)ulen; i += len)
|
|
{
|
|
/* Check for level 2 table */
|
|
for (j = 0; j < cckd->cdevhdr[sfx].num_L1tab; j++)
|
|
if (cckd->L1tab[sfx][j] == (U32)(upos + i)) break;
|
|
|
|
if (!didmsg)
|
|
{
|
|
// "Collecting garbage for CCKD%s file[%d] %1d:%04X %s..."
|
|
if (cckdblk.gcmsgs)
|
|
WRMSG( HHC00384, "I", "", cckd->sfn, LCSS_DEVNUM, cckd_sf_name( dev, cckd->sfn ));
|
|
didmsg = true;
|
|
}
|
|
|
|
if (j < cckd->cdevhdr[sfx].num_L1tab)
|
|
{
|
|
/* Moving a level 2 table */
|
|
len = CCKD_L2TAB_SIZE;
|
|
if (i + len > ulen) break;
|
|
CCKD_TRACE( "gcperc move l2tab[%d] at pos 0x%16.16"PRIx64" len %d",
|
|
j, upos + i, len);
|
|
|
|
/* Make the level 2 table active */
|
|
if (cckd_read_l2 (dev, sfx, j) < 0)
|
|
return GC_PERC_ERROR();
|
|
|
|
/* Write the level 2 table */
|
|
if (cckd_write_l2 (dev) < 0)
|
|
return GC_PERC_ERROR();
|
|
}
|
|
else
|
|
{
|
|
/* Moving a track image */
|
|
if ((trk = cckd_cchh (dev, buf + i, -1)) < 0)
|
|
return GC_PERC_SPACE_ERROR();
|
|
|
|
L1idx = trk >> 8;
|
|
l2x = trk & 0xff;
|
|
|
|
/* Read the lookup entry for the track */
|
|
if (cckd_read_l2ent (dev, &l2, trk) < 0)
|
|
return GC_PERC_ERROR();
|
|
if (l2.L2_trkoff != (U32)(upos + i))
|
|
return GC_PERC_SPACE_ERROR();
|
|
len = (int)l2.L2_size;
|
|
if (i + l2.L2_len > (int)ulen) break;
|
|
|
|
CCKD_TRACE( "gcperc move trk %d at pos 0x%16.16"PRIx64" len %hu",
|
|
trk, upos + i, l2.L2_len);
|
|
|
|
/* Relocate the track image somewhere else */
|
|
if ((rc = cckd_write_trkimg (dev, buf + i, (int)l2.L2_len, trk, flags)) < 0)
|
|
return GC_PERC_ERROR();
|
|
a += rc;
|
|
}
|
|
} /* for each space in the used space */
|
|
|
|
/* Set `after' to 1 if first time space was relocated after */
|
|
after += after ? a : (a > 0);
|
|
moved += i;
|
|
|
|
cckdblk.stats_gcolmoves++;
|
|
cckdblk.stats_gcolbytes += i;
|
|
|
|
release_lock( &cckd->filelock );
|
|
|
|
} /* while (moved < size) */
|
|
|
|
CCKD_TRACE( "gcperc moved %d 1st 0x%x nbr %u", moved,
|
|
cckd->cdevhdr[cckd->sfn].free_off,cckd->cdevhdr[cckd->sfn].free_num);
|
|
// "Collected %u bytes of garbage for CCKD%s file[%d] %1d:%04X %s..."
|
|
if (cckdblk.gcmsgs)
|
|
WRMSG( HHC00386, "I", moved, "", cckd->sfn, LCSS_DEVNUM, cckd_sf_name( dev, cckd->sfn ));
|
|
return moved;
|
|
} /* end function cckd_gc_percolate */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Garbage Collection -- Reposition level 2 tables */
|
|
/* */
|
|
/* General idea is to relocate all level 2 tables as close to the */
|
|
/* beginning of the file as possible. This can help speed up, for */
|
|
/* example, chkdsk processing. */
|
|
/* */
|
|
/* If any level 2 tables reside outside of the bounds (that is, if */
|
|
/* any level 2 table could be moved closer to the beginning of the */
|
|
/* file) then first we relocate all track images within the bounds. */
|
|
/* Note that cckd_get_space will not allocate space within the */
|
|
/* the bounds for track images. Next we try to relocate all level 2 */
|
|
/* tables outside the bounds. This may take a few iterations for */
|
|
/* the freed space within the bounds to become non-pending. */
|
|
/* */
|
|
/* The bounds can change as level 2 tables are added or removed. */
|
|
/* cckd_read_l1 sets the bounds and they are adjusted by */
|
|
/* cckd_write_l2. */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_gc_l2(DEVBLK *dev, BYTE *buf)
|
|
{
|
|
#if 1 // (deprecate this function!)
|
|
|
|
UNREFERENCED( dev );
|
|
UNREFERENCED( buf );
|
|
|
|
return 0;
|
|
|
|
#else // (deprecate this function!)
|
|
|
|
CCKD_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* Shadow file index */
|
|
int i, j; /* Work variables */
|
|
int trk; /* Track number */
|
|
int len; /* Track length */
|
|
off_t pos, fpos; /* File offsets */
|
|
|
|
if (dev->cckd64)
|
|
return cckd64_gc_l2( dev, buf );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
obtain_lock (&cckd->filelock);
|
|
sfx = cckd->sfn;
|
|
|
|
if (cckd->L2ok || cckd->cdevhdr[cckd->sfn].free_total == 0)
|
|
goto cckd_gc_l2_exit;
|
|
|
|
/* Find any level 2 table out of bounds */
|
|
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
if (cckd->L1tab[sfx][i] != CCKD_NOSIZE && cckd->L1tab[sfx][i] != CCKD_MAXSIZE
|
|
&& cckd->L2_bounds - CCKD_L2TAB_SIZE < (U64)cckd->L1tab[sfx][i])
|
|
break;
|
|
|
|
/* Return OK if no l2 tables out of bounds */
|
|
if (i >= cckd->cdevhdr[sfx].num_L1tab)
|
|
goto cckd_gc_l2_exit_ok;
|
|
|
|
/* Relocate all track images within the bounds */
|
|
pos = CCKD_L1TAB_POS + (cckd->cdevhdr[sfx].num_L1tab * CCKD_L1ENT_SIZE);
|
|
i = cckd->free_idx1st;
|
|
fpos = (off_t)cckd->cdevhdr[sfx].free_off;
|
|
while ((U64)pos < cckd->L2_bounds)
|
|
{
|
|
if (i >= 0 && pos == fpos)
|
|
{
|
|
pos += cckd->ifb[i].ifb_len;
|
|
fpos = (off_t)cckd->ifb[i].ifb_offnxt;
|
|
i = cckd->ifb[i].ifb_idxnxt;
|
|
j = 0;
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < cckd->cdevhdr[sfx].num_L1tab; j++)
|
|
if (pos == (off_t)cckd->L1tab[sfx][j])
|
|
{
|
|
pos += CCKD_L2TAB_SIZE;
|
|
break;
|
|
}
|
|
}
|
|
if (j >= cckd->cdevhdr[sfx].num_L1tab)
|
|
{
|
|
/* Found a track to relocate */
|
|
if (cckd_read (dev, sfx, pos, buf, CKD_TRKHDR_SIZE) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
if ((trk = cckd_cchh (dev, buf, -1)) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
CCKD_TRACE( "gc_l2 relocate trk[%d] offset 0x%x", trk, pos);
|
|
if ((len = cckd_read_trkimg (dev, buf, trk, NULL)) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
if (cckd_write_trkimg (dev, buf, len, trk, CCKD_SIZE_EXACT) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
/* Start over */
|
|
pos = CCKD_L1TAB_POS + (cckd->cdevhdr[sfx].num_L1tab * CCKD_L1ENT_SIZE);
|
|
i = cckd->free_idx1st;
|
|
fpos = (off_t)cckd->cdevhdr[sfx].free_off;
|
|
}
|
|
}
|
|
|
|
do {
|
|
/* Find a level 2 table to relocate */
|
|
|
|
i = cckd->free_idx1st;
|
|
fpos = (off_t) cckd->cdevhdr[sfx].free_off;
|
|
|
|
CCKD_TRACE( "gc_l2 first free[%d] pos 0x%x len %d pending %d",
|
|
i, (int)fpos, i >= 0 ? (int)cckd->ifb[i].ifb_len : -1,
|
|
i >= 0 ? cckd->ifb[i].ifb_pending : -1 );
|
|
|
|
if (i < 0 || (U64) fpos >= cckd->L2_bounds || cckd->ifb[i].ifb_pending)
|
|
goto cckd_gc_l2_exit;
|
|
|
|
CCKD_TRACE( "gc_l2 bounds 0x%"PRIx64" sfx %d num_L1tab %d",
|
|
cckd->L2_bounds, sfx, cckd->cdevhdr[sfx].num_L1tab );
|
|
|
|
if ( cckd->ifb[i].ifb_len < CCKD_L2TAB_SIZE
|
|
|| (cckd->ifb[i].ifb_len != CCKD_L2TAB_SIZE
|
|
&& cckd->ifb[i].ifb_len < CCKD_L2TAB_SIZE + CCKD_FREEBLK_SIZE)
|
|
)
|
|
{
|
|
for (i=0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
{
|
|
if (fpos + cckd->ifb[i].ifb_len == (off_t)cckd->L1tab[sfx][i])
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
{
|
|
if (cckd->L2_bounds - CCKD_L2TAB_SIZE < (U64)cckd->L1tab[sfx][i]
|
|
&& cckd->L1tab[sfx][i] != CCKD_MAXSIZE)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < cckd->cdevhdr[sfx].num_L1tab)
|
|
{
|
|
CCKD_TRACE( "gc_l2 relocate l2[%d] pos 0x%x",
|
|
i, cckd->L1tab[sfx][i] );
|
|
|
|
if (cckd_read_l2( dev, sfx, i ) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
|
|
if (cckd_write_l2( dev ) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
}
|
|
}
|
|
while (i < cckd->cdevhdr[sfx].num_L1tab);
|
|
|
|
cckd_gc_l2_exit:
|
|
release_lock (&cckd->filelock);
|
|
return 0;
|
|
|
|
cckd_gc_l2_exit_ok:
|
|
CCKD_TRACE( "gc_l2 ok%s", "" );
|
|
cckd->L2ok = 1;
|
|
goto cckd_gc_l2_exit;
|
|
|
|
#endif // (deprecate this function!)
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Find device by devnum */
|
|
/*-------------------------------------------------------------------*/
|
|
DEVBLK *cckd_find_device_by_devnum (U16 devnum)
|
|
{
|
|
DEVBLK *dev;
|
|
CCKD_EXT *cckd;
|
|
|
|
cckd_lock_devchain (0);
|
|
for (dev = cckdblk.dev1st; dev; dev = cckd->devnext)
|
|
{
|
|
if (dev->devnum == devnum) break;
|
|
cckd = dev->cckd_ext;
|
|
}
|
|
cckd_unlock_devchain ();
|
|
return dev;
|
|
} /* end function cckd_find_device_by_devnum */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Uncompress a track image */
|
|
/*-------------------------------------------------------------------*/
|
|
BYTE *cckd_uncompress (DEVBLK *dev, BYTE *from, int len, int maxlen,
|
|
int trk)
|
|
{
|
|
CCKD_EXT *cckd;
|
|
BYTE *to = NULL; /* Uncompressed buffer */
|
|
int newlen; /* Uncompressed length */
|
|
BYTE comp; /* Compression type */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "uncompress comp %d len %d maxlen %d trk %d",
|
|
from[0] & CCKD_COMPRESS_MASK, len, maxlen, trk);
|
|
|
|
/* Extract compression type */
|
|
comp = (from[0] & CCKD_COMPRESS_MASK);
|
|
|
|
/* Get a buffer to uncompress into */
|
|
if (comp != CCKD_COMPRESS_NONE && cckd->newbuf == NULL)
|
|
{
|
|
cckd->newbuf = cckd_malloc (dev, "newbuf", maxlen);
|
|
if (cckd->newbuf == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
/* Uncompress the track image */
|
|
switch (comp)
|
|
{
|
|
case CCKD_COMPRESS_NONE:
|
|
newlen = cckd_trklen (dev, from);
|
|
to = from;
|
|
break;
|
|
case CCKD_COMPRESS_ZLIB:
|
|
to = cckd->newbuf;
|
|
newlen = cckd_uncompress_zlib (dev, to, from, len, maxlen);
|
|
break;
|
|
case CCKD_COMPRESS_BZIP2:
|
|
to = cckd->newbuf;
|
|
newlen = cckd_uncompress_bzip2 (dev, to, from, len, maxlen);
|
|
break;
|
|
default:
|
|
newlen = -1;
|
|
break;
|
|
}
|
|
|
|
/* Validate the uncompressed track image */
|
|
newlen = cckd_validate (dev, to, trk, newlen);
|
|
|
|
/* Return if successful */
|
|
if (newlen > 0)
|
|
{
|
|
if (to != from)
|
|
{
|
|
cckd->newbuf = from;
|
|
cckd->bufused = 1;
|
|
}
|
|
return to;
|
|
}
|
|
|
|
/* Get a buffer now if we haven't gotten one */
|
|
if (cckd->newbuf == NULL)
|
|
{
|
|
cckd->newbuf = cckd_malloc (dev, "newbuf2", maxlen);
|
|
if (cckd->newbuf == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
/* Try each uncompression routine in turn */
|
|
|
|
/* uncompressed */
|
|
newlen = cckd_trklen (dev, from);
|
|
newlen = cckd_validate (dev, from, trk, newlen);
|
|
if (newlen > 0)
|
|
return from;
|
|
|
|
/* zlib compression */
|
|
to = cckd->newbuf;
|
|
newlen = cckd_uncompress_zlib (dev, to, from, len, maxlen);
|
|
newlen = cckd_validate (dev, to, trk, newlen);
|
|
if (newlen > 0)
|
|
{
|
|
cckd->newbuf = from;
|
|
cckd->bufused = 1;
|
|
return to;
|
|
}
|
|
|
|
/* bzip2 compression */
|
|
to = cckd->newbuf;
|
|
newlen = cckd_uncompress_bzip2 (dev, to, from, len, maxlen);
|
|
newlen = cckd_validate (dev, to, trk, newlen);
|
|
if (newlen > 0)
|
|
{
|
|
cckd->newbuf = from;
|
|
cckd->bufused = 1;
|
|
return to;
|
|
}
|
|
|
|
// "%1d:%04X CCKD file[%d] %s: uncompress error trk %d: %2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
WRMSG (HHC00343, "E",
|
|
LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn), trk,
|
|
from[0], from[1], from[2], from[3], from[4]);
|
|
if (comp & ~cckdblk.comps)
|
|
WRMSG (HHC00344, "E",
|
|
LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn), compname[comp]);
|
|
return NULL;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd_uncompress_zlib */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_uncompress_zlib (DEVBLK *dev, BYTE *to, BYTE *from, int len, int maxlen)
|
|
{
|
|
#if defined( HAVE_ZLIB )
|
|
unsigned long newlen;
|
|
int rc;
|
|
|
|
UNREFERENCED(dev);
|
|
memcpy (to, from, CKD_TRKHDR_SIZE);
|
|
newlen = maxlen - CKD_TRKHDR_SIZE;
|
|
rc = uncompress(&to[CKD_TRKHDR_SIZE], &newlen,
|
|
&from[CKD_TRKHDR_SIZE], len - CKD_TRKHDR_SIZE);
|
|
if (rc == Z_OK)
|
|
{
|
|
newlen += CKD_TRKHDR_SIZE;
|
|
to[0] = 0;
|
|
}
|
|
else
|
|
newlen = -1;
|
|
|
|
CCKD_TRACE( "uncompress zlib newlen %d rc %d",(int)newlen,rc);
|
|
|
|
return (int)newlen;
|
|
#else
|
|
UNREFERENCED(dev);
|
|
UNREFERENCED(to);
|
|
UNREFERENCED(from);
|
|
UNREFERENCED(len);
|
|
UNREFERENCED(maxlen);
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd_uncompress_bzip2 */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_uncompress_bzip2 (DEVBLK *dev, BYTE *to, BYTE *from, int len, int maxlen)
|
|
{
|
|
#if defined( CCKD_BZIP2 )
|
|
unsigned int newlen;
|
|
int rc;
|
|
|
|
UNREFERENCED(dev);
|
|
memcpy (to, from, CKD_TRKHDR_SIZE);
|
|
newlen = maxlen - CKD_TRKHDR_SIZE;
|
|
rc = BZ2_bzBuffToBuffDecompress (
|
|
(void *)&to[CKD_TRKHDR_SIZE], &newlen,
|
|
(void *)&from[CKD_TRKHDR_SIZE], len - CKD_TRKHDR_SIZE,
|
|
0, 0);
|
|
if (rc == BZ_OK)
|
|
{
|
|
newlen += CKD_TRKHDR_SIZE;
|
|
to[0] = 0;
|
|
}
|
|
else
|
|
newlen = -1;
|
|
|
|
CCKD_TRACE( "uncompress bz2 newlen %d rc %d",newlen,rc);
|
|
|
|
return (int)newlen;
|
|
#else
|
|
UNREFERENCED(dev);
|
|
UNREFERENCED(to);
|
|
UNREFERENCED(from);
|
|
UNREFERENCED(len);
|
|
UNREFERENCED(maxlen);
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compress a track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_compress (DEVBLK *dev, BYTE **to, BYTE *from, int len,
|
|
int comp, int parm)
|
|
{
|
|
int newlen;
|
|
|
|
switch (comp)
|
|
{
|
|
case CCKD_COMPRESS_NONE:
|
|
newlen = cckd_compress_none (dev, to, from, len, parm);
|
|
break;
|
|
case CCKD_COMPRESS_ZLIB:
|
|
newlen = cckd_compress_zlib (dev, to, from, len, parm);
|
|
break;
|
|
case CCKD_COMPRESS_BZIP2:
|
|
newlen = cckd_compress_bzip2 (dev, to, from, len, parm);
|
|
break;
|
|
default:
|
|
newlen = cckd_compress_bzip2 (dev, to, from, len, parm);
|
|
break;
|
|
}
|
|
return newlen;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd_compress_none */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_compress_none (DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm)
|
|
{
|
|
UNREFERENCED(dev);
|
|
UNREFERENCED(parm);
|
|
*to = from;
|
|
from[0] = CCKD_COMPRESS_NONE;
|
|
return len;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd_compress_zlib */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd_compress_zlib (DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm)
|
|
{
|
|
#if defined( HAVE_ZLIB )
|
|
unsigned long newlen;
|
|
int rc;
|
|
BYTE *buf;
|
|
|
|
UNREFERENCED(dev);
|
|
buf = *to;
|
|
from[0] = CCKD_COMPRESS_NONE;
|
|
memcpy (buf, from, CKD_TRKHDR_SIZE);
|
|
buf[0] = CCKD_COMPRESS_ZLIB;
|
|
newlen = 65535 - CKD_TRKHDR_SIZE;
|
|
rc = compress2 (&buf[CKD_TRKHDR_SIZE], &newlen,
|
|
&from[CKD_TRKHDR_SIZE], len - CKD_TRKHDR_SIZE,
|
|
parm);
|
|
newlen += CKD_TRKHDR_SIZE;
|
|
if (rc != Z_OK || (int)newlen >= len)
|
|
{
|
|
*to = from;
|
|
newlen = len;
|
|
}
|
|
return (int)newlen;
|
|
#else
|
|
|
|
#if defined( CCKD_BZIP2 )
|
|
return cckd_compress_bzip2 (dev, to, from, len, parm);
|
|
#else
|
|
return cckd_compress_none (dev, to, from, len, parm);
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
int cckd_compress_bzip2 (DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm)
|
|
{
|
|
#if defined( CCKD_BZIP2 )
|
|
unsigned int newlen;
|
|
int rc;
|
|
BYTE *buf;
|
|
|
|
UNREFERENCED(dev);
|
|
buf = *to;
|
|
from[0] = CCKD_COMPRESS_NONE;
|
|
memcpy (buf, from, CKD_TRKHDR_SIZE);
|
|
buf[0] = CCKD_COMPRESS_BZIP2;
|
|
newlen = 65535 - CKD_TRKHDR_SIZE;
|
|
rc = BZ2_bzBuffToBuffCompress (
|
|
(void *)&buf[CKD_TRKHDR_SIZE], &newlen,
|
|
(void *)&from[CKD_TRKHDR_SIZE], len - CKD_TRKHDR_SIZE,
|
|
parm >= 1 && parm <= 9 ? parm : 5, 0, 0);
|
|
newlen += CKD_TRKHDR_SIZE;
|
|
if (rc != BZ_OK || newlen >= (unsigned int)len)
|
|
{
|
|
*to = from;
|
|
newlen = len;
|
|
}
|
|
return newlen;
|
|
#else
|
|
return cckd_compress_zlib (dev, to, from, len, parm);
|
|
#endif
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd command help */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_command_help()
|
|
{
|
|
int i;
|
|
char *help[] =
|
|
{
|
|
"Command parameters for cckd:"
|
|
, ""
|
|
, " help Display help message"
|
|
, " stats Display cckd statistics"
|
|
, " opts Display cckd options"
|
|
, ""
|
|
|
|
// *** Please keep these in alphabetical order! ***
|
|
|
|
, " comp=<n> Override compression (-1,0,1,2)"
|
|
, " compparm=<n> Override compression parm (-1 ... 9)"
|
|
, " debug=<n> Enable CCW tracing debug messages (0 or 1)"
|
|
, " dhint=<n> Set Dasd Hardener interval (sec) (0 ... 999)"
|
|
, " dhstart=<n> Start Dasd Hardener (0 or 1)"
|
|
, " dtax=<n> Dump cckd trace table at exit (0 or 1)"
|
|
, " freepend=<n> Set free pending cycles (-1 ... 4)"
|
|
, " fsync=<n> Enable fsync (0 or 1)"
|
|
, " gcint=<n> Set garbage collector interval (sec) ( 0 .. 60)"
|
|
, " gcmsgs=<n> Display garbage collector messages (0 or 1)"
|
|
, " gcparm=<n> Set garbage collector parameter (-8 ... 8)"
|
|
, " gcstart=<n> Start garbage collector (0 or 1)"
|
|
, " linuxnull=<n> Check for null linux tracks (0 or 1)"
|
|
, " nosfd=<n> Disable stats report at close (0 or 1)"
|
|
, " nostress=<n> Disable stress writes (0 or 1)"
|
|
, " ra=<n> Set number readahead threads ( 1 ... 9)"
|
|
, " raq=<n> Set readahead queue size ( 0 .. 16)"
|
|
, " rat=<n> Set number tracks to read ahead ( 0 .. 16)"
|
|
, " trace=<n> Set trace table size (0 ... 200000)"
|
|
, " wr=<n> Set number writer threads ( 1 ... 9)"
|
|
|
|
, NULL
|
|
};
|
|
|
|
for (i=0; help[i] != NULL; i++ )
|
|
WRMSG(HHC00345, "I", help[i] );
|
|
|
|
} /* end function cckd_command_help */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Report cckd command opts */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_command_report_opts()
|
|
{
|
|
char msgbuf[256];
|
|
|
|
MSGBUF( msgbuf, "cckd opts:"
|
|
|
|
// *** Please keep these in alphabetical order! ***
|
|
|
|
" " "comp=%d"
|
|
"," "compparm=%d"
|
|
"," "debug=%d"
|
|
"," "dhint=%d"
|
|
"," "dhstart=%d"
|
|
"," "dtax=%d"
|
|
"," "freepend=%d"
|
|
|
|
, cckdblk.comp == 0xff ? -1 : cckdblk.comp
|
|
, cckdblk.compparm
|
|
, cckdblk.debug
|
|
, cckdblk.dhint
|
|
, cckdblk.dhstart
|
|
, cckdblk.dtax
|
|
, cckdblk.freepend
|
|
);
|
|
WRMSG( HHC00346, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " "
|
|
|
|
// *** Please keep these in alphabetical order! ***
|
|
|
|
" " "fsync=%d"
|
|
"," "gcint=%d"
|
|
"," "gcmsgs=%d"
|
|
"," "gcparm=%d"
|
|
"," "gcstart=%d"
|
|
"," "linuxnull=%d"
|
|
"," "nosfd=%d"
|
|
|
|
, cckdblk.fsync
|
|
, cckdblk.gcint
|
|
, cckdblk.gcmsgs
|
|
, cckdblk.gcparm
|
|
, cckdblk.gcstart
|
|
, cckdblk.linuxnull
|
|
, cckdblk.nosfd
|
|
);
|
|
WRMSG( HHC00346, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " "
|
|
|
|
// *** Please keep these in alphabetical order! ***
|
|
|
|
" " "nostress=%d"
|
|
"," "ra=%d"
|
|
"," "raq=%d"
|
|
"," "rat=%d"
|
|
"," "trace=%d"
|
|
"," "wr=%d"
|
|
|
|
, cckdblk.nostress
|
|
, cckdblk.ramax
|
|
, cckdblk.ranbr
|
|
, cckdblk.readaheads
|
|
, cckdblk.itracen
|
|
, cckdblk.wrmax
|
|
);
|
|
WRMSG( HHC00346, "I", msgbuf );
|
|
|
|
} /* end function cckd_command_report_opts */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd command stats */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_command_stats()
|
|
{
|
|
char msgbuf[128];
|
|
|
|
WRMSG( HHC00347, "I", "cckd stats:" );
|
|
|
|
MSGBUF( msgbuf, " reads....%10"PRId64" Kbytes...%10"PRId64,
|
|
cckdblk.stats_reads, cckdblk.stats_readbytes >> SHIFT_1K );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " writes...%10"PRId64" Kbytes...%10"PRId64,
|
|
cckdblk.stats_writes, cckdblk.stats_writebytes >> SHIFT_1K );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " readaheads%9"PRId64" misses...%10"PRId64,
|
|
cckdblk.stats_readaheads, cckdblk.stats_readaheadmisses );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " switches.%10"PRId64" l2 reads.%10"PRId64" strs wrt.%10"PRId64,
|
|
cckdblk.stats_switches, cckdblk.stats_l2reads, cckdblk.stats_stresswrites );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " cachehits%10"PRId64" misses...%10"PRId64,
|
|
cckdblk.stats_cachehits, cckdblk.stats_cachemisses );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " l2 hits..%10"PRId64" misses...%10"PRId64,
|
|
cckdblk.stats_l2cachehits, cckdblk.stats_l2cachemisses );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " waits............ i/o......%10"PRId64" cache....%10"PRId64,
|
|
cckdblk.stats_iowaits, cckdblk.stats_cachewaits );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " garbage collector moves....%10"PRId64" Kbytes...%10"PRId64,
|
|
cckdblk.stats_gcolmoves, cckdblk.stats_gcolbytes >> SHIFT_1K );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " Dasd Hardener.... passes...%10"PRId64" hardens..%10"PRId64,
|
|
cckdblk.stats_dhpasses, cckdblk.stats_hardens );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
MSGBUF( msgbuf, " filesyncs........ %10"PRId64,
|
|
cckdblk.stats_fsyncs );
|
|
WRMSG( HHC00347, "I", msgbuf );
|
|
|
|
return;
|
|
} /* end function cckd_command_stats */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd_dtax return Dump Table At Exit setting */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT bool cckd_dtax()
|
|
{
|
|
return cckdblk.dtax ? true : false;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd command processor */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int cckd_command( char* op, bool verbose )
|
|
{
|
|
char *kw, *p, c, buf[256];
|
|
int rc, val;
|
|
|
|
bool opts_changed = false;
|
|
bool gcint_changed = false;
|
|
bool dhint_changed = false;
|
|
|
|
/* Display help for null operand */
|
|
if (!op)
|
|
{
|
|
if (verbose && memcmp( &cckdblk.id, CCKDBLK_ID, sizeof( cckdblk.id )) == 0)
|
|
cckd_command_help();
|
|
return 0;
|
|
}
|
|
|
|
STRLCPY( buf, op );
|
|
op = buf;
|
|
|
|
/* Initialize the global cckd block if necessary */
|
|
if (memcmp( &cckdblk.id, CCKDBLK_ID, sizeof( cckdblk.id )))
|
|
cckd_dasd_init( 0, NULL );
|
|
|
|
while (op)
|
|
{
|
|
/* Operands are delimited by commas */
|
|
kw = op;
|
|
op = strchr( op, ',' );
|
|
if (op) *op++ = 0;
|
|
|
|
/* Check for "keyword=value" */
|
|
if ((p = strchr( kw, '=' )))
|
|
{
|
|
*p++ = 0;
|
|
c = 0;
|
|
rc = sscanf( p, "%d%c", &val, &c );
|
|
}
|
|
|
|
/* If p == NULL, then just keyword syntax (no "=value") */
|
|
if (!p)
|
|
{
|
|
/* Parse the keyword... */
|
|
|
|
// Display cckd help
|
|
if (CMD( kw, HELP, 4 ))
|
|
{
|
|
if (!verbose) return 0;
|
|
cckd_command_help();
|
|
}
|
|
// Display current cckd statistics
|
|
else if (CMD( kw, STATS, 4 ))
|
|
{
|
|
if (!verbose) return 0;
|
|
cckd_command_stats();
|
|
}
|
|
// Display current cckd options
|
|
else if (CMD( kw, OPTS, 4 ))
|
|
{
|
|
if (!verbose) return 0;
|
|
cckd_command_report_opts();
|
|
}
|
|
else
|
|
{
|
|
// "CCKD file: invalid cckd keyword: %s"
|
|
WRMSG( HHC00349, "E", kw );
|
|
cckd_command_help();
|
|
return -1;
|
|
}
|
|
}
|
|
/* If rc != 1 || c != 0 then either a non-numeric "=value"
|
|
was skipped (sscanf %d failed) and/or something follows
|
|
the numeric "=value" (sscanf %c succeeded)
|
|
*/
|
|
else if (rc != 1 || c != 0)
|
|
{
|
|
// "CCKD file: invalid numeric value %s"
|
|
WRMSG( HHC00350, "E", p );
|
|
return -1;
|
|
}
|
|
/* If rc == 1 && c == 0, then "keyword=value" syntax */
|
|
/* Please keep the below tests in alphabetical order! */
|
|
|
|
// Compression to be used
|
|
else if (CMD( kw, COMP, 4 ))
|
|
{
|
|
if (val < -1 || (val > 0 && (val & ~cckdblk.comps)))
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else switch (val)
|
|
{
|
|
case -1:
|
|
case CCKD_COMPRESS_NONE:
|
|
case CCKD_COMPRESS_ZLIB:
|
|
case CCKD_COMPRESS_BZIP2:
|
|
cckdblk.comp = val < 0 ? 0xff : val;
|
|
opts_changed = true;
|
|
break;
|
|
default: /* unsupported algorithm */
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
}
|
|
// Compression parameter to be used
|
|
else if (CMD( kw, COMPPARM, 8 ))
|
|
{
|
|
if (val < -1 || val > 9)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.compparm = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Turn CCW tracing debug messages on or off
|
|
else if (CMD( kw, DEBUG, 5 ))
|
|
{
|
|
if (val < 0 || val > 1)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.debug = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Check for Dasd Hardener interval in seconds
|
|
else if (CMD( kw, DHINT, 5 ))
|
|
{
|
|
if (val < CCKD_MIN_DHINT || val > CCKD_MAX_DHINT)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.dhint = val;
|
|
opts_changed = dhint_changed = true;
|
|
}
|
|
}
|
|
// Start Dasd Hardener
|
|
else if (CMD( kw, DHSTART, 7 ))
|
|
{
|
|
if (val < 0 || val > 1)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.dhstart = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Dump Table At Exit
|
|
else if (CMD( kw, DTAX, 4 ))
|
|
{
|
|
if (val < 0 || val > 1)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.dtax = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Set the free pending value
|
|
else if (CMD( kw, FREEPEND, 8 ))
|
|
{
|
|
if (val < -1 || val > CCKD_MAX_FREEPEND)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.freepend = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Turn fsync on or off
|
|
else if (CMD( kw, FSYNC, 5 ))
|
|
{
|
|
if (val < 0 || val > 1)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.fsync = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Garbage collection interval
|
|
else if (CMD( kw, GCINT, 5 ))
|
|
{
|
|
if (val < CCKD_MIN_GCINT || val > CCKD_MAX_GCINT)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.gcint = val;
|
|
opts_changed = gcint_changed = true;
|
|
}
|
|
}
|
|
// Garbage collector messages
|
|
else if (CMD( kw, GCMSGS, 6 ))
|
|
{
|
|
if (val < 0 || val > 1)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.gcmsgs = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Garbage collection parameter
|
|
else if (CMD( kw, GCPARM, 6 ))
|
|
{
|
|
if (val < CCKD_MIN_GCPARM || val > CCKD_MAX_GCPARM)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.gcparm = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Start garbage collector
|
|
else if (CMD( kw, GCSTART, 7 ))
|
|
{
|
|
if (val < 0 || val > 1)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.gcstart = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Check for null linux tracks
|
|
else if (CMD( kw, LINUXNULL, 5 ))
|
|
{
|
|
if (val < 0 || val > 1)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.linuxnull = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Turn off stats report at close
|
|
else if (CMD( kw, NOSFD, 5 ))
|
|
{
|
|
if (val < 0 || val > 1)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.nosfd = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Turn stress writes on or off
|
|
else if (CMD( kw, NOSTRESS, 8 ))
|
|
{
|
|
if (val < 0 || val > 1)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.nostress = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Number readahead threads
|
|
else if (CMD( kw, RA, 2 ))
|
|
{
|
|
if (val < CCKD_MIN_RA || val > CCKD_MAX_RA)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.ramax = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Readahead queue size
|
|
else if (CMD( kw, RAQ, 3 ))
|
|
{
|
|
if (val < 0 || val > CCKD_MAX_RA_SIZE)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.ranbr = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Number of tracks to readahead
|
|
else if (CMD( kw, RAT, 3 ))
|
|
{
|
|
if (val < 0 || val > CCKD_MAX_RA_SIZE)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.readaheads = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Number of trace table entries
|
|
else if (CMD( kw, TRACE, 5 ))
|
|
{
|
|
if (val < 0 || val > CCKD_MAX_NUM_TRACE)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
OBTAIN_TRACE_LOCK();
|
|
{
|
|
CCKD_ITRACE* itrace = cckdblk.itrace;
|
|
|
|
/* Tell others there is no table anymore */
|
|
cckdblk.itracen = 0;
|
|
cckdblk.itracec = 0;
|
|
cckdblk.itrace = NULL;
|
|
cckdblk.itracep = NULL;
|
|
cckdblk.itracex = NULL;
|
|
|
|
/* Discard existing trace table */
|
|
free( itrace );
|
|
|
|
/* Allocate new table (if requested) */
|
|
if (val > 0)
|
|
{
|
|
if (!(itrace = calloc( val, sizeof( CCKD_ITRACE ))))
|
|
{
|
|
char buf[64];
|
|
MSGBUF( buf, "calloc( %d, %d )",
|
|
val, (int) sizeof( CCKD_ITRACE ));
|
|
// "%1d:%04X CCKD file: error in function %s: %s"
|
|
WRMSG( HHC00303, "E", 0,0, buf, strerror( errno ));
|
|
val = 0;
|
|
}
|
|
}
|
|
else
|
|
itrace = NULL;
|
|
|
|
/* Update global variables */
|
|
cckdblk.itrace = itrace;
|
|
cckdblk.itracep = itrace;
|
|
cckdblk.itracex = itrace + val;
|
|
cckdblk.itracen = val;
|
|
cckdblk.itracec = 0;
|
|
opts_changed = true;
|
|
}
|
|
RELEASE_TRACE_LOCK();
|
|
}
|
|
}
|
|
// Number writer threads
|
|
else if (CMD( kw, WR, 2 ))
|
|
{
|
|
if (val < CCKD_MIN_WRITER || val > CCKD_MAX_WRITER)
|
|
{
|
|
// "CCKD file: value %d invalid for %s"
|
|
WRMSG( HHC00348, "E", val, kw );
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
cckdblk.wrmax = val;
|
|
opts_changed = true;
|
|
}
|
|
}
|
|
// Unknown option
|
|
else
|
|
{
|
|
// "CCKD file: invalid cckd keyword: %s"
|
|
WRMSG( HHC00349, "E", kw );
|
|
if (!verbose) return -1;
|
|
cckd_command_help();
|
|
op = NULL;
|
|
}
|
|
}
|
|
/* end while (op) */
|
|
|
|
/* If anything changed... */
|
|
if (opts_changed)
|
|
{
|
|
// Report new values if requested */
|
|
if (verbose)
|
|
cckd_command_report_opts();
|
|
|
|
/* REACT to the new values, as needed... */
|
|
|
|
if (gcint_changed) // (notify thread of new interval)
|
|
{
|
|
obtain_lock( &cckdblk.gclock );
|
|
{
|
|
broadcast_condition( &cckdblk.gccond );
|
|
}
|
|
release_lock( &cckdblk.gclock );
|
|
}
|
|
|
|
if (cckdblk.gcstart) // (start thread if needed)
|
|
cckd_gcstart( true );
|
|
|
|
if (dhint_changed) // (notify thread of new interval)
|
|
{
|
|
obtain_lock( &cckdblk.dhlock );
|
|
{
|
|
broadcast_condition( &cckdblk.dhcond );
|
|
}
|
|
release_lock( &cckdblk.dhlock );
|
|
}
|
|
|
|
if (cckdblk.dhstart) // (start thread if needed)
|
|
cckd_dhstart( true );
|
|
}
|
|
|
|
return 0;
|
|
} /* end function cckd_command */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Print internal trace */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void cckd_print_itrace()
|
|
{
|
|
|
|
// "CCKD file: internal cckd trace"
|
|
WRMSG( HHC00399, "I" );
|
|
|
|
OBTAIN_TRACE_LOCK();
|
|
if (cckdblk.itracen) // have trace table?
|
|
{
|
|
CCKD_ITRACE* pbeg; // start of trace table
|
|
CCKD_ITRACE* pcur; // current working entry
|
|
int i; // loop counter
|
|
|
|
pbeg = cckdblk.itrace; // save beginning of table
|
|
pcur = cckdblk.itracep; // "current" (oldest) entry
|
|
|
|
if (pcur >= cckdblk.itracex) // past end of table?
|
|
pcur = pbeg; // then wrap to begin
|
|
|
|
for (i=0; i < cckdblk.itracec; ++i) // print all used entries
|
|
{
|
|
if (*(const char*)pcur) // print entry if not empty
|
|
// "%s"
|
|
WRMSG( HHC00398, "I", (const char*) pcur );
|
|
|
|
if (++pcur >= cckdblk.itracex) // next entry; if past end
|
|
pcur = pbeg; // then wrap to begin
|
|
}
|
|
|
|
// Clear all table entries and reset pointers
|
|
memset( pbeg, 0, cckdblk.itracen * sizeof( CCKD_ITRACE ));
|
|
cckdblk.itracep = pbeg;
|
|
cckdblk.itrace = pbeg;
|
|
cckdblk.itracec = 0;
|
|
}
|
|
RELEASE_TRACE_LOCK();
|
|
|
|
} /* end function cckd_print_itrace */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write internal trace entry */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd_trace( const char* func, int line, DEVBLK* dev, char* fmt, ... )
|
|
{
|
|
int rc;
|
|
va_list vl;
|
|
char trcmsg[ CCKD_TRACE_SIZE ];
|
|
|
|
/* Quick exit if trace messages not enabled or no trace table */
|
|
if (!(dev && (cckdblk.debug || cckdblk.itrace)))
|
|
return;
|
|
|
|
/* Build the cckd trace message */
|
|
va_start( vl, fmt );
|
|
rc = vsnprintf( trcmsg, sizeof( trcmsg ), fmt, vl );
|
|
va_end( vl );
|
|
if (rc < 0)
|
|
return;
|
|
|
|
/* Add the message to our internal trace table (if we have one) */
|
|
OBTAIN_TRACE_LOCK();
|
|
if (cckdblk.itracen)
|
|
{
|
|
CCKD_ITRACE* itracep; // (trace table entry ptr)
|
|
struct timeval timeval; // (microsecond accuracy)
|
|
time_t todsecs; // (#of secs since epoch)
|
|
char todwrk[32]; // (work)
|
|
char trcpfx[64]; // "HHHHHHHH @ hh:mm:ss.uuuuuu n:CCUU"
|
|
|
|
/* Build TID+TOD+CUU trace message prefix string */
|
|
|
|
gettimeofday( &timeval, NULL ); // (microsecond accuracy)
|
|
|
|
todsecs = timeval.tv_sec;
|
|
STRLCPY( todwrk, ctime( &todsecs ));// "Day Mon dd hh:mm:ss yyyy\n"
|
|
todwrk[19] = 0; // "Day Mon dd hh:mm:ss"
|
|
|
|
MSGBUF( trcpfx,
|
|
|
|
TIDPAT" @ %s.%6.6ld %1d:%04X", // "HHHHHHHH @ hh:mm:ss.uuuuuu n:CCUU"
|
|
|
|
TID_CAST(hthread_self()), // "HHHHHHHH" (TIDPAT)
|
|
todwrk + 11, // "hh:mm:ss" (%s)
|
|
(long int)timeval.tv_usec, // "uuuuuu" (%6.6ld
|
|
LCSS_DEVNUM // "n:CCUU" (%1d:%04X)
|
|
);
|
|
|
|
/* Print trace message directly into trace table entry */
|
|
|
|
itracep = cckdblk.itracep++; // (grab table entry)
|
|
|
|
if (itracep >= cckdblk.itracex) // (past end of table?)
|
|
{
|
|
itracep = cckdblk.itrace; // (wrap to beginning)
|
|
cckdblk.itracep = itracep + 1; // (curr = next entry)
|
|
}
|
|
|
|
if (cckdblk.itracec < cckdblk.itracen) // (less than all used?)
|
|
cckdblk.itracec++; // (count entries used)
|
|
|
|
snprintf( (char*) itracep, CCKD_TRACE_SIZE, "%s %s(%d): %s",
|
|
trcpfx, func, line, trcmsg );
|
|
}
|
|
RELEASE_TRACE_LOCK();
|
|
|
|
/* Log the trace entry if requested */
|
|
if (cckdblk.debug)
|
|
{
|
|
if (dev && dev->ccwtrace)
|
|
// "%1d:%04X %s"
|
|
WRMSG( HHC00396, "I", LCSS_DEVNUM, trcmsg );
|
|
}
|
|
|
|
} /* end function cckd_trace */
|
|
|
|
DLL_EXPORT DEVHND cckd_dasd_device_hndinfo = {
|
|
&ckd_dasd_init_handler, /* Device Initialization */
|
|
&ckd_dasd_execute_ccw, /* Device CCW execute */
|
|
&cckd_dasd_close_device, /* Device Close */
|
|
&ckd_dasd_query_device, /* Device Query */
|
|
NULL, /* Device Extended Query */
|
|
&cckd_dasd_start, /* Device Start channel pgm */
|
|
&cckd_dasd_end, /* Device End channel pgm */
|
|
&cckd_dasd_start, /* Device Resume channel pgm */
|
|
&cckd_dasd_end, /* Device Suspend channel pgm */
|
|
NULL, /* Device Halt channel pgm */
|
|
&cckd_read_track, /* Device Read */
|
|
&cckd_update_track, /* Device Write */
|
|
&cckd_used, /* Device Query used */
|
|
NULL, /* Device Reserve */
|
|
NULL, /* Device Release */
|
|
NULL, /* Device Attention */
|
|
NULL, /* Immediate CCW Codes */
|
|
NULL, /* Signal Adapter Input */
|
|
NULL, /* Signal Adapter Output */
|
|
NULL, /* Signal Adapter Sync */
|
|
NULL, /* Signal Adapter Output Mult */
|
|
NULL, /* QDIO subsys desc */
|
|
NULL, /* QDIO set subchan ind */
|
|
&ckd_dasd_hsuspend, /* Hercules suspend */
|
|
&ckd_dasd_hresume /* Hercules resume */
|
|
};
|
|
|
|
DLL_EXPORT DEVHND cfba_dasd_device_hndinfo = {
|
|
&fba_dasd_init_handler, /* Device Initialization */
|
|
&fba_dasd_execute_ccw, /* Device CCW execute */
|
|
&cckd_dasd_close_device, /* Device Close */
|
|
&fba_dasd_query_device, /* Device Query */
|
|
NULL, /* Device Extended Query */
|
|
&cckd_dasd_start, /* Device Start channel pgm */
|
|
&cckd_dasd_end, /* Device End channel pgm */
|
|
&cckd_dasd_start, /* Device Resume channel pgm */
|
|
&cckd_dasd_end, /* Device Suspend channel pgm */
|
|
NULL, /* Device Halt channel pgm */
|
|
&cfba_read_block, /* Device Read */
|
|
&cfba_write_block, /* Device Write */
|
|
&cfba_used, /* Device Query used */
|
|
NULL, /* Device Reserve */
|
|
NULL, /* Device Release */
|
|
NULL, /* Device Attention */
|
|
NULL, /* Immediate CCW Codes */
|
|
NULL, /* Signal Adapter Input */
|
|
NULL, /* Signal Adapter Output */
|
|
NULL, /* Signal Adapter Sync */
|
|
NULL, /* Signal Adapter Output Mult */
|
|
NULL, /* QDIO subsys desc */
|
|
NULL, /* QDIO set subchan ind */
|
|
&fba_dasd_hsuspend, /* Hercules suspend */
|
|
&ckd_dasd_hresume /* Hercules resume */
|
|
};
|