Files
gottfriedleibniz 3d239d3db5 cleanup va_start/va_end and introduce vasprintf (#826)
* 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.
2026-04-30 12:44:49 -07:00

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 */
};