Files
org-hyperion-cules/cckddasd.c
Fish (David B. Trout) 312dc2a5ac Rename CCKDDASD_EXT struct fields to eliminate confusion:
To more easily locate (grep) code pertaining to one feature from another that of another it helps for them to use different variable names.
2014-08-21 06:54:26 -07:00

5968 lines
202 KiB
C

/* CCKDDASD.C (c) Copyright Roger Bowler, 1999-2012 */
/* (c) Copyright Greg Smith, 2002-2012 */
/* */
/* ESA/390 Compressed CKD Direct Access Storage 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"
/*-------------------------------------------------------------------*/
/* Internal functions */
/*-------------------------------------------------------------------*/
int cckddasd_init(int argc, BYTE *argv[]);
int cckddasd_term();
int cckddasd_init_handler( DEVBLK *dev, int argc, char *argv[] );
int cckddasd_close_device(DEVBLK *dev);
void cckddasd_start(DEVBLK *dev);
void cckddasd_end(DEVBLK *dev);
int cckd_open (DEVBLK *dev, int sfx, int flags, mode_t mode);
int cckd_close (DEVBLK *dev, int sfx);
int cckd_read (DEVBLK *dev, int sfx, off_t off, void *buf, unsigned int len);
int cckd_write (DEVBLK *dev, int sfx, off_t off, void *buf, unsigned int len);
int cckd_ftruncate(DEVBLK *dev, int sfx, off_t off);
void *cckd_malloc(DEVBLK *dev, char *id, size_t size);
void *cckd_calloc(DEVBLK *dev, char *id, size_t n, size_t size);
void *cckd_free(DEVBLK *dev, char *id,void *p);
int cckd_read_track(DEVBLK *dev, int trk, BYTE *unitstat);
int cckd_update_track(DEVBLK *dev, int trk, int off,
BYTE *buf, int len, BYTE *unitstat);
int cckd_used(DEVBLK *dev);
int cfba_read_block(DEVBLK *dev, int blkgrp, BYTE *unitstat);
int cfba_write_block(DEVBLK *dev, int blkgrp, int off,
BYTE *buf, int wrlen, BYTE *unitstat);
int cfba_used(DEVBLK *dev);
int cckd_read_trk(DEVBLK *dev, int trk, int ra, BYTE *unitstat);
void cckd_readahead(DEVBLK *dev, int trk);
int cckd_readahead_scan(int *answer, int ix, int i, void *data);
void* cckd_ra(void* arg);
void cckd_flush_cache(DEVBLK *dev);
int cckd_flush_cache_scan(int *answer, int ix, int i, void *data);
void cckd_flush_cache_all();
void cckd_purge_cache(DEVBLK *dev);
int cckd_purge_cache_scan(int *answer, int ix, int i, void *data);
void* cckd_writer(void *arg);
int cckd_writer_scan(int *o, int ix, int i, void *data);
off_t cckd_get_space(DEVBLK *dev, int *size, int flags);
void cckd_rel_space(DEVBLK *dev, off_t pos, int len, int size);
void cckd_flush_space(DEVBLK *dev);
int cckd_read_chdr(DEVBLK *dev);
int cckd_write_chdr(DEVBLK *dev);
int cckd_read_l1(DEVBLK *dev);
int cckd_write_l1(DEVBLK *dev);
int cckd_write_l1ent(DEVBLK *dev, int l1x);
int cckd_read_init(DEVBLK *dev);
int cckd_read_fsp(DEVBLK *dev);
int cckd_write_fsp(DEVBLK *dev);
int cckd_read_l2(DEVBLK *dev, int sfx, int l1x);
void cckd_purge_l2(DEVBLK *dev);
int cckd_purge_l2_scan(int *answer, int ix, int i, void *data);
int cckd_steal_l2();
int cckd_steal_l2_scan(int *answer, int ix, int i, void *data);
int cckd_write_l2(DEVBLK *dev);
int cckd_read_l2ent(DEVBLK *dev, CCKD_L2ENT *l2, int trk);
int cckd_write_l2ent(DEVBLK *dev, CCKD_L2ENT *l2, int trk);
int cckd_read_trkimg(DEVBLK *dev, BYTE *buf, int trk, BYTE *unitstat);
int cckd_write_trkimg(DEVBLK *dev, BYTE *buf, int len, int trk, int flags);
int cckd_harden(DEVBLK *dev);
int cckd_trklen(DEVBLK *dev, BYTE *buf);
int cckd_null_trk(DEVBLK *dev, BYTE *buf, int trk, int nullfmt);
int cckd_check_null_trk (DEVBLK *dev, BYTE *buf, int trk, int len);
int cckd_cchh(DEVBLK *dev, BYTE *buf, int trk);
int cckd_validate(DEVBLK *dev, BYTE *buf, int trk, int len);
char *cckd_sf_name(DEVBLK *dev, int sfx);
int cckd_sf_init(DEVBLK *dev);
int cckd_sf_new(DEVBLK *dev);
DLL_EXPORT void *cckd_sf_add(void *data);
DLL_EXPORT void *cckd_sf_remove(void *data);
DLL_EXPORT void *cckd_sf_comp(void *data);
DLL_EXPORT void *cckd_sf_chk(void *data);
DLL_EXPORT void *cckd_sf_stats(void *data);
#ifdef OPTION_SYNCIO
int cckd_disable_syncio(DEVBLK *dev);
#endif // OPTION_SYNCIO
void cckd_lock_devchain(int flag);
void cckd_unlock_devchain();
void* cckd_gcol(void* arg);
int cckd_gc_percolate(DEVBLK *dev, unsigned int size);
int cckd_gc_l2(DEVBLK *dev, BYTE *buf);
DEVBLK *cckd_find_device_by_devnum (U16 devnum);
BYTE *cckd_uncompress(DEVBLK *dev, BYTE *from, int len, int maxlen, int trk);
int cckd_uncompress_zlib(DEVBLK *dev, BYTE *to, BYTE *from, int len, int maxlen);
int cckd_uncompress_bzip2(DEVBLK *dev, BYTE *to, BYTE *from, int len, int maxlen);
int cckd_compress(DEVBLK *dev, BYTE **to, BYTE *from, int len, int comp, int parm);
int cckd_compress_none(DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm);
int cckd_compress_zlib(DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm);
int cckd_compress_bzip2(DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm);
void cckd_command_help();
void cckd_command_opts();
void cckd_command_stats();
void cckd_command_debug();
int cckd_command(char *op, int cmd);
DLL_EXPORT void cckd_print_itrace();
void cckd_trace(DEVBLK *dev, char *msg, ...);
/*-------------------------------------------------------------------*/
/* Definitions for sense data format codes and message codes */
/*-------------------------------------------------------------------*/
#define FORMAT_0 0 /* Program or System Checks */
#define FORMAT_1 1 /* Device Equipment Checks */
#define FORMAT_2 2 /* 3990 Equipment Checks */
#define FORMAT_3 3 /* 3990 Control Checks */
#define FORMAT_4 4 /* Data Checks */
#define FORMAT_5 5 /* Data Check + Displacement */
#define FORMAT_6 6 /* Usage Stats/Overrun Errors*/
#define FORMAT_7 7 /* Device Control Checks */
#define FORMAT_8 8 /* Device Equipment Checks */
#define FORMAT_9 9 /* Device Rd/Wrt/Seek Checks */
#define FORMAT_F 15 /* Cache Storage Checks */
#define MESSAGE_0 0 /* Message 0 */
#define MESSAGE_1 1 /* Message 1 */
#define MESSAGE_2 2 /* Message 2 */
#define MESSAGE_3 3 /* Message 3 */
#define MESSAGE_4 4 /* Message 4 */
#define MESSAGE_5 5 /* Message 5 */
#define MESSAGE_6 6 /* Message 6 */
#define MESSAGE_7 7 /* Message 7 */
#define MESSAGE_8 8 /* Message 8 */
#define MESSAGE_9 9 /* Message 9 */
#define MESSAGE_A 10 /* Message A */
#define MESSAGE_B 11 /* Message B */
#define MESSAGE_C 12 /* Message C */
#define MESSAGE_D 13 /* Message D */
#define MESSAGE_E 14 /* Message E */
#define MESSAGE_F 15 /* Message F */
/*-------------------------------------------------------------------*/
/* Data areas */
/*-------------------------------------------------------------------*/
static CCKD_L2ENT empty_l2[CKDDASD_NULLTRK_FMTMAX+1][256];
static BYTE eighthexFF[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
DEVHND cckddasd_device_hndinfo;
DEVHND cfbadasd_device_hndinfo;
DLL_EXPORT CCKDBLK cckdblk; /* cckd global area */
/*-------------------------------------------------------------------*/
/* CCKD global initialization */
/*-------------------------------------------------------------------*/
int cckddasd_init (int argc, BYTE *argv[])
{
int i, j; /* Loop indexes */
UNREFERENCED(argc);
UNREFERENCED(argv);
if (memcmp (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id)) == 0)
return 0;
/* Clear the cckdblk */
memset(&cckdblk, 0, sizeof(CCKDBLK));
/* Initialize locks and conditions */
memcpy (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id));
initialize_lock (&cckdblk.gclock);
initialize_lock (&cckdblk.ralock);
initialize_lock (&cckdblk.wrlock);
initialize_lock (&cckdblk.devlock);
initialize_condition (&cckdblk.gccond);
initialize_condition (&cckdblk.racond);
initialize_condition (&cckdblk.wrcond);
initialize_condition (&cckdblk.devcond);
initialize_condition (&cckdblk.termcond);
/* Initialize some variables */
cckdblk.wrprio = 16;
cckdblk.ranbr = CCKD_DEFAULT_RA_SIZE;
cckdblk.ramax = CCKD_DEFAULT_RA;
cckdblk.wrmax = CCKD_DEFAULT_WRITER;
cckdblk.gcmax = CCKD_DEFAULT_GCOL;
cckdblk.gcwait = CCKD_DEFAULT_GCOLWAIT;
cckdblk.gcparm = CCKD_DEFAULT_GCOLPARM;
cckdblk.readaheads = CCKD_DEFAULT_READAHEADS;
cckdblk.freepend = CCKD_DEFAULT_FREEPEND;
#ifdef HAVE_LIBZ
cckdblk.comps |= CCKD_COMPRESS_ZLIB;
#endif
#ifdef CCKD_BZIP2
cckdblk.comps |= CCKD_COMPRESS_BZIP2;
#endif
cckdblk.comp = 0xff;
cckdblk.compparm = -1;
/* Initialize the readahead queue */
cckdblk.ra1st = cckdblk.ralast = -1;
cckdblk.rafree = 0;
for (i = 0; i < cckdblk.ranbr; i++) cckdblk.ra[i].next = i + 1;
cckdblk.ra[cckdblk.ranbr - 1].next = -1;
/* Clear the empty L2 tables */
for (i = 0; i <= CKDDASD_NULLTRK_FMTMAX; i++)
for (j = 0; j < 256; j++)
{
empty_l2[i][j].pos = 0;
empty_l2[i][j].len = empty_l2[i][j].size = i;
}
return 0;
} /* end function cckddasd_init */
/*-------------------------------------------------------------------*/
/* CCKD dasd global termination */
/*-------------------------------------------------------------------*/
int cckddasd_term ()
{
/* Terminate the readahead threads */
obtain_lock (&cckdblk.ralock);
cckdblk.ramax = 0;
if (cckdblk.ras)
{
broadcast_condition (&cckdblk.racond);
wait_condition (&cckdblk.termcond, &cckdblk.ralock);
}
release_lock (&cckdblk.ralock);
destroy_lock (&cckdblk.ralock);
destroy_condition (&cckdblk.racond);
/* Terminate the garbage collection threads */
obtain_lock (&cckdblk.gclock);
cckdblk.gcmax = 0;
if (cckdblk.gcs)
{
broadcast_condition (&cckdblk.gccond);
wait_condition (&cckdblk.termcond, &cckdblk.gclock);
}
release_lock (&cckdblk.gclock);
destroy_lock (&cckdblk.gclock);
destroy_condition (&cckdblk.gccond);
/* Terminate the writer threads */
obtain_lock (&cckdblk.wrlock);
cckdblk.wrmax = 0;
if (cckdblk.wrs)
{
broadcast_condition (&cckdblk.wrcond);
wait_condition (&cckdblk.termcond, &cckdblk.wrlock);
}
release_lock (&cckdblk.wrlock);
destroy_lock (&cckdblk.wrlock);
destroy_condition (&cckdblk.wrcond);
destroy_lock (&cckdblk.devlock);
destroy_condition (&cckdblk.devcond);
destroy_condition (&cckdblk.termcond);
memset(&cckdblk, 0, sizeof(CCKDBLK));
return 0;
} /* end function cckddasd_term */
/*-------------------------------------------------------------------*/
/* CKD dasd initialization */
/*-------------------------------------------------------------------*/
int cckddasd_init_handler ( DEVBLK *dev, int argc, char *argv[] )
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
DEVBLK *dev2; /* -> device in cckd queue */
int i; /* Counter */
int fdflags; /* File flags */
UNREFERENCED(argc);
UNREFERENCED(argv);
/* Initialize the global cckd block if necessary */
if (memcmp (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id)))
cckddasd_init (0, NULL);
/* Obtain area for cckd extension */
dev->cckd_ext = cckd = cckd_calloc (dev, "ext", 1, sizeof(CCKDDASD_EXT));
if (cckd == NULL)
return -1;
/* Initialize locks and conditions */
initialize_lock (&cckd->cckdiolock);
initialize_lock (&cckd->filelock);
initialize_condition (&cckd->cckdiocond);
/* Initialize some variables */
obtain_lock (&cckd->filelock);
cckd->l1x = cckd->sfx = cckd->l2active = -1;
dev->cache = cckd->free1st = -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->maxsize = sizeof(off_t) > 4 ? 0xffffffffll : 0x7fffffffll;
/* 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_BLOCK_SIZE;
/* open the shadow files */
if (cckd_sf_init (dev) < 0)
{
WRMSG (HHC00300, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
return -1;
}
/* Update the device handler routines */
if (cckd->ckddasd)
dev->hnd = &cckddasd_device_hndinfo;
else
dev->hnd = &cfbadasd_device_hndinfo;
release_lock (&cckd->filelock);
/* Insert the device into the cckd device queue */
cckd_lock_devchain(1);
for (cckd = NULL, dev2 = cckdblk.dev1st; dev2; dev2 = cckd->devnext)
cckd = dev2->cckd_ext;
if (cckd) cckd->devnext = dev;
else cckdblk.dev1st = dev;
cckd_unlock_devchain();
cckdblk.batch = dev->batch;
if (cckdblk.batch)
{
cckdblk.nostress = 1;
cckdblk.freepend = 0;
cckdblk.linuxnull = 1;
}
return 0;
} /* end function cckddasd_init_handler */
/*-------------------------------------------------------------------*/
/* Close a Compressed CKD Device */
/*-------------------------------------------------------------------*/
int cckddasd_close_device (DEVBLK *dev)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int i; /* Index */
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++;
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
cckd->cckdwaiters--;
cckd_flush_cache (dev);
}
broadcast_condition (&cckd->cckdiocond);
cckd_purge_cache (dev); cckd_purge_l2 (dev);
dev->bufcur = 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);
if (dev == cckdblk.dev1st) cckdblk.dev1st = cckd->devnext;
else
{
DEVBLK *dev2 = cckdblk.dev1st;
CCKDDASD_EXT *cckd2 = dev2->cckd_ext;
while (cckd2->devnext != dev)
{
dev2 = cckd2->devnext; cckd2 = dev2->cckd_ext;
}
cckd2->devnext = cckd->devnext;
}
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->l1[i] = cckd_free (dev, "l1", cckd->l1[i]);
/* reset the device handler */
if (cckd->ckddasd)
dev->hnd = &ckddasd_device_hndinfo;
else
dev->hnd = &fbadasd_device_hndinfo;
/* write some statistics */
if (!dev->batch)
cckd_sf_stats (dev);
release_lock (&cckd->filelock);
/* free the cckd extension */
dev->cckd_ext= cckd_free (dev, "ext", cckd);
if (dev->dasdsfn) free (dev->dasdsfn);
dev->dasdsfn = NULL;
close (dev->fd);
dev->fd = -1;
/* If no more devices then perform global termination */
if (cckdblk.dev1st == NULL) cckddasd_term ();
return 0;
} /* end function cckddasd_close_device */
/*-------------------------------------------------------------------*/
/* Compressed ckd start/resume channel program */
/*-------------------------------------------------------------------*/
void cckddasd_start (DEVBLK *dev)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
U16 devnum = 0; /* Last active device number */
int trk = 0; /* Last active track */
cckd = dev->cckd_ext;
cckd_trace (dev, "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_BLOCK_SIZE;
#ifdef OPTION_SYNCIO
/* Check for merge - synchronous i/o should be disabled */
#else // OPTION_NOSYNCIO
/* Check for merge */
#endif // OPTION_SYNCIO
obtain_lock(&cckd->cckdiolock);
if (cckd->merging)
{
cckd_trace (dev, "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 cckddasd_start */
/*-------------------------------------------------------------------*/
/* Compressed ckd end/suspend channel program */
/*-------------------------------------------------------------------*/
void cckddasd_end (DEVBLK *dev)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
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 (dev, "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 cckddasd_end */
/*-------------------------------------------------------------------*/
/* Open a cckd file */
/* */
/* If O_CREAT is not set and mode is non-zero then the error message */
/* will be supressed. */
/*-------------------------------------------------------------------*/
int cckd_open (DEVBLK *dev, int sfx, int flags, mode_t mode)
{
CCKDDASD_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", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx),
"open()", strerror(errno));
cckd_trace (dev, "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 (dev, "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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc = 0; /* Return code */
cckd = dev->cckd_ext;
cckd_trace (dev, "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", SSID_TO_LCSS(dev->ssid), dev->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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
cckd = dev->cckd_ext;
cckd_trace (dev, "file[%d] fd[%d] read, off 0x"I64_FMTx" len %d",
sfx, cckd->fd[sfx], off, len);
/* Seek to specified offset */
if (lseek (cckd->fd[sfx], off, SEEK_SET) < 0)
{
WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->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)
WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->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);
WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc = 0; /* Return code */
cckd = dev->cckd_ext;
cckd_trace (dev, "file[%d] fd[%d] write, off 0x"I64_FMTx" len %d",
sfx, cckd->fd[sfx], off, len);
/* Seek to specified offset */
if (lseek (cckd->fd[sfx], off, SEEK_SET) < 0)
{
WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx),
"lseek()", off, strerror(errno));
return -1;
}
/* Write the data */
rc = write (cckd->fd[sfx], buf, len);
if (rc < (int)len)
{
if (rc < 0)
WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->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);
WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
cckd = dev->cckd_ext;
cckd_trace (dev, "file[%d] fd[%d] ftruncate, off 0x"I64_FMTx,
sfx, cckd->fd[sfx], off);
/* Truncate the file */
if (ftruncate (cckd->fd[sfx], off) < 0)
{
WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->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; /* Pointer */
p = malloc (size);
cckd_trace (dev, "%s malloc %p len %ld", id, p, (long)size);
if (p == NULL)
{
char buf[64];
MSGBUF( buf, "malloc(%d)", (int)size);
WRMSG (HHC00303, "E", dev ? SSID_TO_LCSS(dev->ssid) : 0, dev ? dev->devnum : 0, buf, strerror(errno));
cckd_print_itrace ();
}
return p;
} /* end function cckd_malloc */
/*-------------------------------------------------------------------*/
/* calloc */
/*-------------------------------------------------------------------*/
void *cckd_calloc (DEVBLK *dev, char *id, size_t n, size_t size)
{
void *p; /* Pointer */
p = calloc (n, size);
cckd_trace (dev, "%s calloc %p len %ld", id, p, n*(long)size);
if (p == NULL)
{
char buf[64];
MSGBUF( buf, "calloc(%d, %d)", (int)n, (int)size);
WRMSG (HHC00303, "E", dev ? SSID_TO_LCSS(dev->ssid) : 0, dev ? dev->devnum : 0, buf, strerror(errno));
cckd_print_itrace ();
}
return p;
} /* end function cckd_calloc */
/*-------------------------------------------------------------------*/
/* free */
/*-------------------------------------------------------------------*/
void *cckd_free (DEVBLK *dev, char *id, void *p)
{
cckd_trace (dev, "%s free %p", id, p);
if (p) free (p);
return (void*)NULL;
} /* end function cckd_free */
/*-------------------------------------------------------------------*/
/* Compressed ckd read track image */
/*-------------------------------------------------------------------*/
int cckd_read_track (DEVBLK *dev, int trk, BYTE *unitstat)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int len; /* Compressed length */
BYTE *newbuf; /* Uncompressed buffer */
int cache; /* New active cache entry */
#ifdef OPTION_SYNCIO
int syncio; /* Syncio indicator */
#endif // OPTION_SYNCIO
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);
}
#ifdef OPTION_SYNCIO
/* Turn off the synchronous I/O bit if trk overflow or trk 0 */
syncio = dev->syncio_active;
if (dev->ckdtrkof || trk == 0)
dev->syncio_active = 0;
#endif // OPTION_SYNCIO
/* 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)
{
#ifdef OPTION_SYNCIO
#if 0
/* Return if synchronous i/o */
if (dev->syncio_active)
{
cckd_trace (dev, "read trk %d syncio compressed", trk);
cckdblk.stats_synciomisses++;
dev->syncio_retry = 1;
return -1;
}
#endif
#endif // OPTION_SYNCIO
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;
#ifdef OPTION_SYNCIO
dev->syncio_active = syncio;
#endif // OPTION_SYNCIO
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 (dev, "read trk %d uncompressed len %d",
trk, dev->buflen);
}
dev->comp = dev->buf[0] & CCKD_COMPRESS_MASK;
if (dev->comp != 0) dev->compoff = CKDDASD_TRKHDR_SIZE;
return 0;
}
cckd_trace (dev, "read trk %d (%s)", trk,
#ifdef OPTION_SYNCIO
dev->syncio_active ? "synchronous" :
#endif // OPTION_SYNCIO
"asynchronous");
/* read the new track */
dev->bufupd = 0;
*unitstat = 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 = CKDDASD_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;
#ifdef OPTION_SYNCIO
dev->syncio_active = syncio;
#endif // OPTION_SYNCIO
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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
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 (dev, "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);
}
return len;
} /* end function cckd_update_track */
/*-------------------------------------------------------------------*/
/* Return used cylinders */
/*-------------------------------------------------------------------*/
int cckd_used (DEVBLK *dev)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int l1x, l2x; /* Lookup table indexes */
int sfx; /* Shadow file suffix */
CCKD_L2ENT l2; /* Copied level 2 entry */
cckd = dev->cckd_ext;
obtain_lock (&cckd->filelock);
/* Find the last used level 1 table entry */
for (l1x = cckd->cdevhdr[0].numl1tab - 1; l1x > 0; l1x--)
{
sfx = cckd->sfn;
while (cckd->l1[sfx][l1x] == 0xffffffff && sfx > 0) sfx--;
if (cckd->l1[sfx][l1x]) break;
}
/* Find the last used level 2 table entry */
for (l2x = 255; l2x >= 0; l2x--)
{
rc = cckd_read_l2ent (dev, &l2, l1x * 256 + l2x);
if (rc < 0 || l2.pos != 0) break;
}
release_lock (&cckd->filelock);
return (l1x * 256 + l2x + dev->ckdheads) / dev->ckdheads;
}
/*-------------------------------------------------------------------*/
/* Compressed fba read block(s) */
/*-------------------------------------------------------------------*/
int cfba_read_block (DEVBLK *dev, int blkgrp, BYTE *unitstat)
{
CCKDDASD_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_BLOCK_SIZE + CKDDASD_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)
{
#ifdef OPTION_SYNCIO
#if 0
/* Return if synchronous i/o */
if (dev->syncio_active)
{
cckd_trace (dev, "read blkgrp %d syncio compressed",
blkgrp);
cckdblk.stats_synciomisses++;
dev->syncio_retry = 1;
return -1;
}
#endif
#endif // OPTION_SYNCIO
len = cache_getval(CACHE_DEVBUF, dev->cache) + CKDDASD_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 + CKDDASD_TRKHDR_SIZE;
dev->buflen = CFBA_BLOCK_SIZE;
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
dev->bufupd = 0;
cckd_trace (dev, "read bkgrp %d uncompressed len %d",
blkgrp, dev->buflen);
}
dev->comp = cbuf[0] & CCKD_COMPRESS_MASK;
return 0;
}
cckd_trace (dev, "read blkgrp %d (%s)", blkgrp,
#ifdef OPTION_SYNCIO
dev->syncio_active ? "synchronous" :
#endif // OPTION_SYNCIO
"asynchronous");
/* Read the new blkgrp */
dev->bufupd = 0;
*unitstat = 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 + CKDDASD_TRKHDR_SIZE;
dev->bufcur = blkgrp;
dev->bufoff = 0;
dev->bufoffhi = CFBA_BLOCK_SIZE;
dev->buflen = CFBA_BLOCK_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)
{
CCKDDASD_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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int l1x, l2x; /* Lookup table indexes */
int sfx; /* Shadow file suffix */
CCKD_L2ENT l2; /* Copied level 2 entry */
cckd = dev->cckd_ext;
obtain_lock (&cckd->filelock);
/* Find the last used level 1 table entry */
for (l1x = cckd->cdevhdr[0].numl1tab - 1; l1x > 0; l1x--)
{
sfx = cckd->sfn;
while (cckd->l1[sfx][l1x] == 0xffffffff && sfx > 0) sfx--;
if (cckd->l1[sfx][l1x]) break;
}
/* Find the last used level 2 table entry */
for (l2x = 255; l2x >= 0; l2x--)
{
rc = cckd_read_l2ent (dev, &l2, l1x * 256 + l2x);
if (rc < 0 || l2.pos != 0) break;
}
release_lock (&cckd->filelock);
return (l1x * 256 + l2x + CFBA_BLOCK_NUM) / CFBA_BLOCK_NUM;
}
/*-------------------------------------------------------------------*/
/* 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)
{
CCKDDASD_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 */
cckd = dev->cckd_ext;
cckd_trace (dev, "%d rdtrk %d", ra, trk);
maxlen = cckd->ckddasd ? dev->ckdtrksz
: CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_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;
}
#ifdef OPTION_SYNCIO
/* If synchronous I/O and I/O is active then return
with syncio_retry bit on */
if (dev->syncio_active)
{
if (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_IOBUSY)
{
cckd_trace (dev, "%d rdtrk[%d] %d syncio %s", ra, fnd, trk,
cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_READING ?
"reading" : "writing");
cckdblk.stats_synciomisses++;
dev->syncio_retry = 1;
cache_unlock (CACHE_DEVBUF);
release_lock (&cckd->cckdiolock);
return -1;
}
else cckdblk.stats_syncios++;
}
#endif // OPTION_SYNCIO
/* 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 (dev, "%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 (dev, "%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 (dev, "%d rdtrk[%d] %d io wait complete",
ra, fnd, trk);
}
release_lock (&cckd->cckdiolock);
/* Asynchrously schedule readaheads */
if (curtrk > 0 && trk > curtrk && trk <= curtrk + 2)
cckd_readahead (dev, trk);
return fnd;
} /* cache hit */
#ifdef OPTION_SYNCIO
/* If not readahead and synchronous I/O then retry */
if (!ra && dev->syncio_active)
{
cache_unlock(CACHE_DEVBUF);
release_lock (&cckd->cckdiolock);
cckd_trace (dev, "%d rdtrk[%d] %d syncio cache miss", ra, lru, trk);
cckdblk.stats_synciomisses++;
dev->syncio_retry = 1;
return -1;
}
#endif // OPTION_SYNCIO
cckd_trace (dev, "%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 (dev, "%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 (dev, "%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 (dev, "%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 (dev, "%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 (dev, "%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)
{
CCKDDASD_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].next)
if (cckdblk.ra[r].dev == dev)
{
i = cckdblk.ra[r].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].next;
if (cckdblk.ralast < 0)
{
cckdblk.ra1st = cckdblk.ralast = r;
cckdblk.ra[r].prev = cckdblk.ra[r].next = -1;
}
else
{
cckdblk.ra[cckdblk.ralast].next = r;
cckdblk.ra[r].prev = cckdblk.ralast;
cckdblk.ra[r].next = -1;
cckdblk.ralast = r;
}
cckdblk.ra[r].trk = trk + i;
cckdblk.ra[r].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)
{
rc = create_thread (&tid, JOINABLE, cckd_ra, NULL, "cckd_ra");
if (rc)
WRMSG(HHC00102, "E", strerror(rc));
}
}
release_lock (&cckdblk.ralock);
} /* end function cckd_readahead */
int cckd_readahead_scan (int *answer, int ix, int i, void *data)
{
CCKDDASD_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)
{
CCKDDASD_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;
UNREFERENCED( arg );
obtain_lock (&cckdblk.ralock);
ra = ++cckdblk.ras;
/* Return without messages if too many already started */
if (ra > cckdblk.ramax)
{
--cckdblk.ras;
release_lock (&cckdblk.ralock);
return NULL;
}
if (!cckdblk.batch)
{
MSGBUF( threadname, "Read-ahead thread-%d", ra);
WRMSG (HHC00100, "I", thread_id(), get_thread_priority(0), threadname);
}
while (ra <= cckdblk.ramax)
{
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].trk;
dev = cckdblk.ra[r].dev;
cckd = dev->cckd_ext;
/* Requeue the 1st entry to the readahead free queue */
cckdblk.ra1st = cckdblk.ra[r].next;
if (cckdblk.ra[r].next > -1)
cckdblk.ra[cckdblk.ra[r].next].prev = -1;
else cckdblk.ralast = -1;
cckdblk.ra[r].next = 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)
{
rc = create_thread (&tid, JOINABLE, cckd_ra, dev, "cckd_ra");
if (rc)
WRMSG(HHC00102, "E", strerror(rc));
}
}
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)
WRMSG (HHC00101, "I", thread_id(), get_thread_priority(0), threadname);
--cckdblk.ras;
if (!cckdblk.ras) signal_condition(&cckdblk.termcond);
release_lock(&cckdblk.ralock);
return NULL;
} /* end thread cckd_ra_thread */
/*-------------------------------------------------------------------*/
/* 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 */
/* Scan cache for updated cache entries */
obtain_lock (&cckdblk.wrlock);
cache_lock (CACHE_DEVBUF);
//BHe: rc is never used?
rc = 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)
{
rc = create_thread (&tid, JOINABLE, cckd_writer, NULL, "cckd_writer");
if (rc)
WRMSG(HHC00102, "E", strerror(rc));
}
}
release_lock (&cckdblk.wrlock);
}
int cckd_flush_cache_scan (int *answer, int ix, int i, void *data)
{
CCKDDASD_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 (dev, "flush file[%d] cache[%d] %4.4X trk %d",
cckd->sfn, i, devnum, trk);
}
return 0;
}
void cckd_flush_cache_all()
{
CCKDDASD_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)
{
/* 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 (dev, "purge cache[%d] %4.4X trk %d purged",
i, devnum, trk);
}
return 0;
}
/*-------------------------------------------------------------------*/
/* Writer thread */
/*-------------------------------------------------------------------*/
void* cckd_writer(void *arg)
{
DEVBLK *dev; /* Device block */
CCKDDASD_EXT *cckd; /* -> cckd extension */
int writer; /* Writer identifier */
int o; /* Cache entry found */
U16 devnum; /* Device number */
BYTE *buf; /* Buffer */
BYTE *bufp; /* Buffer to be written */
int len, bufl; /* Buffer lengths */
int trk; /* Track number */
int comp; /* Compression algorithm */
int parm; /* Compression parameter */
TID tid; /* Writer thead id */
U32 flag; /* Cache flag */
static char *compress[] = {"none", "zlib", "bzip2"};
BYTE buf2[65536]; /* Compress buffer */
char threadname[40];
int rc;
UNREFERENCED(arg);
#ifndef WIN32
/* Set writer priority just below cpu priority to mimimize the
compression effect */
if(cckdblk.wrprio >= 0)
set_thread_priority(0, cckdblk.wrprio);
#endif
obtain_lock (&cckdblk.wrlock);
writer = ++cckdblk.wrs;
/* Return without messages if too many already started */
if (writer > cckdblk.wrmax)
{
--cckdblk.wrs;
release_lock (&cckdblk.wrlock);
return NULL;
}
if (!cckdblk.batch)
{
MSGBUF( threadname, "Writer thread-%d", writer);
WRMSG ( HHC00100, "I", thread_id(), get_thread_priority(0), threadname);
}
while (writer <= cckdblk.wrmax || cckdblk.wrpending)
{
/* Wait for work */
if (cckdblk.wrpending == 0)
{
cckdblk.wrwaiting++;
wait_condition (&cckdblk.wrcond, &cckdblk.wrlock);
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;
}
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)
{
rc = create_thread (&tid, JOINABLE, cckd_writer, NULL, "cckd_writer");
if (rc)
WRMSG(HHC00102, "E", strerror(rc));
}
}
release_lock (&cckdblk.wrlock);
/* Prepare to compress */
CCKD_CACHE_GETKEY(o, devnum, trk);
dev = cckd_find_device_by_devnum (devnum);
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].compress
: cckdblk.comp;
parm = cckdblk.compparm < 0
? cckd->cdevhdr[cckd->sfn].compress_parm
: cckdblk.compparm;
cckd_trace (dev, "%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)) > CKDDASD_NULLTRK_FMTMAX)
{
/* Stress adjustments */
if ((cache_waiters(CACHE_DEVBUF) || cache_busy(CACHE_DEVBUF) > 90)
&& !cckdblk.nostress)
{
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 (dev, "%d wrtrk[%d] %d comp %s parm %d",
writer, o, trk, compress[comp], parm);
bufp = (BYTE *)&buf2;
bufl = cckd_compress(dev, &bufp, buf, len, comp, parm);
cckd_trace (dev, "%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].options & CCKD_OPENED))
{
cckd->cdevhdr[cckd->sfn].options |= (CCKD_OPENED | CCKD_ORDWR);
cckd_write_chdr (dev);
}
/* Write the track image */
cckd_write_trkimg (dev, bufp, bufl, trk, CCKD_SIZE_ANY);
release_lock (&cckd->filelock);
/* Schedule the garbage collector */
if (cckdblk.gcs < cckdblk.gcmax)
{
rc = create_thread (&tid, JOINABLE, cckd_gcol, NULL, "cckd_gcol");
if (rc)
WRMSG(HHC00102, "E", strerror(rc));
}
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 (cckd->cckdwaiters && ((flag & CCKD_CACHE_IOWAIT) || !cckd->wrpending))
{ cckd_trace (dev, "writer[%d] cache[%2.2d] %d signalling write complete",
writer, o, trk);
broadcast_condition (&cckd->cckdiocond);
}
release_lock(&cckd->cckdiolock);
cckd_trace (dev, "%d wrtrk[%2.2d] %d complete flags:%8.8x",
writer, o, trk, cache_getflag(CACHE_DEVBUF,o));
obtain_lock(&cckdblk.wrlock);
}
if (!cckdblk.batch)
WRMSG (HHC00101, "I", thread_id(), get_thread_priority(0), threadname);
cckdblk.wrs--;
if (cckdblk.wrs == 0) signal_condition(&cckdblk.termcond);
release_lock(&cckdblk.wrlock);
return NULL;
} /* end thread cckd_writer */
int cckd_writer_scan (int *o, int ix, int i, void *data)
{
UNREFERENCED(data);
if ((cache_getflag(ix,i) & DEVBUF_TYPE_COMP)
&& (cache_getflag(ix,i) & CCKD_CACHE_WRITE)
&& (*o == -1 || cache_getage(ix, i) < cache_getage(ix, *o)))
*o = i;
return 0;
}
/*-------------------------------------------------------------------*/
/* Debug routine for checking the free space array */
/*-------------------------------------------------------------------*/
void cckd_chk_space(DEVBLK *dev)
{
#if 1
CCKDDASD_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;
cckd = dev->cckd_ext;
sfx = cckd->sfn;
p = -1;
fpos = cckd->cdevhdr[sfx].free;
for (i = cckd->free1st; i >= 0; i = cckd->free[i].next)
{
n++; total += cckd->free[i].len;
if (n > cckd->freenbr) break;
if (cckd->free[i].prev != p)
err = 1;
if (cckd->free[i].next >= 0)
{
if (fpos + cckd->free[i].len > cckd->free[i].pos)
err = 1;
}
else
{
if (fpos + cckd->free[i].len > cckd->cdevhdr[sfx].size)
err = 1;
}
if (cckd->free[i].pending == 0 && cckd->free[i].len > largest)
largest = cckd->free[i].len;
fpos = cckd->free[i].pos;
p = i;
}
if (err
|| (cckd->cdevhdr[sfx].free != 0 && cckd->cdevhdr[sfx].free_number == 0)
|| (cckd->cdevhdr[sfx].free == 0 && cckd->cdevhdr[sfx].free_number != 0)
|| (n != cckd->cdevhdr[sfx].free_number)
|| (total != cckd->cdevhdr[sfx].free_total - cckd->cdevhdr[sfx].free_imbed)
|| (cckd->freelast != p)
|| (largest != cckd->cdevhdr[sfx].free_largest)
)
{
cckd_trace (dev, "cdevhdr[%d] size %10d used %10d free 0x%8.8x",
sfx,cckd->cdevhdr[sfx].size,cckd->cdevhdr[sfx].used,
cckd->cdevhdr[sfx].free);
cckd_trace (dev, " nbr %10d total %10d imbed %10d largest %10d",
cckd->cdevhdr[sfx].free_number,
cckd->cdevhdr[sfx].free_total,cckd->cdevhdr[sfx].free_imbed,
cckd->cdevhdr[sfx].free_largest);
cckd_trace (dev, "free %p nbr %d 1st %d last %d avail %d",
cckd->free,cckd->freenbr,cckd->free1st,
cckd->freelast,cckd->freeavail);
cckd_trace (dev, "found nbr %d total %ld largest %ld",n,(long)total,(long)largest);
fpos = cckd->cdevhdr[sfx].free;
for (n = 0, i = cckd->free1st; i >= 0; i = cckd->free[i].next)
{
if (++n > cckd->freenbr) break;
cckd_trace (dev, "%4d: [%4d] prev[%4d] next[%4d] pos "I64_FMTx" len %8d "I64_FMTx" pend %d",
n, i, cckd->free[i].prev, cckd->free[i].next,
fpos, cckd->free[i].len,
fpos + cckd->free[i].len, cckd->free[i].pending);
fpos = cckd->free[i].pos;
}
cckd_print_itrace();
}
#endif
} /* end function cckd_chk_space */
/*-------------------------------------------------------------------*/
/* Get file space */
/*-------------------------------------------------------------------*/
off_t cckd_get_space(DEVBLK *dev, int *size, int flags)
{
CCKDDASD_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 */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
len = *size;
if (flags & CCKD_L2SPACE)
{
flags |= CCKD_SIZE_EXACT;
len = *size = CCKD_L2TAB_SIZE;
}
cckd_trace (dev, "get_space len %d largest %d flags 0x%2.2x",
len, cckd->cdevhdr[sfx].free_largest, flags);
if (len <= CKDDASD_NULLTRK_FMTMAX)
return 0;
if (!cckd->free)
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].size;
if ((fpos + len) > cckd->maxsize)
{
WRMSG (HHC00304, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx),
(long long) (cckd->maxsize >> 20) + 1);
return -1;
}
cckd->cdevhdr[sfx].size += len;
cckd->cdevhdr[sfx].used += len;
cckd_trace (dev, "get_space atend 0x"I64_FMTx" len %d",fpos, len);
return fpos;
}
/* Scan free space chain */
fpos = (off_t)cckd->cdevhdr[sfx].free;
for (i = cckd->free1st; i >= 0; i = cckd->free[i].next)
{
if (cckd->free[i].pending == 0
&& (len2 <= (int)cckd->free[i].len || len == (int)cckd->free[i].len)
&& ((flags & CCKD_L2SPACE) || fpos >= cckd->l2bounds))
break;
fpos = (off_t)cckd->free[i].pos;
}
/* This can happen if largest comes before l2bounds */
if (i < 0) goto cckd_get_space_atend;
flen = cckd->free[i].len;
p = cckd->free[i].prev;
n = cckd->free[i].next;
/*
* 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->freemin)
*size = (int)flen;
/* Remove the new space from free space */
if (*size < (int)flen)
{
cckd->free[i].len -= *size;
if (p >= 0)
cckd->free[p].pos += *size;
else
cckd->cdevhdr[sfx].free += *size;
}
else
{
cckd->cdevhdr[sfx].free_number--;
/* Remove the free space entry from the chain */
if (p >= 0)
{
cckd->free[p].pos = cckd->free[i].pos;
cckd->free[p].next = n;
}
else
{
cckd->cdevhdr[sfx].free = cckd->free[i].pos;
cckd->free1st = n;
}
if (n >= 0)
cckd->free[n].prev = p;
else
cckd->freelast = p;
/* Add entry to the available queue */
cckd->free[i].next = cckd->freeavail;
cckd->freeavail = 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->free1st; i >= 0; i = cckd->free[i].next)
if (cckd->free[i].len > cckd->cdevhdr[sfx].free_largest
&& cckd->free[i].pending == 0)
cckd->cdevhdr[sfx].free_largest = cckd->free[i].len;
}
/* Update free space stats */
cckd->cdevhdr[sfx].used += len;
cckd->cdevhdr[sfx].free_total -= len;
cckd->cdevhdr[sfx].free_imbed += *size - len;
cckd_trace (dev, "get_space found 0x"I64_FMTx" 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)
{
CCKDDASD_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 (len <= CKDDASD_NULLTRK_FMTMAX || pos == 0 || pos == 0xffffffff)
return;
cckd = dev->cckd_ext;
sfx = cckd->sfn;
cckd_trace (dev, "rel_space offset 0x"I64_FMTx" len %d size %d",
pos, len, size);
if (!cckd->free) cckd_read_fsp (dev);
// cckd_chk_space(dev);
/* Scan free space chain */
ppos = -1;
npos = cckd->cdevhdr[sfx].free;
for (p = -1, n = cckd->free1st; n >= 0; n = cckd->free[n].next)
{
if (pos < npos) break;
ppos = npos;
npos = cckd->free[n].pos;
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->free[p].len == pos && cckd->free[p].pending == pending)
{
cckd->free[p].len += size;
fsize = cckd->free[p].len;
}
else
{
/* Increase the size of the free space array if necessary */
if (cckd->freeavail < 0)
{
cckd->freeavail = cckd->freenbr;
cckd->freenbr += 1024;
cckd->free = realloc ( cckd->free, cckd->freenbr * CCKD_FREEBLK_ISIZE);
for (i = cckd->freeavail; i < cckd->freenbr; i++)
cckd->free[i].next = i + 1;
cckd->free[i-1].next = -1;
cckd->freemin = CCKD_FREE_MIN_SIZE + ((cckd->freenbr >> 10) * CCKD_FREE_MIN_INCR);
}
/* Get an available free space entry */
i = cckd->freeavail;
cckd->freeavail = cckd->free[i].next;
cckd->cdevhdr[sfx].free_number++;
/* Update the new entry */
cckd->free[i].prev = p;
cckd->free[i].next = n;
cckd->free[i].len = size;
cckd->free[i].pending = pending;
/* Update the previous entry */
if (p >= 0)
{
cckd->free[i].pos = cckd->free[p].pos;
cckd->free[p].pos = pos;
cckd->free[p].next = i;
}
else
{
cckd->free[i].pos = cckd->cdevhdr[sfx].free;
cckd->cdevhdr[sfx].free = pos;
cckd->free1st = i;
}
/* Update the next entry */
if (n >= 0)
cckd->free[n].prev = i;
else
cckd->freelast = i;
}
/* Update the free space statistics */
cckd->cdevhdr[sfx].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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int p,i,n; /* Free free space indexes */
int sfx; /* Shadow file index */
U32 ppos, pos; /* Free space offsets */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
cckd_trace (dev, "flush_space nbr %d",cckd->cdevhdr[sfx].free_number);
/* Make sure the free space chain is built */
if (!cckd->free) cckd_read_fsp (dev);
// cckd_chk_space(dev);
if (cckd->cdevhdr[sfx].free_number == 0 || cckd->cdevhdr[sfx].free == 0)
{
cckd->cdevhdr[sfx].free_number = cckd->cdevhdr[sfx].free = 0;
cckd->free1st = cckd->freelast = cckd->freeavail = -1;
}
pos = cckd->cdevhdr[sfx].free;
ppos = p = -1;
cckd->cdevhdr[sfx].free_number = cckd->cdevhdr[sfx].free_largest = 0;
for (i = cckd->free1st; i >= 0; i = cckd->free[i].next)
{
/* Decrement the pending count */
if (cckd->free[i].pending)
--cckd->free[i].pending;
/* Combine adjacent free spaces */
while (pos + cckd->free[i].len == cckd->free[i].pos)
{
n = cckd->free[i].next;
if (cckd->free[n].pending > cckd->free[i].pending + 1
|| cckd->free[n].pending < cckd->free[i].pending)
break;
cckd->free[i].pos = cckd->free[n].pos;
cckd->free[i].len += cckd->free[n].len;
cckd->free[i].next = cckd->free[n].next;
cckd->free[n].next = cckd->freeavail;
cckd->freeavail = n;
n = cckd->free[i].next;
if (n >= 0)
cckd->free[n].prev = i;
}
ppos = pos;
pos = cckd->free[i].pos;
cckd->cdevhdr[sfx].free_number++;
if (cckd->free[i].len > cckd->cdevhdr[sfx].free_largest
&& !cckd->free[i].pending)
cckd->cdevhdr[sfx].free_largest = cckd->free[i].len;
p = i;
}
cckd->freelast = p;
cckd_trace (dev, "rel_flush_space nbr %d (after merge)",
cckd->cdevhdr[sfx].free_number);
/* If the last free space is at the end of the file then release it */
if (p >= 0 && ppos + cckd->free[p].len == cckd->cdevhdr[sfx].size
&& !cckd->free[p].pending)
{
i = p;
p = cckd->free[i].prev;
cckd_trace (dev, "file[%d] rel_flush_space atend 0x"I64_FMTx" len %d",
sfx, ppos, cckd->free[i].len);
/* Remove the entry from the chain */
if (p >= 0)
{
cckd->free[p].pos = 0;
cckd->free[p].next = -1;
}
else
{
cckd->cdevhdr[sfx].free = 0;
cckd->free1st = -1;
}
cckd->freelast = p;
/* Add the entry to the available chain */
cckd->free[i].next = cckd->freeavail;
cckd->freeavail = i;
/* Update the device header */
cckd->cdevhdr[sfx].size -= cckd->free[i].len;
cckd->cdevhdr[sfx].free_total -= cckd->free[i].len;
cckd->cdevhdr[sfx].free_number--;
if (cckd->free[i].len >= cckd->cdevhdr[sfx].free_largest)
{
cckd->cdevhdr[sfx].free_largest = 0;
for (i = cckd->free1st; i >= 0; i = cckd->free[i].next)
if (cckd->free[i].len > cckd->cdevhdr[sfx].free_largest
&& cckd->free[i].pending == 0)
cckd->cdevhdr[sfx].free_largest = cckd->free[i].len;
}
/* Truncate the file */
cckd_ftruncate (dev, sfx, (off_t)cckd->cdevhdr[sfx].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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
cckd_trace (dev, "file[%d] read_chdr", sfx);
memset(&cckd->cdevhdr[sfx], 0, CCKDDASD_DEVHDR_SIZE);
/* Read the device header */
if (cckd_read (dev, sfx, CKDDASD_DEVHDR_SIZE, &cckd->cdevhdr[sfx], CCKDDASD_DEVHDR_SIZE) < 0)
return -1;
/* Check endian format */
cckd->swapend[sfx] = 0;
if ((cckd->cdevhdr[sfx].options & CCKD_BIGENDIAN) != cckd_endian())
{
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].nullfmt > CKDDASD_NULLTRK_FMTMAX)
cckd->cdevhdr[sfx].nullfmt = 0;
if (cckd->cdevhdr[sfx].nullfmt == 0 && dev->oslinux && dev->devtype == 0x3390)
cckd->cdevhdr[sfx].nullfmt = CKDDASD_NULLTRK_FMT2;
if (cckd->cdevhdr[sfx].nullfmt == CKDDASD_NULLTRK_FMT2)
dev->oslinux = 1;
return 0;
} /* end function cckd_read_chdr */
/*-------------------------------------------------------------------*/
/* Write compressed dasd header */
/*-------------------------------------------------------------------*/
int cckd_write_chdr (DEVBLK *dev)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
cckd_trace (dev, "file[%d] write_chdr", sfx);
/* Set version.release.modlvl */
cckd->cdevhdr[sfx].vrm[0] = CCKD_VERSION;
cckd->cdevhdr[sfx].vrm[1] = CCKD_RELEASE;
cckd->cdevhdr[sfx].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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
int len; /* Length of level 1 table */
int i; /* Work integer */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
cckd_trace (dev, "file[%d] read_l1 offset 0x%"I64_FMT"x",
sfx, (U64)CCKD_L1TAB_POS);
/* Free the old level 1 table if it exists */
cckd->l1[sfx] = cckd_free (dev, "l1", cckd->l1[sfx]);
/* Allocate the level 1 table */
len = cckd->cdevhdr[sfx].numl1tab * CCKD_L1ENT_SIZE;
if ((cckd->l1[sfx] = cckd_malloc (dev, "l1", len)) == NULL)
return -1;
if ( sfx )
memset(cckd->l1[sfx], 0xFF, len);
else
memset(cckd->l1[sfx], 0, len);
/* Read the level 1 table */
if (cckd_read (dev, sfx, CCKD_L1TAB_POS, cckd->l1[sfx], len) < 0)
return -1;
/* Fix endianess */
if (cckd->swapend[sfx])
cckd_swapend_l1 (cckd->l1[sfx], cckd->cdevhdr[sfx].numl1tab);
/* Determine bounds */
cckd->l2bounds = CCKD_L1TAB_POS + len;
for (i = 0; i < cckd->cdevhdr[sfx].numl1tab; i++)
if (cckd->l1[sfx][i] != 0 && cckd->l1[sfx][i] != 0xffffffff)
cckd->l2bounds += CCKD_L2TAB_SIZE;
/* Check if all l2 tables are within bounds */
cckd->l2ok = 1;
for (i = 0; i < cckd->cdevhdr[sfx].numl1tab && cckd->l2ok; i++)
if (cckd->l1[sfx][i] != 0 && cckd->l1[sfx][i] != 0xffffffff)
if (cckd->l1[sfx][i] > cckd->l2bounds - CCKD_L2TAB_SIZE)
cckd->l2ok = 0;
return 0;
} /* end function cckd_read_l1 */
/*-------------------------------------------------------------------*/
/* Write the level 1 table */
/*-------------------------------------------------------------------*/
int cckd_write_l1 (DEVBLK *dev)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
int len; /* Length of level 1 table */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
len = cckd->cdevhdr[sfx].numl1tab * CCKD_L1ENT_SIZE;
cckd_trace (dev, "file[%d] write_l1 0x%"I64_FMT"x len %d",
sfx, (U64)CCKD_L1TAB_POS, len);
if (cckd_write (dev, sfx, CCKD_L1TAB_POS, cckd->l1[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 l1x)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
off_t off; /* Offset to l1 entry */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
off = (off_t)(CCKD_L1TAB_POS + l1x * CCKD_L1ENT_SIZE);
cckd_trace (dev, "file[%d] write_l1ent[%d] , 0x"I64_FMTx,
sfx, l1x, off);
if (cckd_write (dev, sfx, off, &cckd->l1[sfx][l1x], CCKD_L1ENT_SIZE) < 0)
return -1;
return 0;
} /* end function cckd_write_l1ent */
/*-------------------------------------------------------------------*/
/* Initial read */
/*-------------------------------------------------------------------*/
int cckd_read_init (DEVBLK *dev)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
CKDDASD_DEVHDR devhdr; /* Device header */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
cckd_trace (dev, "file[%d] read_init", sfx);
/* Read the device header */
if (cckd_read (dev, sfx, 0, &devhdr, CKDDASD_DEVHDR_SIZE) < 0)
return -1;
/* Check the device hdr */
if (sfx == 0 && memcmp (&devhdr.devid, "CKD_C370", 8) == 0)
cckd->ckddasd = 1;
else if (sfx == 0 && memcmp (&devhdr.devid, "FBA_C370", 8) == 0)
cckd->fbadasd = 1;
else if (!(sfx && memcmp (&devhdr.devid, "CKD_S370", 8) == 0 && cckd->ckddasd)
&& !(sfx && memcmp (&devhdr.devid, "FBA_S370", 8) == 0 && cckd->fbadasd))
{
WRMSG (HHC00305, "E", SSID_TO_LCSS(dev->ssid), dev->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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
off_t fpos; /* Free space offset */
int sfx; /* File index */
int i; /* Index */
CCKD_FREEBLK freeblk; /* First freeblk read */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
cckd_trace (dev, "file[%d] read_fsp number %d",
sfx, cckd->cdevhdr[sfx].free_number);
cckd->free = cckd_free (dev, "free", cckd->free);
cckd->free1st = cckd->freelast = cckd->freeavail = -1;
/* Get storage for the internal free space chain
* in a multiple of 1024 entries
*/
cckd->freenbr = (cckd->cdevhdr[sfx].free_number + 1023) & ~0x3FF;
if (cckd->freenbr)
if ((cckd->free = cckd_calloc (dev, "free", cckd->freenbr, CCKD_FREEBLK_ISIZE)) == NULL)
return -1;
/* Build the doubly linked internal free space chain */
if (cckd->cdevhdr[sfx].free_number)
{
cckd->free1st = 0;
/* Read the first freeblk to determine old/new format */
fpos = (off_t)cckd->cdevhdr[sfx].free;
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;
int n = cckd->cdevhdr[sfx].free_number * 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_number; i++)
{
if (i == 0)
cckd->cdevhdr[sfx].free = fsp[i].pos;
else
cckd->free[i-1].pos = fsp[i].pos;
cckd->free[i].pos = 0;
cckd->free[i].len = fsp[i].len;
cckd->free[i].prev = i - 1;
cckd->free[i].next = i + 1;
}
cckd->free[i-1].next = -1;
cckd->freelast = i-1;
fsp = cckd_free (dev, "fsp", fsp);
/* truncate if new format free space was at the end */
if (ofree == cckd->cdevhdr[sfx].size)
{
fpos = (off_t)cckd->cdevhdr[sfx].size;
cckd_ftruncate(dev, sfx, fpos);
}
} /* new format free space */
else
{
/* old format free space */
fpos = (off_t)cckd->cdevhdr[sfx].free;
for (i = 0; i < cckd->cdevhdr[sfx].free_number; i++)
{
if (cckd_read (dev, sfx, fpos, &cckd->free[i], CCKD_FREEBLK_SIZE) < 0)
return -1;
cckd->free[i].prev = i - 1;
cckd->free[i].next = i + 1;
fpos = (off_t)cckd->free[i].pos;
}
cckd->free[i-1].next = -1;
cckd->freelast = i-1;
} /* old format free space */
} /* if (cckd->cdevhdr[sfx].free_number) */
/* Build singly linked chain of available free space entries */
if (cckd->cdevhdr[sfx].free_number < cckd->freenbr)
{
cckd->freeavail = cckd->cdevhdr[sfx].free_number;
for (i = cckd->freeavail; i < cckd->freenbr; i++)
cckd->free[i].next = i + 1;
cckd->free[i-1].next = -1;
}
/* Set minimum free space size */
cckd->freemin = CCKD_FREE_MIN_SIZE + ((cckd->freenbr >> 10) * CCKD_FREE_MIN_INCR);
return 0;
} /* end function cckd_read_fsp */
/*-------------------------------------------------------------------*/
/* Write the free space */
/*-------------------------------------------------------------------*/
int cckd_write_fsp (DEVBLK *dev)
{
CCKDDASD_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 */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
if (cckd->free == NULL)
return 0;
cckd_trace (dev, "file[%d] write_fsp number %d",
sfx, cckd->cdevhdr[sfx].free_number);
/* 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_number == 0 || cckd->cdevhdr[sfx].free == 0)
{
cckd->cdevhdr[sfx].free_number = cckd->cdevhdr[sfx].free = 0;
cckd->free1st = cckd->freelast = cckd->freeavail = -1;
}
/* Write any free spaces */
if (cckd->cdevhdr[sfx].free)
{
/* size needed for new format free space */
n = (cckd->cdevhdr[sfx].free_number+1) * CCKD_FREEBLK_SIZE;
/* look for existing free space to fit new format free space */
fpos = 0;
for (i = cckd->free1st; i >= 0; i = cckd->free[i].next)
if (n <= (int)cckd->free[i].len)
break;
if (i >= 0)
fpos = cckd->free[i].prev < 0
? (off_t)cckd->cdevhdr[sfx].free
: (off_t)cckd->free[cckd->free[i].prev].pos;
/* if no applicable space see if we can append to the file */
if (fpos == 0 && cckd->maxsize - cckd->cdevhdr[sfx].size >= n)
fpos = (off_t)cckd->cdevhdr[sfx].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;
for (i = cckd->free1st, j = 1; i >= 0; i = cckd->free[i].next)
{
fsp[j].pos = ppos;
fsp[j++].len = cckd->free[i].len;
ppos = cckd->free[i].pos;
}
rc = cckd_write (dev, sfx, fpos, fsp, n);
fsp = cckd_free (dev, "fsp", fsp);
if (rc < 0)
return -1;
cckd->cdevhdr[sfx].free = (U32)fpos;
} /* new format free space */
else
{
/* Old format free space */
for (i = cckd->free1st; i >= 0; i = cckd->free[i].next)
{
if (cckd_write (dev, sfx, fpos, &cckd->free[i], CCKD_FREEBLK_SIZE) < 0)
return -1;
fpos = (off_t)cckd->free[i].pos;
}
} /* old format free space */
} /* if (cckd->cdevhdr[sfx].free) */
/* Free the free space array */
cckd->free = cckd_free (dev, "free", cckd->free);
cckd->freenbr = 0;
cckd->free1st = cckd->freelast = cckd->freeavail = -1;
return 0;
} /* end function cckd_write_fsp */
/*-------------------------------------------------------------------*/
/* Read a new level 2 table */
/*-------------------------------------------------------------------*/
int cckd_read_l2 (DEVBLK *dev, int sfx, int l1x)
{
CCKDDASD_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 */
cckd = dev->cckd_ext;
nullfmt = cckd->cdevhdr[cckd->sfn].nullfmt;
cckd_trace (dev, "file[%d] read_l2 %d active %d %d %d",
sfx, l1x, cckd->sfx, cckd->l1x, cckd->l2active);
/* Return if table is already active */
if (sfx == cckd->sfx && l1x == cckd->l1x) return 0;
cache_lock(CACHE_L2);
/* Inactivate the previous entry */
if (cckd->l2active >= 0)
cache_setflag(CACHE_L2, cckd->l2active, ~L2_CACHE_ACTIVE, 0);
cckd->l2 = NULL;
cckd->l2active = cckd->sfx = cckd->l1x = -1;
/* scan the cache array for the l2tab */
fnd = cache_lookup (CACHE_L2, L2_CACHE_SETKEY(sfx, dev->devnum, l1x), &lru);
/* check for level 2 cache hit */
if (fnd >= 0)
{
cckd_trace (dev, "l2[%d,%d] cache[%d] hit", sfx, l1x, 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->l1x = l1x;
cckd->l2 = cache_getbuf(CACHE_L2, fnd, 0);
cckd->l2active = fnd;
return 1;
}
cckd_trace (dev, "l2[%d,%d] cache[%d] miss", sfx, l1x, 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, l1x));
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->l1[sfx][l1x] == 0)
{
memset(buf, 0, CCKD_L2TAB_SIZE);
if (nullfmt)
for (i = 0; i < 256; i++)
buf[i].len = buf[i].size = nullfmt;
cckd_trace (dev, "l2[%d,%d] cache[%d] null fmt[%d]", sfx, l1x, lru, nullfmt);
}
else if (cckd->l1[sfx][l1x] == 0xffffffff)
{
memset(buf, 0xff, CCKD_L2TAB_SIZE);
cckd_trace (dev, "l2[%d,%d] cache[%d] null 0xff", sfx, l1x, lru);
}
/* Read the new level 2 table */
else
{
off = (off_t)cckd->l1[sfx][l1x];
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 (dev, "file[%d] cache[%d] l2[%d] read offset 0x"I32_FMTx,
sfx, lru, l1x, cckd->l1[sfx][l1x]);
cckd->l2reads[sfx]++;
cckd->totl2reads++;
cckdblk.stats_l2reads++;
}
cckd->sfx = sfx;
cckd->l1x = l1x;
cckd->l2 = buf;
cckd->l2active = lru;
return 0;
} /* end function cckd_read_l2 */
/*-------------------------------------------------------------------*/
/* Purge all l2tab cache entries for a given device */
/*-------------------------------------------------------------------*/
void cckd_purge_l2 (DEVBLK *dev)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
cckd = dev->cckd_ext;
cckd_trace (dev, "purge_l2%s", "");
cache_lock (CACHE_L2);
cckd->l2active = cckd->sfx = cckd->l1x = -1;
cckd->l2 = 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 l1x; /* Cached level 1 index */
DEVBLK *dev = data; /* -> device block */
UNREFERENCED(answer);
L2_CACHE_GETKEY(i, sfx, devnum, l1x);
if (dev == NULL || devnum == dev->devnum)
{
cckd_trace (dev, "purge l2cache[%d] %4.4X sfx %d ix %d purged",
i, devnum, sfx, l1x);
cache_release(ix, i, 0);
}
return 0;
}
/*-------------------------------------------------------------------*/
/* Steal an l2tab cache entry */
/*-------------------------------------------------------------------*/
int cckd_steal_l2 ()
{
DEVBLK *dev; /* -> device block */
CCKDDASD_EXT *cckd; /* -> cckd extension */
int i; /* Stolen cache index */
U16 sfx; /* Cached suffix */
U16 devnum; /* Cached device number */
U32 l1x; /* Cached level 1 index */
i = cache_scan (CACHE_L2, cckd_steal_l2_scan, NULL);
L2_CACHE_GETKEY(i, sfx, devnum, l1x);
dev = cckd_find_device_by_devnum(devnum);
cckd = dev->cckd_ext;
cckd->l2active = cckd->sfx = cckd->l1x = -1;
cckd->l2 = 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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int sfx,l1x; /* 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 */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
l1x = cckd->l1x;
fix = cckd->cdevhdr[sfx].nullfmt;
cckd->l2ok = 0;
cckd_trace (dev, "file[%d] write_l2 %d", sfx, l1x);
if (sfx < 0 || l1x < 0) return -1;
old_off = (off_t)cckd->l1[sfx][l1x];
if (cckd->l1[sfx][l1x] == 0 || cckd->l1[sfx][l1x] == 0xffffffff)
cckd->l2bounds += CCKD_L2TAB_SIZE;
/* Write the L2 table if it's not empty */
if (memcmp(cckd->l2, &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->l2, CCKD_L2TAB_SIZE) < 0)
return -1;
}
else
{
off = 0;
cckd->l2bounds -= 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->l1[sfx][l1x] = (U32)off;
if (cckd_write_l1ent (dev, l1x) < 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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int sfx,l1x,l2x; /* Lookup table indices */
cckd = dev->cckd_ext;
l1x = trk >> 8;
l2x = trk & 0xff;
if (l2 != NULL) l2->pos = l2->len = l2->size = 0;
for (sfx = cckd->sfn; sfx >= 0; sfx--)
{
cckd_trace (dev, "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%x",
sfx, l1x, l2x, trk, cckd->l1[sfx][l1x]);
/* Continue if l2 table not in this file */
if (cckd->l1[sfx][l1x] == 0xffffffff)
continue;
/* Read l2 table from this file */
if (cckd_read_l2 (dev, sfx, l1x) < 0)
return -1;
/* Exit loop if track is in this file */
if (cckd->l2[l2x].pos != 0xffffffff)
break;
}
cckd_trace (dev, "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%x %d %d",
sfx, l1x, l2x, trk, sfx >= 0 ? cckd->l2[l2x].pos : 0,
sfx >= 0 ? cckd->l2[l2x].len : 0,
sfx >= 0 ? cckd->l2[l2x].size : 0);
if (l2 != NULL && sfx >= 0)
{
l2->pos = cckd->l2[l2x].pos;
l2->len = cckd->l2[l2x].len;
l2->size = cckd->l2[l2x].size;
}
return sfx;
} /* end function cckd_read_l2ent */
/*-------------------------------------------------------------------*/
/* Update a level 2 entry */
/*-------------------------------------------------------------------*/
int cckd_write_l2ent (DEVBLK *dev, CCKD_L2ENT *l2, int trk)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int sfx,l1x,l2x; /* Lookup table indices */
off_t off; /* L2 entry offset */
cckd = dev->cckd_ext;
/* Error return if no available level 2 table */
if (!cckd->l2) return -1;
sfx = cckd->sfn;
l1x = trk >> 8;
l2x = trk & 0xff;
/* Copy the new entry if passed */
if (l2) memcpy (&cckd->l2[l2x], l2, CCKD_L2ENT_SIZE);
cckd_trace (dev, "file[%d] l2[%d,%d] trk[%d] write_l2ent 0x%x %d %d",
sfx, l1x, l2x, trk,
cckd->l2[l2x].pos, cckd->l2[l2x].len, cckd->l2[l2x].size);
/* If no level 2 table for this file, then write a new one */
if (cckd->l1[sfx][l1x] == 0 || cckd->l1[sfx][l1x] == 0xffffffff)
return cckd_write_l2 (dev);
/* Write the level 2 table entry */
off = (off_t)(cckd->l1[sfx][l1x] + l2x * CCKD_L2ENT_SIZE);
if (cckd_write (dev, sfx, off, &cckd->l2[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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int sfx; /* File index */
CCKD_L2ENT l2; /* Level 2 entry */
cckd = dev->cckd_ext;
cckd_trace (dev, "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.pos != 0)
{
rc = cckd_read (dev, sfx, (off_t)l2.pos, buf, (size_t)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.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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
off_t off; /* File offset */
CCKD_L2ENT l2, oldl2; /* Level 2 entries */
int sfx,l1x,l2x; /* Lookup table indices */
int after = 0; /* 1=New track after old */
int size; /* Size of new track */
cckd = dev->cckd_ext;
sfx = cckd->sfn;
l1x = trk >> 8;
l2x = trk & 0xff;
cckd_trace (dev, "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, l1x) < 0)
return -1;
/* Save the level 2 entry for the track */
oldl2.pos = cckd->l2[l2x].pos;
oldl2.len = cckd->l2[l2x].len;
oldl2.size = cckd->l2[l2x].size;
cckd_trace (dev, "file[%d] trk[%d] write_trkimg oldl2 0x%x %d %d",
sfx, trk, oldl2.pos,oldl2.len,oldl2.size);
/* Check if writing a null track */
len = cckd_check_null_trk(dev, buf, trk, len);
if (len > CKDDASD_NULLTRK_FMTMAX)
{
/* Get space for the track image */
size = len;
if ((off = cckd_get_space (dev, &size, flags)) < 0)
return -1;
l2.pos = (U32)off;
l2.len = (U16)len;
l2.size = (U16)size;
if (oldl2.pos != 0 && oldl2.pos != 0xffffffff && oldl2.pos < l2.pos)
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.pos = 0;
l2.len = 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.pos, (int)oldl2.len, (int)oldl2.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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc=0; /* Return code */
cckd = dev->cckd_ext;
if ((dev->ckdrdonly && cckd->sfn == 0)
|| cckd->open[cckd->sfn] != CCKD_OPEN_RW)
return 0;
cckd_trace (dev, "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].options &= ~CCKD_OPENED;
if (cckd_write_chdr (dev) < 0)
rc = -1;
if (cckdblk.fsync)
fdatasync (cckd->fd[cckd->sfn]);
return rc;
} /* cckd_harden */
/*-------------------------------------------------------------------*/
/* Return length of an uncompressed track image */
/*-------------------------------------------------------------------*/
int cckd_trklen (DEVBLK *dev, BYTE *buf)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int size; /* Track size */
cckd = dev->cckd_ext;
if (cckd->fbadasd)
return CKDDASD_TRKHDR_SIZE + CFBA_BLOCK_SIZE;
for (size = CKDDASD_TRKHDR_SIZE;
memcmp (&buf[size], &eighthexFF, 8) != 0; )
{
if (size > dev->ckdtrksz) break;
/* add length of count, key, and data fields */
size += CKDDASD_RECHDR_SIZE +
buf[size+5] +
(buf[size+6] << 8) + buf[size+7];
}
/* add length for end-of-track indicator */
size += CKDDASD_RECHDR_SIZE;
/* check for missing end-of-track indicator */
if (size > dev->ckdtrksz ||
memcmp (&buf[size-CKDDASD_RECHDR_SIZE], &eighthexFF, 8) != 0)
{
WRMSG (HHC00306, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn),
buf[0], buf[1], buf[2], buf[3], buf[4]);
size = -1;
}
return size;
}
/*-------------------------------------------------------------------*/
/* Build a null track */
/*-------------------------------------------------------------------*/
int cckd_null_trk(DEVBLK *dev, BYTE *buf, int trk, int nullfmt)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int i; /* Loop counter */
CKDDASD_TRKHDR *trkhdr; /* -> Track header */
CKDDASD_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 */
cckd = dev->cckd_ext;
if (nullfmt < 0 || nullfmt > CKDDASD_NULLTRK_FMTMAX)
nullfmt = cckd->cdevhdr[cckd->sfn].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].nullfmt == CKDDASD_NULLTRK_FMT2)
nullfmt = CKDDASD_NULLTRK_FMT2;
if (cckd->ckddasd)
{
/* cylinder and head calculations */
cyl = trk / dev->ckdheads;
head = trk % dev->ckdheads;
/* Build the track header */
trkhdr = (CKDDASD_TRKHDR*)buf;
trkhdr->bin = 0;
store_hw(&trkhdr->cyl, cyl);
store_hw(&trkhdr->head, head);
pos = buf + CKDDASD_TRKHDR_SIZE;
/* Build record zero */
r = 0;
rechdr = (CKDDASD_RECHDR*)pos;
pos += CKDDASD_RECHDR_SIZE;
store_hw(&rechdr->cyl, cyl);
store_hw(&rechdr->head, head);
rechdr->rec = r;
rechdr->klen = 0;
store_hw(&rechdr->dlen, 8);
memset( pos, 0, 8 );
pos += 8;
r++;
/* Specific null track formatting */
if (nullfmt == CKDDASD_NULLTRK_FMT0)
{
rechdr = (CKDDASD_RECHDR*)pos;
pos += CKDDASD_RECHDR_SIZE;
store_hw(&rechdr->cyl, cyl);
store_hw(&rechdr->head, head);
rechdr->rec = r;
rechdr->klen = 0;
store_hw(&rechdr->dlen, 0);
r++;
}
else if (nullfmt == CKDDASD_NULLTRK_FMT2)
{
for (i = 0; i < 12; i++)
{
rechdr = (CKDDASD_RECHDR*)pos;
pos += CKDDASD_RECHDR_SIZE;
store_hw(&rechdr->cyl, cyl);
store_hw(&rechdr->head, head);
rechdr->rec = r;
rechdr->klen = 0;
store_hw(&rechdr->dlen, 4096);
r++;
memset( pos, 0, 4096 );
pos += 4096;
}
}
/* Build the end of track marker */
memcpy (pos, eighthexFF, 8);
pos += 8;
len = (int)(pos - buf);
}
else
{
memset( buf, 0, CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE );
store_fw(buf+1, trk);
len = CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE;
}
cckd_trace (dev, "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 .. CKDDASD_NULLTRK_FMTMAX if track is null */
/* else return the original length */
/*-------------------------------------------------------------------*/
int cckd_check_null_trk (DEVBLK *dev, BYTE *buf, int trk, int len)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
BYTE buf2[65536]; /* Null track buffer */
cckd = dev->cckd_ext;
rc = len;
if (len == CKDDASD_NULLTRK_SIZE0)
rc = CKDDASD_NULLTRK_FMT0;
else if (len == CKDDASD_NULLTRK_SIZE1)
rc = CKDDASD_NULLTRK_FMT1;
else if (len == CKDDASD_NULLTRK_SIZE2 && dev->oslinux
&& (!cckd->notnull || cckdblk.linuxnull))
{
cckd_null_trk (dev, buf2, trk, 0);
if (memcmp(buf, buf2, len) == 0)
rc = CKDDASD_NULLTRK_FMT2;
}
return rc;
}
/*-------------------------------------------------------------------*/
/* Verify a track/block header and return track/block number */
/*-------------------------------------------------------------------*/
int cckd_cchh (DEVBLK *dev, BYTE *buf, int trk)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
U16 cyl; /* Cylinder */
U16 head; /* Head */
int t; /* Calculated track */
BYTE badcomp=0; /* 1=Unsupported compression */
static char *comp[] = {"none", "zlib", "bzip2"};
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 (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)
WRMSG (HHC00307, "E", SSID_TO_LCSS(dev->ssid), dev->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)
{
WRMSG (HHC00308, "E", SSID_TO_LCSS(dev->ssid), dev->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)
{
WRMSG (HHC00309, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn),
cckd->ckddasd ? "trk" : "blk",
cckd->ckddasd ? "trk" : "blk", t, comp[buf[0]]);
}
else
{
WRMSG (HHC00310, "E", SSID_TO_LCSS(dev->ssid), dev->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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int cyl; /* Cylinder */
int head; /* Head */
char cchh[4],cchh2[4]; /* Cyl, head big-endian */
int r; /* Record number */
int sz; /* Track size */
int vlen; /* Validation length */
int kl,dl; /* Key/Data lengths */
cckd = dev->cckd_ext;
if (buf == NULL || len < 0) return -1;
cckd_trace (dev, "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], buf[5], buf[6],
buf[7], buf[8], buf[9], buf[10], buf[11], buf[12]);
/* FBA dasd check */
if (cckd->fbadasd)
{
if (len == CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE || len == 0)
return len;
cckd_trace (dev, "validation failed: bad length%s","");
return -1;
}
/* cylinder and head calculations */
cyl = trk / dev->ckdheads;
head = trk % dev->ckdheads;
cchh[0] = cyl >> 8;
cchh[1] = cyl & 0xFF;
cchh[2] = head >> 8;
cchh[3] = head & 0xFF;
/* validate record 0 */
memcpy (cchh2, &buf[5], 4); cchh2[0] &= 0x7f; /* fix for ovflow */
if (/* memcmp (cchh, cchh2, 4) != 0 || */ buf[9] != 0 ||
buf[10] != 0 || buf[11] != 0 || buf[12] != 8)
{
cckd_trace (dev, "validation failed: bad r0%s","");
return -1;
}
/* validate records 1 thru n */
vlen = len > 0 ? len : dev->ckdtrksz;
for (r = 1, sz = 21; sz + 8 <= vlen; sz += 8 + kl + dl, r++)
{
if (memcmp (&buf[sz], eighthexFF, 8) == 0) break;
kl = buf[sz+5];
dl = buf[sz+6] * 256 + buf[sz+7];
/* fix for track overflow bit */
memcpy (cchh2, &buf[sz], 4); cchh2[0] &= 0x7f;
/* fix for funny formatted vm disks */
/*
if (r == 1) memcpy (cchh, cchh2, 4);
*/
if (/*memcmp (cchh, cchh2, 4) != 0 ||*/ buf[sz+4] == 0 ||
sz + 8 + kl + dl >= vlen)
{
cckd_trace (dev, "validation failed: bad r%d "
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
r, buf[sz], buf[sz+1], buf[sz+2], buf[sz+3],
buf[sz+4], buf[sz+5], buf[sz+6], buf[sz+7]);
return -1;
}
}
sz += 8;
if ((sz != len && len > 0) || sz > vlen)
{
cckd_trace (dev, "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 = '0' + sfx;
else
*dev->dasdsfx = '*';
return dev->dasdsfn;
} /* end function cckd_sf_name */
/*-------------------------------------------------------------------*/
/* Initialize shadow files */
/*-------------------------------------------------------------------*/
int cckd_sf_init (DEVBLK *dev)
{
CCKDDASD_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 */
cckd = dev->cckd_ext;
/* return if no shadow files */
if (dev->dasdsfn == NULL) return 0;
#if 1
/* Check for shadow file name collision */
for (i = 1; i <= CCKD_MAX_SF && dev->dasdsfn != NULL; i++)
{
DEVBLK *dev2;
CCKDDASD_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)
{
WRMSG (HHC00311, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, i, cckd_sf_name(dev, i),
SSID_TO_LCSS(dev2->ssid), dev2->devnum, j, cckd_sf_name(dev2, j));
return -1;
}
}
}
}
#endif
/* open all existing shadow files */
for (cckd->sfn = 1; cckd->sfn <= CCKD_MAX_SF; cckd->sfn++)
{
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;
/* Call the chkdsk function */
rc = cckd_chkdsk (dev, 0);
if (rc < 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)
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)
{
WRMSG (HHC00312, "E", SSID_TO_LCSS(dev->ssid), dev->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)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
int l1size; /* Size of level 1 table */
CKDDASD_DEVHDR devhdr; /* Device header */
cckd = dev->cckd_ext;
cckd_trace (dev, "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", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn+1);
return -1;
}
/* Error if max number of shadow files exceeded */
if (cckd->sfn+1 == CCKD_MAX_SF)
{
WRMSG (HHC00314, "E", SSID_TO_LCSS(dev->ssid), dev->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, CKDDASD_DEVHDR_SIZE) < 0)
goto sf_new_error;
/* Make sure identifier is CKD_S370 or FBA_S370 */
devhdr.devid[4] = 'S';
/* Write new file's device header */
if (cckd_write (dev, cckd->sfn+1, 0, &devhdr, CKDDASD_DEVHDR_SIZE) < 0)
goto sf_new_error;
/* Build the compressed device header */
memcpy (&cckd->cdevhdr[cckd->sfn+1], &cckd->cdevhdr[cckd->sfn], CCKDDASD_DEVHDR_SIZE);
l1size = cckd->cdevhdr[cckd->sfn+1].numl1tab * CCKD_L1ENT_SIZE;
cckd->cdevhdr[cckd->sfn+1].size =
cckd->cdevhdr[cckd->sfn+1].used = CKDDASD_DEVHDR_SIZE + CCKDDASD_DEVHDR_SIZE + l1size;
cckd->cdevhdr[cckd->sfn+1].free =
cckd->cdevhdr[cckd->sfn+1].free_total =
cckd->cdevhdr[cckd->sfn+1].free_largest =
cckd->cdevhdr[cckd->sfn+1].free_number =
cckd->cdevhdr[cckd->sfn+1].free_imbed = 0;
/* Init the level 1 table */
if ((cckd->l1[cckd->sfn+1] = cckd_malloc (dev, "l1", l1size)) == NULL)
goto sf_new_error;
memset (cckd->l1[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 l2bounds, l2ok */
cckd_read_l1 (dev);
return 0;
sf_new_error:
cckd->l1[cckd->sfn+1] = cckd_free(dev, "l1", cckd->l1[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 l2bounds, l2ok */
cckd_read_l1 (dev);
return -1;
} /* end function cckd_sf_new */
/*-------------------------------------------------------------------*/
/* Add a shadow file (sf+) */
/*-------------------------------------------------------------------*/
void *cckd_sf_add (void *data)
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKDDASD_EXT *cckd; /* -> cckd extension */
#ifdef OPTION_SYNCIO
int syncio; /* Saved syncio bit */
#endif // OPTION_SYNCIO
if (dev == NULL)
{
int n = 0;
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
if (dev->cckd_ext)
{
WRMSG (HHC00315, "I", SSID_TO_LCSS(dev->ssid), dev->devnum );
cckd_sf_add (dev);
n++;
}
WRMSG(HHC00316, "I", n );
return NULL;
}
cckd = dev->cckd_ext;
if (!cckd)
{
WRMSG (HHC00317, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
return NULL;
}
#ifdef OPTION_SYNCIO
/* Disable synchronous I/O for the device */
syncio = cckd_disable_syncio(dev);
#endif // OPTION_SYNCIO
/* Schedule updated track entries to be written */
obtain_lock (&cckd->cckdiolock);
if (cckd->merging)
{
#ifdef OPTION_SYNCIO
dev->syncio = syncio;
#endif // OPTION_SYNCIO
release_lock (&cckd->cckdiolock);
WRMSG (HHC00318, "W", SSID_TO_LCSS(dev->ssid), dev->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", SSID_TO_LCSS(dev->ssid), dev->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", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn));
cckd_sf_add_exit:
/* Re-read the l1 to set l2bounds, l2ok */
cckd_read_l1 (dev);
release_lock (&cckd->filelock);
obtain_lock (&cckd->cckdiolock);
cckd->merging = 0;
if (cckd->cckdwaiters)
broadcast_condition (&cckd->cckdiocond);
#ifdef OPTION_SYNCIO
dev->syncio = syncio;
#endif // OPTION_SYNCIO
release_lock (&cckd->cckdiolock);
cckd_sf_stats (dev);
return NULL;
} /* end function cckd_sf_add */
/*-------------------------------------------------------------------*/
/* Remove a shadow file (sf-) */
/*-------------------------------------------------------------------*/
void *cckd_sf_remove (void *data)
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKDDASD_EXT *cckd; /* -> cckd extension */
#ifdef OPTION_SYNCIO
int syncio; /* Saved syncio bit */
#endif // OPTION_SYNCIO
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[65536]; /* Buffer */
if (dev == NULL)
{
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))
{
WRMSG(HHC00321, "I", SSID_TO_LCSS(dev->ssid), dev->devnum );
cckd->sfmerge = merge;
cckd->sfforce = force;
cckd_sf_remove (dev);
n++;
}
WRMSG(HHC00316, "I", n );
return NULL;
}
cckd = dev->cckd_ext;
if (!cckd)
{
WRMSG (HHC00317, "E", dev ? SSID_TO_LCSS(dev->ssid) : 0, dev ? dev->devnum : 0);
return NULL;
}
/* Set flags */
merge = cckd->sfmerge || cckd->sfforce;
force = cckd->sfforce;
cckd->sfmerge = cckd->sfforce = 0;
cckd_trace (dev, "merge starting: %s %s",
merge ? "merge" : "nomerge", force ? "force" : "");
#ifdef OPTION_SYNCIO
/* Disable synchronous I/O for the device */
syncio = cckd_disable_syncio(dev);
#endif // OPTION_SYNCIO
/* Schedule updated track entries to be written */
obtain_lock (&cckd->cckdiolock);
if (cckd->merging)
{
#ifdef OPTION_SYNCIO
dev->syncio = syncio;
#endif // OPTION_SYNCIO
release_lock (&cckd->cckdiolock);
WRMSG (HHC00322, "W", SSID_TO_LCSS(dev->ssid), dev->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 == 0)
{
#ifdef OPTION_SYNCIO
dev->syncio = syncio;
#endif // OPTION_SYNCIO
release_lock (&cckd->filelock);
WRMSG (HHC00323, "E", SSID_TO_LCSS(dev->ssid), dev->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].nullfmt;
/* Harden the `from' file */
if (cckd_harden (dev) < 0)
{
WRMSG (HHC00324, "E", SSID_TO_LCSS(dev->ssid), dev->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)
{
WRMSG (HHC00324, "E", SSID_TO_LCSS(dev->ssid), dev->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;
WRMSG (HHC00324, "E", SSID_TO_LCSS(dev->ssid), dev->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 (dev, "merging to file[%d]", to_sfx);
/* Make the target file the active file */
cckd->sfn = to_sfx;
cckd->cdevhdr[to_sfx].options |= (CCKD_OPENED | CCKD_ORDWR);
/* Loop for each level 1 table entry */
for (i = 0; i < cckd->cdevhdr[from_sfx].numl1tab; i++)
{
l2updated = 0;
/* Continue if from L2 doesn't exist */
if (cckd->l1[from_sfx][i] == 0xffffffff
|| (cckd->l1[from_sfx][i] == 0 && cckd->l1[to_sfx][i] == 0))
continue;
trk = i*256;
/* Read `from' l2 table */
if (cckd->l1[from_sfx][i] == 0)
memset( &from_l2, 0, CCKD_L2TAB_SIZE );
else
{
pos = (off_t)cckd->l1[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->l1[to_sfx][i] == 0)
memset( &to_l2, 0, CCKD_L2TAB_SIZE );
else if (cckd->l1[to_sfx][i] == 0xffffffff)
memset (&to_l2, 0xff, CCKD_L2TAB_SIZE);
else
{
pos = (off_t)cckd->l1[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].pos == 0xffffffff
|| (from_l2[j].pos == 0 && to_l2[j].pos == 0))
continue;
/* Read the `from' track/blkgrp image */
len = (int)from_l2[j].len;
if (len > CKDDASD_NULLTRK_FMTMAX)
{
pos = (off_t)from_l2[j].pos;
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.pos = (U32)pos;
new_l2.len = (U16)len;
new_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.pos = 0;
new_l2.len = new_l2.size = (U16)len;
}
/* Release space occupied by old `to' entry */
cckd_rel_space (dev, (off_t)to_l2[j].pos, (int)to_l2[j].len,
(int)to_l2[j].size);
/* Update `to' l2 table entry */
l2updated = 1;
to_l2[j].pos = new_l2.pos;
to_l2[j].len = new_l2.len;
to_l2[j].size = new_l2.size;
} /* for each level 2 table entry */
/* Update the `to' level 2 table */
if (l2updated)
{
l2updated = 0;
pos = (off_t)cckd->l1[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 == 0 || pos == (off_t)0xffffffff)
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->l1[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->l1[from_sfx] = cckd_free (dev, "l1", cckd->l1[from_sfx]);
memset( &cckd->cdevhdr[from_sfx], 0, CCKDDASD_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->l2reads[to_sfx] += cckd->l2reads[from_sfx];
cckd->reads[from_sfx] = 0;
cckd->writes[from_sfx] = 0;
cckd->l2reads[from_sfx] = 0;
/* Add the file back if necessary */
if (add) rc = cckd_sf_new (dev) ;
WRMSG (HHC00325, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, from_sfx, cckd_sf_name(dev, from_sfx),
merge ? "merged" : add ? "re-added" : "removed");
sf_remove_exit:
/* Re-read the l1 to set l2bounds, 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);
#ifdef OPTION_SYNCIO
dev->syncio = syncio;
#endif // OPTION_SYNCIO
cckd_trace (dev, "merge complete%s","");
release_lock (&cckd->cckdiolock);
cckd_sf_stats (dev);
return NULL;
sf_merge_error:
if (trk < 0)
WRMSG (HHC00326, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, from_sfx, cckd_sf_name(dev, from_sfx));
else
WRMSG (HHC00327, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, from_sfx, cckd_sf_name(dev, from_sfx), trk);
if (l2updated && cckd->l1[to_sfx][i] && cckd->l1[to_sfx][i] != 0xffffffff)
{
l2updated = 0;
pos = (off_t)cckd->l1[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) */
/*-------------------------------------------------------------------*/
void *cckd_sf_comp (void *data)
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKDDASD_EXT *cckd; /* -> cckd extension */
#ifdef OPTION_SYNCIO
int syncio; /* Saved syncio bit */
#endif // OPTION_SYNCIO
int rc; /* Return code */
if (dev == NULL)
{
int n = 0;
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
if (dev->cckd_ext)
{
WRMSG( HHC00328, "I", SSID_TO_LCSS(dev->ssid), dev->devnum );
cckd_sf_comp (dev);
n++;
}
WRMSG (HHC00316, "I", n );
return NULL;
}
cckd = dev->cckd_ext;
if (!cckd)
{
WRMSG (HHC00317, "W", SSID_TO_LCSS(dev->ssid), dev->devnum);
return NULL;
}
#ifdef OPTION_SYNCIO
/* Disable synchronous I/O for the device */
syncio = cckd_disable_syncio(dev);
#endif // OPTION_SYNCIO
/* schedule updated track entries to be written */
obtain_lock (&cckd->cckdiolock);
if (cckd->merging)
{
#ifdef OPTION_SYNCIO
dev->syncio = syncio;
#endif // OPTION_SYNCIO
release_lock (&cckd->cckdiolock);
WRMSG (HHC00329, "W", SSID_TO_LCSS(dev->ssid), dev->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);
#ifdef OPTION_SYNCIO
dev->syncio = syncio;
#endif // OPTION_SYNCIO
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) */
/*-------------------------------------------------------------------*/
void *cckd_sf_chk (void *data)
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKDDASD_EXT *cckd; /* -> cckd extension */
#ifdef OPTION_SYNCIO
int syncio; /* Saved syncio bit */
#endif // OPTION_SYNCIO
int rc; /* Return code */
int level = 2; /* Check level */
if (dev == NULL)
{
int n = 0;
level = cckdblk.sflevel;
cckdblk.sflevel = 0;
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
if ((cckd = dev->cckd_ext))
{
WRMSG (HHC00330, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, level );
cckd->sflevel = level;
cckd_sf_chk (dev);
n++;
}
WRMSG(HHC00316, "I", n );
return NULL;
}
cckd = dev->cckd_ext;
if (!cckd)
{
WRMSG (HHC00317, "W", SSID_TO_LCSS(dev->ssid), dev->devnum);
return NULL;
}
level = cckd->sflevel;
cckd->sflevel = 0;
#ifdef OPTION_SYNCIO
/* Disable synchronous I/O for the device */
syncio = cckd_disable_syncio(dev);
#endif // OPTION_SYNCIO
/* schedule updated track entries to be written */
obtain_lock (&cckd->cckdiolock);
if (cckd->merging)
{
#ifdef OPTION_SYNCIO
dev->syncio = syncio;
#endif // OPTION_SYNCIO
release_lock (&cckd->cckdiolock);
WRMSG (HHC00331, "W", SSID_TO_LCSS(dev->ssid), dev->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 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);
#ifdef OPTION_SYNCIO
dev->syncio = syncio;
#endif // OPTION_SYNCIO
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) */
/*-------------------------------------------------------------------*/
void *cckd_sf_stats (void *data)
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKDDASD_EXT *cckd; /* -> cckd extension */
struct stat st; /* File information */
int i; /* Index */
int rc; /* Return code */
char *ost[] = {" ", "ro", "rd", "rw"};
U64 usize=0,ufree=0; /* Total size, free space */
int freenbr=0; /* Total number free spaces */
if (dev == NULL)
{
int n = 0;
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
if (dev->cckd_ext)
{
WRMSG( HHC00332, "I", SSID_TO_LCSS(dev->ssid), dev->devnum );
cckd_sf_stats (dev);
n++;
}
WRMSG(HHC00316, "I", n );
return NULL;
}
cckd = dev->cckd_ext;
if (!cckd)
{
WRMSG (HHC00317, "W", SSID_TO_LCSS(dev->ssid), dev->devnum);
return NULL;
}
// obtain_lock (&cckd->filelock);
/* Calculate totals */
rc = fstat (cckd->fd[0], &st);
for (i = 0; i <= cckd->sfn; i++)
{
if (!i) usize = st.st_size;
else usize += cckd->cdevhdr[i].size;
ufree += cckd->cdevhdr[i].free_total;
freenbr += cckd->cdevhdr[i].free_number;
}
/* header */
WRMSG (HHC00333, "I", SSID_TO_LCSS(dev->ssid), dev->devnum);
if (cckd->readaheads || cckd->misses)
WRMSG (HHC00334, "I", SSID_TO_LCSS(dev->ssid), dev->devnum);
WRMSG (HHC00335, "I", SSID_TO_LCSS(dev->ssid), dev->devnum);
/* total statistics */
WRMSG (HHC00336, "I", SSID_TO_LCSS(dev->ssid), dev->devnum,
usize, (ufree * 100) / usize, freenbr,
cckd->totreads, cckd->totwrites, cckd->totl2reads,
cckd->cachehits, cckd->switches);
if (cckd->readaheads || cckd->misses)
WRMSG (HHC00337, "I", SSID_TO_LCSS(dev->ssid), dev->devnum,
cckd->readaheads, cckd->misses);
/* base file statistics */
WRMSG (HHC00338, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename);
WRMSG (HHC00339, "I", SSID_TO_LCSS(dev->ssid), dev->devnum,
st.st_size,
((S64)(cckd->cdevhdr[0].free_total * 100) / st.st_size),
cckd->cdevhdr[0].free_number, ost[cckd->open[0]],
cckd->reads[0], cckd->writes[0], cckd->l2reads[0]);
if (dev->dasdsfn != NULL && CCKD_MAX_SF > 0)
WRMSG (HHC00340, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd_sf_name(dev, -1));
/* shadow file statistics */
for (i = 1; i <= cckd->sfn; i++)
{
WRMSG (HHC00341, "I", SSID_TO_LCSS(dev->ssid), dev->devnum,
i, (S64)cckd->cdevhdr[i].size,
((S64)(cckd->cdevhdr[i].free_total * 100) / cckd->cdevhdr[i].size),
cckd->cdevhdr[i].free_number, ost[cckd->open[i]],
cckd->reads[i], cckd->writes[i], cckd->l2reads[i]);
}
// release_lock (&cckd->filelock);
return NULL;
} /* end function cckd_sf_stats */
#ifdef OPTION_SYNCIO
/*-------------------------------------------------------------------*/
/* Disable synchronous I/O for a device */
/*-------------------------------------------------------------------*/
int cckd_disable_syncio(DEVBLK *dev)
{
if (!dev->syncio) return 0;
obtain_lock(&dev->lock);
while (dev->syncio_active)
{
release_lock(&dev->lock);
usleep(500);
obtain_lock(&dev->lock);
}
dev->syncio = 0;
release_lock(&dev->lock);
cckd_trace (dev, "syncio disabled%s","");
return 1;
}
#endif // OPTION_SYNCIO
/*-------------------------------------------------------------------*/
/* Lock/unlock the device chain */
/*-------------------------------------------------------------------*/
void cckd_lock_devchain(int flag)
{
obtain_lock(&cckdblk.devlock);
while ((flag && cckdblk.devusers != 0)
|| (!flag && cckdblk.devusers < 0))
{
cckdblk.devwaiters++;
#if FALSE
{
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);
}
void cckd_unlock_devchain()
{
obtain_lock(&cckdblk.devlock);
if (cckdblk.devusers < 0)
cckdblk.devusers++;
else
cckdblk.devusers--;
if (cckdblk.devusers == 0 && cckdblk.devwaiters)
signal_condition(&cckdblk.devcond);
release_lock(&cckdblk.devlock);
}
/*-------------------------------------------------------------------*/
/* Garbage Collection thread */
/*-------------------------------------------------------------------*/
void* cckd_gcol(void* arg)
{
int gcol; /* Identifier */
int rc; /* Return code */
DEVBLK *dev; /* -> device block */
CCKDDASD_EXT *cckd; /* -> cckd extension */
S64 size, fsiz; /* File size, free size */
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 gc; /* Garbage collection state */
int gctab[5]= { /* default gcol 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% */
UNREFERENCED( arg );
gettimeofday (&tv_now, NULL);
obtain_lock (&cckdblk.gclock);
gcol = ++cckdblk.gcs;
/* Return without messages if too many already started */
if (gcol > cckdblk.gcmax)
{
--cckdblk.gcs;
release_lock (&cckdblk.gclock);
return NULL;
}
if (!cckdblk.batch)
{
WRMSG (HHC00100, "I", thread_id(), get_thread_priority(0), "Garbage collector");
}
while (gcol <= cckdblk.gcmax)
{
cckd_lock_devchain(0);
/* Perform collection on each device */
for (dev = cckdblk.dev1st; dev; dev = cckd->devnext)
{
cckd = dev->cckd_ext;
obtain_lock (&cckd->cckdiolock);
/* Bypass if merging or stopping */
if (cckd->merging || cckd->stopping)
{
release_lock (&cckd->cckdiolock);
continue;
}
/* Bypass if not opened read-write */
if (cckd->open[cckd->sfn] != CCKD_OPEN_RW)
{
release_lock (&cckd->cckdiolock);
continue;
}
/* 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].options & CCKD_OPENED))
{
if (cckd->updated) cckd_flush_cache (dev);
release_lock (&cckd->cckdiolock);
continue;
}
/* Determine garbage state */
size = (S64)cckd->cdevhdr[cckd->sfn].size;
fsiz = (S64)cckd->cdevhdr[cckd->sfn].free_total;
if (fsiz >= (size = size/2)) gc = 0;
else if (fsiz >= (size = size/2)) gc = 1;
else if (fsiz >= (size = size/2)) gc = 2;
else if (fsiz >= (size = size/2)) gc = 3;
else gc = 4;
/* Adjust the state based on the number of free spaces */
if (cckd->cdevhdr[cckd->sfn].free_number > 800 && gc > 0) gc--;
if (cckd->cdevhdr[cckd->sfn].free_number > 1800 && gc > 0) gc--;
if (cckd->cdevhdr[cckd->sfn].free_number > 3000) gc = 0;
/* 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].used >> 10)
size = cckd->cdevhdr[cckd->sfn].used >> 10;
if (size < 64) size = 64;
release_lock (&cckd->cckdiolock);
/* Call the garbage collector */
cckd_gc_percolate (dev, (unsigned int)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_number)
{
obtain_lock (&cckd->filelock);
cckd_flush_space (dev);
release_lock (&cckd->filelock);
}
} /* for each cckd device */
cckd_unlock_devchain();
/* wait a bit */
// Get the time of day again for the file sync check above
gettimeofday (&tv_now, NULL);
tt_now = tv_now.tv_sec + ((tv_now.tv_usec + 500000)/1000000);
cckd_trace (dev, "gcol wait %d seconds at %s",
cckdblk.gcwait, ctime (&tt_now));
tm.tv_sec = tv_now.tv_sec + cckdblk.gcwait;
tm.tv_nsec = tv_now.tv_usec * 1000;
timed_wait_condition (&cckdblk.gccond, &cckdblk.gclock, &tm);
}
if (!cckdblk.batch)
WRMSG (HHC00101, "I", thread_id(), get_thread_priority(0), "Garbage collector");
cckdblk.gcs--;
if (!cckdblk.gcs) signal_condition (&cckdblk.termcond);
release_lock (&cckdblk.gclock);
return NULL;
} /* end thread cckd_gcol */
/*-------------------------------------------------------------------*/
/* Garbage Collection -- Percolate algorithm */
/*-------------------------------------------------------------------*/
int cckd_gc_percolate(DEVBLK *dev, unsigned int size)
{
CCKDDASD_EXT *cckd; /* -> cckd extension */
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, l; /* Indexes */
int flags; /* Write trkimg flags */
off_t fpos, upos; /* File offsets */
unsigned int flen, ulen, len; /* Lengths */
int trk; /* Track number */
int l1x,l2x; /* Table Indexes */
CCKD_L2ENT l2; /* Copied level 2 entry */
BYTE buf[256*1024]; /* Buffer */
cckd = dev->cckd_ext;
size = size << 10;
/* Debug */
if (cckdblk.itracen)
{
cckd_trace (dev, "gcperc size %d 1st 0x%x nbr %d largest %u",
size, cckd->cdevhdr[cckd->sfn].free,
cckd->cdevhdr[cckd->sfn].free_number,
cckd->cdevhdr[cckd->sfn].free_largest);
fpos = (off_t)cckd->cdevhdr[cckd->sfn].free;
for (i = cckd->free1st; i >= 0; i = cckd->free[i].next)
{
cckd_trace (dev, "gcperc free[%4d]:%8.8x end %8.8x len %10d%cpend %d",
i,(int)fpos,(int)(fpos+cckd->free[i].len),(int)cckd->free[i].len,
fpos+(int)cckd->free[i].len == (int)cckd->free[i].pos ?
'*' : ' ',cckd->free[i].pending);
fpos = cckd->free[i].pos;
}
}
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->free) cckd_read_fsp (dev);
/* Find a space to start with */
l = -1;
upos = ulen = flen = 0;
fpos = cckd->cdevhdr[sfx].free;
/* First non-pending free space */
for (i = cckd->free1st; i >= 0; i = cckd->free[i].next)
{
if (!cckd->free[i].pending)
{
flen += cckd->free[i].len;
break;
}
fpos = cckd->free[i].pos;
}
/* Continue to largest if non-zero `after' */
for ( ; i >= 0 && after; i = cckd->free[i].next)
{
l = i;
if (!cckd->free[i].pending) flen += cckd->free[i].len;
if (cckd->free[i].len == cckd->cdevhdr[sfx].free_largest)
break;
fpos = cckd->free[i].pos;
}
/* Skip following free spaces */
for ( ; i >= 0; i = cckd->free[i].next)
{
if (!cckd->free[i].pending) flen += cckd->free[i].len;
if (fpos + cckd->free[i].len != cckd->free[i].pos) break;
fpos = cckd->free[i].pos;
}
/* Space preceding largest if largest is at the end */
if (i < 0 && l >= 0)
{
if (!cckd->free[l].pending) flen -= cckd->free[i].len;
for (i = cckd->free[l].prev; i >= 0; i = cckd->free[i].prev)
{
fpos = cckd->free[i].prev >= 0
? cckd->free[cckd->free[i].prev].pos
: cckd->cdevhdr[sfx].free;
if (fpos + cckd->free[i].len < cckd->free[i].pos) break;
if (!cckd->free[i].pending) flen -= cckd->free[i].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->free[i].len;
ulen = (cckd->free[i].pos ? cckd->free[i].pos : cckd->cdevhdr[sfx].size) - upos;
}
else if (!cckd->cdevhdr[sfx].free_number && cckd->cdevhdr[sfx].free_imbed)
{
upos = (off_t)(CCKD_L1TAB_POS + cckd->cdevhdr[sfx].numl1tab * CCKD_L1ENT_SIZE);
while (1)
{
for (i = 0; i < cckd->cdevhdr[sfx].numl1tab; i++)
if (cckd->l1[sfx][i] == (U32)upos)
break;
if (i >= cckd->cdevhdr[sfx].numl1tab)
break;
upos += CCKD_L2TAB_SIZE;
}
ulen = cckd->cdevhdr[sfx].size - upos;
}
/* Return if no applicable used space */
if (ulen == 0)
{
cckd_trace (dev, "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 (dev, "gcperc selected space 0x"I64_FMTx" len %d", upos, ulen);
if (cckd_read (dev, sfx, upos, buf, ulen) < 0)
goto cckd_gc_perc_error;
/* Process each space in the buffer */
flags = cckd->cdevhdr[sfx].free_number < 100 ? CCKD_SIZE_EXACT : CCKD_SIZE_ANY;
for (i = a = 0; i + CKDDASD_TRKHDR_SIZE <= (int)ulen; i += len)
{
/* Check for level 2 table */
for (j = 0; j < cckd->cdevhdr[sfx].numl1tab; j++)
if (cckd->l1[sfx][j] == (U32)(upos + i)) break;
if (j < cckd->cdevhdr[sfx].numl1tab)
{
/* Moving a level 2 table */
len = CCKD_L2TAB_SIZE;
if (i + len > ulen) break;
cckd_trace (dev, "gcperc move l2tab[%d] at pos 0x"I64_FMTx" len %d",
j, upos + i, len);
/* Make the level 2 table active */
if (cckd_read_l2 (dev, sfx, j) < 0)
goto cckd_gc_perc_error;
/* Write the level 2 table */
if (cckd_write_l2 (dev) < 0)
goto cckd_gc_perc_error;
}
else
{
/* Moving a track image */
if ((trk = cckd_cchh (dev, buf + i, -1)) < 0)
goto cckd_gc_perc_space_error;
l1x = trk >> 8;
l2x = trk & 0xff;
/* Read the lookup entry for the track */
if (cckd_read_l2ent (dev, &l2, trk) < 0)
goto cckd_gc_perc_error;
if (l2.pos != (U32)(upos + i))
goto cckd_gc_perc_space_error;
len = (int)l2.size;
if (i + l2.len > (int)ulen) break;
cckd_trace (dev, "gcperc move trk %d at pos 0x"I64_FMTx" len %h",
trk, upos + i, l2.len);
/* Relocate the track image somewhere else */
if ((rc = cckd_write_trkimg (dev, buf + i, (int)l2.len, trk, flags)) < 0)
goto cckd_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 (dev, "gcperc moved %d 1st 0x%x nbr %u", moved,
cckd->cdevhdr[cckd->sfn].free,cckd->cdevhdr[cckd->sfn].free_number);
return moved;
cckd_gc_perc_space_error:
WRMSG (HHC00342, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
cckd->sfn,cckd_sf_name(dev, cckd->sfn), upos + i,
buf[i], buf[i+1],buf[i+2], buf[i+3], buf[i+4]);
cckd->cdevhdr[cckd->sfn].options |= CCKD_SPERRS;
cckd_print_itrace();
cckd_gc_perc_error:
cckd_trace (dev, "gcperc exiting due to error, moved %u", moved);
release_lock (&cckd->filelock);
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)
{
CCKDDASD_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 */
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].numl1tab; i++)
if (cckd->l1[sfx][i] != 0 && cckd->l1[sfx][i] != 0xffffffff
&& cckd->l2bounds - CCKD_L2TAB_SIZE < (off_t)cckd->l1[sfx][i])
break;
/* Return OK if no l2 tables out of bounds */
if (i >= cckd->cdevhdr[sfx].numl1tab)
goto cckd_gc_l2_exit_ok;
/* Relocate all track images within the bounds */
pos = CCKD_L1TAB_POS + (cckd->cdevhdr[sfx].numl1tab * CCKD_L1ENT_SIZE);
i = cckd->free1st;
fpos = (off_t)cckd->cdevhdr[sfx].free;
while (pos < cckd->l2bounds)
{
if (i >= 0 && pos == fpos)
{
pos += cckd->free[i].len;
fpos = (off_t)cckd->free[i].pos;
i = cckd->free[i].next;
j = 0;
}
else
{
for (j = 0; j < cckd->cdevhdr[sfx].numl1tab; j++)
if (pos == (off_t)cckd->l1[sfx][j])
{
pos += CCKD_L2TAB_SIZE;
break;
}
}
if (j >= cckd->cdevhdr[sfx].numl1tab)
{
/* Found a track to relocate */
if (cckd_read (dev, sfx, pos, buf, CKDDASD_TRKHDR_SIZE) < 0)
goto cckd_gc_l2_exit;
if ((trk = cckd_cchh (dev, buf, -1)) < 0)
goto cckd_gc_l2_exit;
cckd_trace (dev, "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].numl1tab * CCKD_L1ENT_SIZE);
i = cckd->free1st;
fpos = (off_t)cckd->cdevhdr[sfx].free;
}
}
do {
/* Find a level 2 table to relocate */
i = cckd->free1st;
fpos = (off_t)cckd->cdevhdr[sfx].free;
cckd_trace (dev, "gc_l2 first free[%d] pos 0x%x len %d pending %d",
i, (int)fpos, i >= 0 ? (int)cckd->free[i].len : -1,
i >= 0 ? cckd->free[i].pending : -1);
if (i < 0 || fpos >= cckd->l2bounds || cckd->free[i].pending)
goto cckd_gc_l2_exit;
if (cckd->free[i].len < CCKD_L2TAB_SIZE
|| (cckd->free[i].len != CCKD_L2TAB_SIZE
&& cckd->free[i].len < CCKD_L2TAB_SIZE + CCKD_FREEBLK_SIZE
)
)
{
for (i = 0; i < cckd->cdevhdr[sfx].numl1tab; i++)
if (fpos + cckd->free[i].len == (off_t)cckd->l1[sfx][i])
break;
}
else
{
for (i = 0; i < cckd->cdevhdr[sfx].numl1tab; i++)
if (cckd->l2bounds - CCKD_L2TAB_SIZE < (off_t)cckd->l1[sfx][i]
&& cckd->l1[sfx][i] != 0xffffffff)
break;
}
if (i < cckd->cdevhdr[sfx].numl1tab)
{
cckd_trace (dev, "gc_l2 relocate l2[%d] pos 0x%x",
i, cckd->l1[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].numl1tab);
cckd_gc_l2_exit:
release_lock (&cckd->filelock);
return 0;
cckd_gc_l2_exit_ok:
cckd_trace (dev, "gc_l2 ok%s", "");
cckd->l2ok = 1;
goto cckd_gc_l2_exit;
}
/*-------------------------------------------------------------------*/
/* Find device by devnum */
/*-------------------------------------------------------------------*/
DEVBLK *cckd_find_device_by_devnum (U16 devnum)
{
DEVBLK *dev;
CCKDDASD_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)
{
CCKDDASD_EXT *cckd;
BYTE *to = NULL; /* Uncompressed buffer */
int newlen; /* Uncompressed length */
BYTE comp; /* Compression type */
static char *compress[] = {"none", "zlib", "bzip2"};
cckd = dev->cckd_ext;
cckd_trace (dev, "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;
}
/* Unable to uncompress */
WRMSG (HHC00343, "E",
SSID_TO_LCSS(dev->ssid), dev->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",
SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name(dev, cckd->sfn), compress[comp]);
return NULL;
}
int cckd_uncompress_zlib (DEVBLK *dev, BYTE *to, BYTE *from, int len, int maxlen)
{
#if defined(HAVE_LIBZ)
unsigned long newlen;
int rc;
UNREFERENCED(dev);
memcpy (to, from, CKDDASD_TRKHDR_SIZE);
newlen = maxlen - CKDDASD_TRKHDR_SIZE;
rc = uncompress(&to[CKDDASD_TRKHDR_SIZE], &newlen,
&from[CKDDASD_TRKHDR_SIZE], len - CKDDASD_TRKHDR_SIZE);
if (rc == Z_OK)
{
newlen += CKDDASD_TRKHDR_SIZE;
to[0] = 0;
}
else
newlen = -1;
cckd_trace (dev, "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
}
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, CKDDASD_TRKHDR_SIZE);
newlen = maxlen - CKDDASD_TRKHDR_SIZE;
rc = BZ2_bzBuffToBuffDecompress (
(void *)&to[CKDDASD_TRKHDR_SIZE], &newlen,
(void *)&from[CKDDASD_TRKHDR_SIZE], len - CKDDASD_TRKHDR_SIZE,
0, 0);
if (rc == BZ_OK)
{
newlen += CKDDASD_TRKHDR_SIZE;
to[0] = 0;
}
else
newlen = -1;
cckd_trace (dev, "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;
}
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;
}
int cckd_compress_zlib (DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm)
{
#if defined(HAVE_LIBZ)
unsigned long newlen;
int rc;
BYTE *buf;
UNREFERENCED(dev);
buf = *to;
from[0] = CCKD_COMPRESS_NONE;
memcpy (buf, from, CKDDASD_TRKHDR_SIZE);
buf[0] = CCKD_COMPRESS_ZLIB;
newlen = 65535 - CKDDASD_TRKHDR_SIZE;
rc = compress2 (&buf[CKDDASD_TRKHDR_SIZE], &newlen,
&from[CKDDASD_TRKHDR_SIZE], len - CKDDASD_TRKHDR_SIZE,
parm);
newlen += CKDDASD_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, CKDDASD_TRKHDR_SIZE);
buf[0] = CCKD_COMPRESS_BZIP2;
newlen = 65535 - CKDDASD_TRKHDR_SIZE;
rc = BZ2_bzBuffToBuffCompress (
(void *)&buf[CKDDASD_TRKHDR_SIZE], &newlen,
(void *)&from[CKDDASD_TRKHDR_SIZE], len - CKDDASD_TRKHDR_SIZE,
parm >= 1 && parm <= 9 ? parm : 5, 0, 0);
newlen += CKDDASD_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:"
," comp=<n> Override compression (-1,0,1,2)"
," compparm=<n> Override compression parm (-1 ... 9)"
," 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)"
," wr=<n> Set number writer threads ( 1 ... 9)"
," gcint=<n> Set garbage collector interval (sec) ( 1 .. 60)"
," gcparm=<n> Set garbage collector parameter (-8 ... 8)"
," freepend=<n> Set free pending cycles (-1 ... 4)"
," trace=<n> Set trace table size (0 ... 200000)"
," gcstart=<n> Start garbage collector (0 or 1)"
," nostress=<n> Disable stress writes (0 or 1)"
," fsync=<n> Enable fsync (0 or 1)"
," linuxnull=<n> Check for null linux tracks (0 or 1)"
," opts Display cckd options"
," stats Display cckd statistics"
," help Display help message"
,NULL };
for( i = 0; help[i] != NULL; i++ )
WRMSG(HHC00345, "I", help[i] );
} /* end function cckd_command_help */
/*-------------------------------------------------------------------*/
/* cckd command stats */
/*-------------------------------------------------------------------*/
void cckd_command_opts()
{
char msgbuf[128];
MSGBUF( msgbuf, "cckd opts: comp=%d,compparm=%d,ra=%d,raq=%d,rat=%d,wr=%d,gcint=%d",
cckdblk.comp == 0xff ? -1 : cckdblk.comp,
cckdblk.compparm, cckdblk.ramax,
cckdblk.ranbr, cckdblk.readaheads,
cckdblk.wrmax, cckdblk.gcwait );
WRMSG( HHC00346, "I", msgbuf );
MSGBUF( msgbuf, " gcparm=%d,nostress=%d,freepend=%d,fsync=%d,linuxnull=%d,trace=%d",
cckdblk.gcparm, cckdblk.nostress, cckdblk.freepend,
cckdblk.fsync, cckdblk.linuxnull, cckdblk.itracen );
WRMSG( HHC00346, "I", msgbuf );
return;
} /* end function cckd_command_opts */
/*-------------------------------------------------------------------*/
/* cckd command stats */
/*-------------------------------------------------------------------*/
void cckd_command_stats()
{
char msgbuf[128];
WRMSG( HHC00347, "I", "cckd stats:" );
MSGBUF( msgbuf, " reads....%10" I64_FMT "d Kbytes...%10" I64_FMT "d",
cckdblk.stats_reads, cckdblk.stats_readbytes >> 10 );
WRMSG( HHC00347, "I", msgbuf );
MSGBUF( msgbuf, " writes...%10" I64_FMT "d Kbytes...%10" I64_FMT "d",
cckdblk.stats_writes, cckdblk.stats_writebytes >> 10 );
WRMSG( HHC00347, "I", msgbuf );
MSGBUF( msgbuf, " readaheads%9" I64_FMT "d misses...%10" I64_FMT "d",
cckdblk.stats_readaheads, cckdblk.stats_readaheadmisses );
WRMSG( HHC00347, "I", msgbuf );
#ifdef OPTION_SYNCIO
MSGBUF( msgbuf, " syncios..%10" I64_FMT "d misses...%10" I64_FMT "d",
cckdblk.stats_syncios, cckdblk.stats_synciomisses );
WRMSG( HHC00347, "I", msgbuf );
#endif // OPTION_SYNCIO
MSGBUF( msgbuf, " switches.%10" I64_FMT "d l2 reads.%10" I64_FMT "d strs wrt.%10" I64_FMT "d",
cckdblk.stats_switches, cckdblk.stats_l2reads, cckdblk.stats_stresswrites );
WRMSG( HHC00347, "I", msgbuf );
MSGBUF( msgbuf, " cachehits%10" I64_FMT "d misses...%10" I64_FMT "d",
cckdblk.stats_cachehits, cckdblk.stats_cachemisses );
WRMSG( HHC00347, "I", msgbuf );
MSGBUF( msgbuf, " l2 hits..%10" I64_FMT "d misses...%10" I64_FMT "d",
cckdblk.stats_l2cachehits, cckdblk.stats_l2cachemisses );
WRMSG( HHC00347, "I", msgbuf );
MSGBUF( msgbuf, " waits............ i/o......%10" I64_FMT "d cache....%10" I64_FMT "d",
cckdblk.stats_iowaits, cckdblk.stats_cachewaits );
WRMSG( HHC00347, "I", msgbuf );
MSGBUF( msgbuf, " garbage collector moves....%10" I64_FMT "d Kbytes...%10" I64_FMT "d",
cckdblk.stats_gcolmoves, cckdblk.stats_gcolbytes >> 10 );
WRMSG( HHC00347, "I", msgbuf );
return;
} /* end function cckd_command_stats */
/*-------------------------------------------------------------------*/
/* cckd command debug */
/*-------------------------------------------------------------------*/
void cckd_command_debug()
{
return;
}
/*-------------------------------------------------------------------*/
/* cckd command processor */
/*-------------------------------------------------------------------*/
DLL_EXPORT int cckd_command(char *op, int cmd)
{
char *kw, *p, c, buf[256];
int val, opts = 0;
int rc;
/* Display help for null operand */
if (op == NULL)
{
if (memcmp (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id)) == 0 && cmd)
cckd_command_help();
return 0;
}
strlcpy(buf, op, sizeof(buf));
op = buf;
/* Initialize the global cckd block if necessary */
if (memcmp (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id)))
cckddasd_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';
rc = sscanf (p, "%d%c", &val, &c);
}
else rc = 0;
/* Parse the keyword */
if ( CMD(kw,help,4 ) )
{
if (!cmd) return 0;
cckd_command_help();
}
else if ( CMD(kw,stats,4) )
{
if (!cmd) return 0;
cckd_command_stats ();
}
else if ( CMD(kw,opts,4) )
{
if (!cmd) return 0;
cckd_command_opts();
}
else if ( CMD(kw,debug,5) )
{
if (!cmd) return 0;
cckd_command_debug();
}
else if (rc != 1)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else if ( CMD(kw,comp,4) )
{
if (val < -1 || (val > 0 && (val & ~cckdblk.comps)))
{
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 = 1;
break;
default: /* unsupported algorithm */
WRMSG(HHC00348, "E", val, kw);
return -1;
}
}
else if ( CMD(kw,compparm,8) )
{
if (val < -1 || val > 9)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.compparm = val;
opts = 1;
}
}
else if ( CMD(kw,ra,2) )
{
if (val < CCKD_MIN_RA || val > CCKD_MAX_RA)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.ramax = val;
opts = 1;
}
}
else if ( CMD(kw,raq,3) )
{
if (val < 0 || val > CCKD_MAX_RA_SIZE)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.ranbr = val;
opts = 1;
}
}
else if ( CMD(kw,rat,3) )
{
if (val < 0 || val > CCKD_MAX_RA_SIZE)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.readaheads = val;
opts = 1;
}
}
else if ( CMD(kw,wr,2) )
{
if (val < CCKD_MIN_WRITER || val > CCKD_MAX_WRITER)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.wrmax = val;
opts = 1;
}
}
else if ( CMD(kw,gcint,5) )
{
if (val < 1 || val > 60)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.gcwait = val;
opts = 1;
}
}
else if ( CMD(kw,gcparm,6) )
{
if (val < -8 || val > 8)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.gcparm = val;
opts = 1;
}
}
else if ( CMD(kw,nostress,8) )
{
if (val < 0 || val > 1)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.nostress = val;
opts = 1;
}
}
else if ( CMD(kw,freepend,8) )
{
if (val < -1 || val > CCKD_MAX_FREEPEND)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.freepend = val;
opts = 1;
}
}
else if ( CMD(kw,fsync,5) )
{
if (val < 0 || val > 1)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.fsync = val;
opts = 1;
}
}
else if ( CMD(kw,trace,5) )
{
if (val < 0 || val > CCKD_MAX_TRACE)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
/* Disable tracing in case it's already active */
CCKD_TRACE *p = cckdblk.itrace;
cckdblk.itrace = NULL;
if (p)
{
SLEEP (1);
cckdblk.itrace = cckdblk.itracep = cckdblk.itracex = NULL;
cckdblk.itracen = 0;
free (p);
}
/* Get a new trace table */
if (val > 0)
{
p = calloc (val, sizeof(CCKD_TRACE));
if (p)
{
cckdblk.itracen = val;
cckdblk.itracex = p + val;
cckdblk.itracep = p;
cckdblk.itrace = p;
}
else
{
char buf[64];
MSGBUF( buf, "calloc(%d, %d)", val, (int)sizeof(CCKD_TRACE));
WRMSG (HHC00303, "E", 0, 0, buf, strerror(errno));
}
}
opts = 1;
}
}
else if ( CMD(kw,linuxnull,5) )
{
if (val < 0 || val > 1)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else
{
cckdblk.linuxnull = val;
opts = 1;
}
}
else if ( CMD(kw,gcstart,7) )
{
if (val < 0 || val > 1)
{
WRMSG(HHC00348, "E", val, kw);
return -1;
}
else if (val == 1)
{
DEVBLK *dev;
CCKDDASD_EXT *cckd;
TID tid;
int flag = 0;
cckd_lock_devchain(0);
for (dev = cckdblk.dev1st; dev; dev = cckd->devnext)
{
cckd = dev->cckd_ext;
obtain_lock (&cckd->filelock);
if (cckd->cdevhdr[cckd->sfn].free_total)
{
cckd->cdevhdr[cckd->sfn].options |= (CCKD_OPENED | CCKD_ORDWR);
cckd_write_chdr (dev);
flag = 1;
}
release_lock (&cckd->filelock);
}
cckd_unlock_devchain();
if (flag && cckdblk.gcs < cckdblk.gcmax)
{
rc = create_thread (&tid, JOINABLE, cckd_gcol, NULL, "cckd_gcol");
if (rc)
WRMSG(HHC00102, "E", strerror(rc));
}
}
}
else
{
WRMSG(HHC00349, "E", kw);
if (!cmd) return -1;
cckd_command_help ();
op = NULL;
}
}
if (cmd && opts)
cckd_command_opts();
return 0;
} /* end function cckd_command */
/*-------------------------------------------------------------------*/
/* Print internal trace */
/*-------------------------------------------------------------------*/
DLL_EXPORT void cckd_print_itrace()
{
CCKD_TRACE *i, *p; /* Trace table pointers */
int n; /* Trace Entries Printed */
WRMSG (HHC00399, "I");
if (!cckdblk.itrace)
return;
n = 0;
i = cckdblk.itrace;
cckdblk.itrace = NULL;
SLEEP (1);
p = cckdblk.itracep;
if (p >= cckdblk.itracex) p = i;
do
{
if ( strlen(*p) > 0 )
{
n++;
WRMSG(HHC00398, "I", (char *)p);
}
if (++p >= cckdblk.itracex) p = i;
} while (p != cckdblk.itracep);
if ( n == 0 )
WRMSG(HHC00397, "I");
memset(i, 0, cckdblk.itracen * sizeof(CCKD_TRACE));
cckdblk.itracep = i;
cckdblk.itrace = i;
} /* end function cckd_print_itrace */
/*-------------------------------------------------------------------*/
/* Write internal trace entry */
/*-------------------------------------------------------------------*/
void cckd_trace(DEVBLK *dev, char *msg, ...)
{
va_list vl;
struct timeval tv;
time_t t;
char tbuf[64];
CCKD_TRACE *p;
int l;
if (dev && (dev->ccwtrace||dev->ccwstep))
{
char *bfr;
int sz=1024,rc;
bfr=malloc(1024);
va_start(vl,msg);
while(1)
{
rc=vsnprintf(bfr,sz,msg,vl);
if(rc<0)
{
free(bfr);
bfr=NULL;
break;
}
if(rc<sz)
{
break;
}
sz+=256;
bfr=realloc(bfr,sz);
}
if(bfr)
{
WRMSG(HHC00396, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, bfr);
}
va_end(vl);
}
/* ISW FIXME : The following code has a potential */
/* for heap overrun (vsprintf) */
if (cckdblk.itrace)
{
gettimeofday(&tv, NULL);
t = tv.tv_sec;
strlcpy(tbuf, ctime(&t), sizeof(tbuf));
tbuf[19] = '\0';
va_start(vl,msg);
p = cckdblk.itracep++;
if (p >= cckdblk.itracex)
{
p = cckdblk.itrace;
cckdblk.itracep = p + 1;
}
if (p)
{
l = sprintf ((char *)p, "%s" "." "%6.6ld %1d:%04X> ",
tbuf+11, (long)tv.tv_usec, dev ? SSID_TO_LCSS(dev->ssid) : 0, dev ? dev->devnum : 0);
vsprintf ((char *)p + l, msg, vl);
}
}
} /* end function cckd_trace */
DEVHND cckddasd_device_hndinfo = {
&ckddasd_init_handler, /* Device Initialisation */
&ckddasd_execute_ccw, /* Device CCW execute */
&cckddasd_close_device, /* Device Close */
&ckddasd_query_device, /* Device Query */
NULL, /* Device Extended Query */
&cckddasd_start, /* Device Start channel pgm */
&cckddasd_end, /* Device End channel pgm */
&cckddasd_start, /* Device Resume channel pgm */
&cckddasd_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 Ouput */
NULL, /* Signal Adapter Sync */
NULL, /* Signal Adapter Output Mult */
NULL, /* QDIO subsys desc */
NULL, /* QDIO set subchan ind */
&ckddasd_hsuspend, /* Hercules suspend */
&ckddasd_hresume /* Hercules resume */
};
DEVHND cfbadasd_device_hndinfo = {
&fbadasd_init_handler, /* Device Initialisation */
&fbadasd_execute_ccw, /* Device CCW execute */
&cckddasd_close_device, /* Device Close */
&fbadasd_query_device, /* Device Query */
NULL, /* Device Extended Query */
&cckddasd_start, /* Device Start channel pgm */
&cckddasd_end, /* Device End channel pgm */
&cckddasd_start, /* Device Resume channel pgm */
&cckddasd_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 Ouput */
NULL, /* Signal Adapter Sync */
NULL, /* Signal Adapter Output Mult */
NULL, /* QDIO subsys desc */
NULL, /* QDIO set subchan ind */
&fbadasd_hsuspend, /* Hercules suspend */
&ckddasd_hresume /* Hercules resume */
};