mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-07-26 02:29:51 +02:00
6ff24aa680
* ctc_lcs.c: ensure pszTUNDevice is free'd 'pszTUNDevice = strdup( DEF_NETDEV );' is definitely lost. pszOATFilename check included to follow convention. * cckddasd.c: ensure dasdsfn is free'd 1. ckd_dasd_init_handler invokes cckd_sf_parse_sfn 2. ckd_dasd_init_handler invokes ckd64_dasd_init_handler 3. ckd64_dasd_init_handler invokes cckd_sf_parse_sfn Leaving previous dasdsfn dangling. Given how 'dasdsfn' is managed (e.g., in attach_device and dasd_close_device), it may be more elegant to compare the previous dasdsfn to the current pathname and throwing a warning if they are different. * config.c: ensure prev_buf is free'd * panel.c: zero prev_psw To avoid instances of memcmp'ing random stack data with curr_psw. * control.c: zero newregs tlb ARCH_DEP( maddr_l ) may reference tlb.asb and tlb.common - ensure newregs in program_return does not reference garbage stack data. * cckddasd64.c: zero L2_pad To avoid feeding random stack data to syscalls later on (e.g., write in cckd64_write). * panel.c: ensure messages are null delimited Uses of string.h functions and regexec assume the string is null delimited. This may not be true with some _PANMSG msg uses. * ecpsvm.c: ensure buffer is null delimited * refactor: CPU_BITMAP formatting When CPU_BITMAP is __uint128_t and F_CPU_BITMAP is '"%16.16"PRIX64', random/clobbered stack data will be referenced in the associated libc formatting routines used by fwritemsg due to the different stack alignment requirements of the variadic arguments. * refactor: reorganize history_init panel_command may be invoked (e.g., 'LPARNUM BASIC') prior to the panel display thread being initialized. This can lead to history_add being invoked prior to history_init (causing dangling pointers). * cckddasd64: zero the entire free block to avoid writing garbage to underlying file Per MemorySanitizer * w32util.c: set_thread_name buffer overflow Ensures correct nNumberOfArguments argument on x64. Updated the struct definition to match the Microsoft documentation: https://learn.microsoft.com/en-us/visualstudio/debugger/tips-for-debugging-threads SetThreadDescription was introduced in Windows 10 version 1607, so Kernel32.dll could technically be probed to see if that export exists.
4840 lines
163 KiB
C
4840 lines
163 KiB
C
/* CCKDDASD64.C (C) Copyright Roger Bowler, 1999-2012 */
|
|
/* (C) Copyright Greg Smith, 2002-2012 */
|
|
/* (C) and others 2013-2022 */
|
|
/* */
|
|
/* CCKD (Compressed CKD) Device Handler */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module contains device functions for compressed emulated */
|
|
/* count-key-data direct access storage devices. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#define _CCKDDASD64_C_
|
|
#define _HDASD_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "devtype.h"
|
|
#include "opcode.h"
|
|
#include "dasdblks.h"
|
|
#include "cckddasd.h"
|
|
#include "ccwarn.h"
|
|
|
|
DISABLE_GCC_UNUSED_SET_WARNING;
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed CKD dasd initialization */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_dasd_init_handler ( DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int i; /* Counter */
|
|
int fdflags; /* File flags */
|
|
char buf[32]; /* Work buffer */
|
|
|
|
UNREFERENCED( argc );
|
|
UNREFERENCED( argv );
|
|
|
|
/* Initialize the global cckd block if necessary */
|
|
if (memcmp( &cckdblk.id, CCKDBLK_ID, sizeof( cckdblk.id )))
|
|
cckd_dasd_init( 0, NULL );
|
|
|
|
/* Obtain area for cckd extension */
|
|
dev->cckd_ext = cckd = cckd_calloc( dev, "ext", 1, sizeof( CCKD64_EXT ));
|
|
if (cckd == NULL)
|
|
return -1;
|
|
|
|
/* Initialize locks and conditions */
|
|
|
|
initialize_lock( &cckd->cckdiolock );
|
|
MSGBUF( buf, "&cckd->cckdiolock %1d:%04X", LCSS_DEVNUM );
|
|
set_lock_name( &cckd->cckdiolock, buf );
|
|
|
|
initialize_lock( &cckd->filelock );
|
|
MSGBUF( buf, "&cckd->filelock %1d:%04X", LCSS_DEVNUM );
|
|
set_lock_name( &cckd->filelock, buf );
|
|
|
|
initialize_condition( &cckd->cckdiocond );
|
|
|
|
/* Initialize some variables */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
cckd->L1idx = cckd->sfx = cckd->L2_active = -1;
|
|
dev->cache = cckd->free_idx1st = -1;
|
|
cckd->fd[0] = dev->fd;
|
|
fdflags = get_file_accmode_flags( dev->fd );
|
|
cckd->open[0] = (fdflags & O_RDWR) ? CCKD_OPEN_RW : CCKD_OPEN_RO;
|
|
for (i = 1; i <= CCKD_MAX_SF; i++)
|
|
{
|
|
cckd->fd[i] = -1;
|
|
cckd->open[i] = CCKD_OPEN_NONE;
|
|
}
|
|
cckd->cckd_maxsize = CCKD64_MAXSIZE;
|
|
|
|
/* call the chkdsk function */
|
|
if (cckd64_chkdsk (dev, 0) < 0)
|
|
return -1;
|
|
|
|
/* Perform initial read */
|
|
if (cckd64_read_init (dev) < 0)
|
|
return -1;
|
|
if (cckd->fbadasd) dev->ckdtrksz = CFBA_BLKGRP_SIZE;
|
|
|
|
/* open the shadow files */
|
|
if (cckd64_sf_init (dev) < 0)
|
|
{
|
|
WRMSG (HHC00300, "E", LCSS_DEVNUM);
|
|
return -1;
|
|
}
|
|
|
|
/* Update the device handler routines */
|
|
if (cckd->ckddasd)
|
|
dev->hnd = &cckd_dasd_device_hndinfo;
|
|
else
|
|
dev->hnd = &cfba_dasd_device_hndinfo;
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
/* Insert the device into the cckd device queue */
|
|
cckd_lock_devchain(1);
|
|
{
|
|
add_dev_to_cckd_chain_locked( dev );
|
|
}
|
|
cckd_unlock_devchain();
|
|
|
|
cckdblk.batch = dev->batch;
|
|
|
|
if (cckdblk.batch)
|
|
{
|
|
cckdblk.nostress = 1;
|
|
cckdblk.freepend = 0;
|
|
cckdblk.linuxnull = 1;
|
|
}
|
|
|
|
return 0;
|
|
} /* end function cckd64_dasd_init_handler */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Close a Compressed CKD Device */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_dasd_close_device (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc, i; /* Return code, Loop index */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_dasd_close_device( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Wait for readaheads to finish */
|
|
obtain_lock(&cckdblk.ralock);
|
|
cckd->stopping = 1;
|
|
while (cckd->ras)
|
|
{
|
|
release_lock(&cckdblk.ralock);
|
|
USLEEP(1);
|
|
obtain_lock(&cckdblk.ralock);
|
|
}
|
|
release_lock(&cckdblk.ralock);
|
|
|
|
/* Flush the cache and wait for the writes to complete */
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cckd->stopping = 1;
|
|
cckd64_flush_cache( dev );
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
rc = timed_wait_condition_relative_usecs(
|
|
&cckd->cckdiocond, &cckd->cckdiolock, 1000000, NULL );
|
|
cckd->cckdwaiters--;
|
|
cckd64_flush_cache( dev );
|
|
if (EINTR == rc)
|
|
continue;
|
|
/* Prevent rare but possible hang at shutdown */
|
|
if (1
|
|
&& ETIMEDOUT == rc
|
|
&& (cckd->wrpending || cckd->cckdioact)
|
|
&& sysblk.shutdown
|
|
)
|
|
{
|
|
CCKD_TRACE( "closing device while wrpending=%d cckdioact=%d",
|
|
cckd->wrpending, cckd->cckdioact );
|
|
// "%1d:%04X CCKD file %s: closing device while wrpending=%d cckdioact=%d"
|
|
WRMSG( HHC00381, "W", LCSS_DEVNUM, dev->filename,
|
|
cckd->wrpending, cckd->cckdioact );
|
|
break;
|
|
}
|
|
}
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
cckd64_purge_cache( dev );
|
|
cckd64_purge_l2( dev );
|
|
dev->bufcur = -1;
|
|
dev->cache = -1;
|
|
if (cckd->newbuf)
|
|
cckd_free( dev, "newbuf", cckd->newbuf );
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* Remove the device from the cckd queue */
|
|
cckd_lock_devchain(1);
|
|
{
|
|
remove_dev_from_cckd_chain_locked( dev );
|
|
}
|
|
cckd_unlock_devchain();
|
|
|
|
/* harden the file */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
cckd64_harden (dev);
|
|
|
|
/* close the shadow files */
|
|
for (i = 1; i <= cckd->sfn; i++)
|
|
{
|
|
cckd64_close (dev, i);
|
|
cckd->open[i] = 0;
|
|
}
|
|
|
|
/* free the level 1 tables */
|
|
for (i = 0; i <= cckd->sfn; i++)
|
|
cckd->L1tab[i] = cckd_free (dev, "l1", cckd->L1tab[i]);
|
|
|
|
/* reset the device handler */
|
|
if (cckd->ckddasd)
|
|
dev->hnd = &ckd_dasd_device_hndinfo;
|
|
else
|
|
dev->hnd = &fba_dasd_device_hndinfo;
|
|
|
|
/* write some statistics */
|
|
if (!dev->batch && !cckdblk.nosfd)
|
|
cckd64_sf_stats (dev);
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
/* If no more devices then perform global termination */
|
|
cckd_dasd_term_if_appropriate();
|
|
|
|
/* Destroy the cckd extension's locks and conditions */
|
|
destroy_lock( &cckd->cckdiolock );
|
|
destroy_lock( &cckd->filelock );
|
|
destroy_condition( &cckd->cckdiocond );
|
|
|
|
/* free the cckd extension itself */
|
|
dev->cckd_ext = cckd_free (dev, "ext", cckd);
|
|
|
|
if (dev->dasdsfn) free (dev->dasdsfn);
|
|
dev->dasdsfn = NULL;
|
|
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
|
|
dev->buf = NULL;
|
|
dev->bufsize = 0;
|
|
|
|
/* return to caller */
|
|
return 0;
|
|
} /* end function cckd64_dasd_close_device */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed ckd start/resume channel program */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_dasd_start (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
U16 devnum = 0; /* Last active device number */
|
|
int trk = 0; /* Last active track */
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_dasd_start( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "start i/o file[%d] bufcur %d cache[%d]",
|
|
cckd->sfn, dev->bufcur, dev->cache);
|
|
|
|
/* Reset buffer offsets */
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = cckd->ckddasd ? dev->ckdtrksz : CFBA_BLKGRP_SIZE;
|
|
|
|
/* Check for merge */
|
|
obtain_lock(&cckd->cckdiolock);
|
|
if (cckd->merging)
|
|
{
|
|
CCKD_TRACE( "start i/o waiting for merge%s","");
|
|
while (cckd->merging)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
|
|
cckd->cckdwaiters--;
|
|
}
|
|
dev->bufcur = dev->cache = -1;
|
|
}
|
|
cckd->cckdioact = 1;
|
|
|
|
cache_lock(CACHE_DEVBUF);
|
|
|
|
if (dev->cache >= 0)
|
|
CCKD_CACHE_GETKEY(dev->cache, devnum, trk);
|
|
|
|
/* Check if previous active entry is still valid and not busy */
|
|
if (dev->cache >= 0 && dev->devnum == devnum && dev->bufcur == trk
|
|
&& !(cache_getflag(CACHE_DEVBUF, dev->cache) & CCKD_CACHE_IOBUSY))
|
|
{
|
|
/* Make the entry active again */
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_ACTIVE);
|
|
|
|
/* If the entry is pending write then change it to `updated' */
|
|
if (cache_getflag(CACHE_DEVBUF, dev->cache) & CCKD_CACHE_WRITE)
|
|
{
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_WRITE, CCKD_CACHE_UPDATED);
|
|
cckd->wrpending--;
|
|
if (cckd->cckdwaiters && !cckd->wrpending)
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
}
|
|
}
|
|
else
|
|
dev->bufcur = dev->cache = -1;
|
|
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
return;
|
|
|
|
} /* end function cckd64_dasd_start */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed ckd end/suspend channel program */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_dasd_end (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_dasd_end( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Update length if previous image was updated */
|
|
if (dev->bufupd && dev->bufcur >= 0 && dev->cache >= 0)
|
|
{
|
|
dev->buflen = cckd_trklen (dev, dev->buf);
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
}
|
|
|
|
dev->bufupd = 0;
|
|
|
|
CCKD_TRACE( "end i/o bufcur %d cache[%d] waiters %d",
|
|
dev->bufcur, dev->cache, cckd->cckdwaiters);
|
|
|
|
obtain_lock (&cckd->cckdiolock);
|
|
|
|
cckd->cckdioact = 0;
|
|
|
|
/* Make the current entry inactive */
|
|
if (dev->cache >= 0)
|
|
{
|
|
cache_lock (CACHE_DEVBUF);
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_ACTIVE, 0);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
}
|
|
|
|
/* Cause writers to start after first update */
|
|
if (cckd->updated && (cckdblk.wrs == 0 || cckd->cckdwaiters != 0))
|
|
cckd64_flush_cache (dev);
|
|
else if (cckd->cckdwaiters)
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
} /* end function cckd64_dasd_end */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Open a cckd file */
|
|
/* */
|
|
/* If O_CREAT is not set and mode is non-zero then the error message */
|
|
/* will be suppressed. */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_open (DEVBLK *dev, int sfx, int flags, mode_t mode)
|
|
{
|
|
CCKD64_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)
|
|
cckd64_close (dev, sfx);
|
|
|
|
hostpath(pathname, cckd_sf_name (dev, sfx), sizeof(pathname));
|
|
cckd->fd[sfx] = HOPEN (pathname, flags, mode);
|
|
if (sfx == 0) dev->fd = cckd->fd[sfx];
|
|
|
|
if (cckd->fd[sfx] >= 0)
|
|
cckd->open[sfx] = flags & O_RDWR ? CCKD_OPEN_RW :
|
|
cckd->open[sfx] == CCKD_OPEN_RW ?
|
|
CCKD_OPEN_RD : CCKD_OPEN_RO;
|
|
else
|
|
{
|
|
if (err)
|
|
{
|
|
WRMSG (HHC00301, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
|
|
"open()", strerror(errno));
|
|
CCKD_TRACE( "file[%d] fd[%d] open %s error flags %8.8x mode %8.8x",
|
|
sfx, cckd->fd[sfx], cckd_sf_name (dev, sfx), flags, mode);
|
|
cckd_print_itrace ();
|
|
}
|
|
cckd->open[sfx] = CCKD_OPEN_NONE;
|
|
}
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] open %s, flags %8.8x mode %8.8x",
|
|
sfx, cckd->fd[sfx], cckd_sf_name (dev, sfx), flags, mode);
|
|
|
|
return cckd->fd[sfx];
|
|
|
|
} /* end function cckd64_open */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Close a cckd file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_close (DEVBLK *dev, int sfx)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc = 0; /* Return code */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] close %s",
|
|
sfx, cckd->fd[sfx], cckd_sf_name(dev, sfx));
|
|
|
|
if (cckd->fd[sfx] >= 0)
|
|
rc = close (cckd->fd[sfx]);
|
|
|
|
if (rc < 0)
|
|
{
|
|
WRMSG (HHC00301, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
|
|
"close()", strerror(errno));
|
|
cckd_print_itrace ();
|
|
}
|
|
|
|
cckd->fd[sfx] = -1;
|
|
if (sfx == 0) dev->fd = -1;
|
|
|
|
return rc;
|
|
|
|
} /* end function cckd64_close */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read from a cckd file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read( DEVBLK* dev, int sfx, U64 off, void* buf, unsigned int len )
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] read, off 0x%16.16"PRIx64" len %d",
|
|
sfx, cckd->fd[ sfx ], off, len );
|
|
|
|
/* Seek to specified offset */
|
|
if (lseek( cckd->fd[ sfx ], off, SEEK_SET ) < 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"lseek()", off, strerror( errno ));
|
|
cckd_print_itrace();
|
|
return -1;
|
|
}
|
|
|
|
/* Read the data */
|
|
rc = read( cckd->fd[ sfx ], buf, len );
|
|
if (rc < (int)len)
|
|
{
|
|
if (rc < 0)
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"read()", off, strerror( errno ));
|
|
else
|
|
{
|
|
char buf[128];
|
|
MSGBUF( buf, "read incomplete: read %d, expected %d", rc, len );
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"read()", off, buf );
|
|
}
|
|
cckd_print_itrace();
|
|
return -1;
|
|
}
|
|
|
|
return rc;
|
|
|
|
} /* end function cckd64_read */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write to a cckd file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_write( DEVBLK* dev, int sfx, U64 off, void* buf, unsigned int len )
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc = 0; /* Return code */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] write, off 0x%16.16"PRIx64" len %d",
|
|
sfx, cckd->fd[ sfx ], off, len );
|
|
|
|
/* Seek to specified offset */
|
|
if (lseek( cckd->fd[ sfx ], off, SEEK_SET ) < 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"lseek()", off, strerror( errno ));
|
|
cckd_print_itrace();
|
|
return -1;
|
|
}
|
|
|
|
/* Write the data */
|
|
rc = write( cckd->fd[ sfx ], buf, len );
|
|
if (rc < (int)len)
|
|
{
|
|
if (rc < 0)
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"write()", off, strerror( errno ));
|
|
else
|
|
{
|
|
char buf[128];
|
|
MSGBUF( buf, "write incomplete: write %d, expected %d", rc, len );
|
|
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
|
|
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
|
|
"write()", off, buf );
|
|
}
|
|
cckd_print_itrace();
|
|
return -1;
|
|
}
|
|
|
|
return rc;
|
|
|
|
} /* end function cckd64_write */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Truncate a cckd file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_ftruncate(DEVBLK *dev, int sfx, U64 off)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] fd[%d] ftruncate, off 0x%16.16"PRIx64,
|
|
sfx, cckd->fd[sfx], off);
|
|
|
|
/* Truncate the file */
|
|
if (ftruncate (cckd->fd[sfx], off) < 0)
|
|
{
|
|
WRMSG (HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
|
|
"ftruncate()", off, strerror(errno));
|
|
cckd_print_itrace ();
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_ftruncate */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed ckd read track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read_track (DEVBLK *dev, int trk, BYTE *unitstat)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int len; /* Compressed length */
|
|
BYTE *newbuf; /* Uncompressed buffer */
|
|
int cache; /* New active cache entry */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Update length if previous image was updated */
|
|
if (dev->bufupd && dev->bufcur >= 0 && dev->cache >= 0)
|
|
{
|
|
dev->buflen = cckd_trklen (dev, dev->buf);
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
}
|
|
|
|
/* Reset buffer offsets */
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
|
|
/* Check if reading the same track image */
|
|
if (trk == dev->bufcur && dev->cache >= 0)
|
|
{
|
|
/* Track image may be compressed */
|
|
if ((dev->buf[0] & CCKD_COMPRESS_MASK) != 0
|
|
&& (dev->buf[0] & dev->comps) == 0)
|
|
{
|
|
len = cache_getval(CACHE_DEVBUF, dev->cache);
|
|
newbuf = cckd64_uncompress (dev, dev->buf, len, dev->ckdtrksz, trk);
|
|
if (newbuf == NULL) {
|
|
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
cache_setbuf (CACHE_DEVBUF, dev->cache, newbuf, dev->ckdtrksz);
|
|
dev->buf = newbuf;
|
|
dev->buflen = cckd_trklen (dev, newbuf);
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
|
|
dev->bufupd = 0;
|
|
CCKD_TRACE( "read trk %d uncompressed len %d",
|
|
trk, dev->buflen);
|
|
}
|
|
|
|
dev->comp = dev->buf[0] & CCKD_COMPRESS_MASK;
|
|
if (dev->comp != 0) dev->compoff = CKD_TRKHDR_SIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
CCKD_TRACE( "read trk %d (%s)", trk, "asynchronous");
|
|
|
|
/* read the new track */
|
|
dev->bufupd = 0;
|
|
cache = cckd64_read_trk (dev, trk, 0, unitstat);
|
|
if (cache < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
|
|
dev->cache = cache;
|
|
dev->buf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
|
|
dev->bufcur = trk;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
dev->buflen = cache_getval (CACHE_DEVBUF, dev->cache);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
|
|
|
|
dev->comp = dev->buf[0] & CCKD_COMPRESS_MASK;
|
|
if (dev->comp != 0) dev->compoff = CKD_TRKHDR_SIZE;
|
|
|
|
/* If the image is compressed then call ourself recursively
|
|
to cause the image to get uncompressed */
|
|
if (dev->comp != 0 && (dev->comp & dev->comps) == 0)
|
|
rc = cckd64_read_track (dev, trk, unitstat);
|
|
else
|
|
rc = 0;
|
|
|
|
return rc;
|
|
} /* end function cckd64_read_track */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed ckd update track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_update_track (DEVBLK *dev, int trk, int off,
|
|
BYTE *buf, int len, BYTE *unitstat)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_update_track( dev, trk, off, buf, len, unitstat );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Error if opened read-only */
|
|
if (dev->ckdrdonly && cckd->sfn == 0)
|
|
{
|
|
ckd_build_sense (dev, SENSE_EC, SENSE1_WRI, 0,FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
|
|
/* If the track is not current or compressed then read it.
|
|
`dev->comps' is set to zero forcing the read routine to
|
|
uncompress the image. */
|
|
if (trk != dev->bufcur || (dev->buf[0] & CCKD_COMPRESS_MASK) != 0)
|
|
{
|
|
dev->comps = 0;
|
|
rc = (dev->hnd->read) (dev, trk, unitstat);
|
|
if (rc < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Invalid track format if going past buffer end */
|
|
if (off + len > dev->ckdtrksz)
|
|
{
|
|
ckd_build_sense (dev, 0, SENSE1_ITF, 0, 0, 0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
|
|
/* Copy the data into the buffer */
|
|
if (buf && len > 0) memcpy (dev->buf + off, buf, len);
|
|
|
|
CCKD_TRACE( "updt trk %d offset %"PRId64" length %d",
|
|
trk, off, len);
|
|
|
|
/* Update the cache entry */
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_UPDATED | CCKD_CACHE_USED);
|
|
cckd->updated = 1;
|
|
|
|
/* Notify the shared server of the update */
|
|
if (!dev->bufupd)
|
|
{
|
|
dev->bufupd = 1;
|
|
shared_update_notify (dev, trk);
|
|
}
|
|
|
|
/* Ensure the Dasd Hardener is running, if it's supposed to be. */
|
|
if (cckdblk.dhint > 0)
|
|
cckd_dhstart( false );
|
|
|
|
return len;
|
|
|
|
} /* end function cckd64_update_track */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return used cylinders */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_used (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int L1idx, l2x; /* Lookup table indexes */
|
|
int sfx; /* Shadow file suffix */
|
|
CCKD64_L2ENT l2; /* Copied level 2 entry */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_used( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* Find the last used level 1 table entry */
|
|
for (L1idx = cckd->cdevhdr[0].num_L1tab - 1; L1idx > 0; L1idx--)
|
|
{
|
|
sfx = cckd->sfn;
|
|
while (cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE && sfx > 0) sfx--;
|
|
if (cckd->L1tab[sfx][L1idx]) break;
|
|
}
|
|
|
|
/* Find the last used level 2 table entry */
|
|
for (l2x = 255; l2x >= 0; l2x--)
|
|
{
|
|
rc = cckd64_read_l2ent (dev, &l2, L1idx * 256 + l2x);
|
|
if (rc < 0 || l2.L2_trkoff != 0) break;
|
|
}
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
return (L1idx * 256 + l2x + dev->ckdheads) / dev->ckdheads;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed fba read block(s) */
|
|
/*-------------------------------------------------------------------*/
|
|
int cfba64_read_block (DEVBLK *dev, int blkgrp, BYTE *unitstat)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int cache; /* New active cache entry */
|
|
BYTE *cbuf; /* -> cache buffer */
|
|
BYTE *newbuf; /* Uncompressed buffer */
|
|
int len; /* Compressed length */
|
|
int maxlen; /* Size for cache entry */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (dev->cache >= 0)
|
|
cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
|
|
else
|
|
cbuf = NULL;
|
|
maxlen = CFBA_BLKGRP_SIZE + CKD_TRKHDR_SIZE;
|
|
|
|
/* Return if reading the same track image */
|
|
if (blkgrp == dev->bufcur && dev->cache >= 0)
|
|
{
|
|
/* Block group image may be compressed */
|
|
if ((cbuf[0] & CCKD_COMPRESS_MASK) != 0
|
|
&& (cbuf[0] & dev->comps) == 0)
|
|
{
|
|
len = cache_getval(CACHE_DEVBUF, dev->cache) + CKD_TRKHDR_SIZE;
|
|
newbuf = cckd64_uncompress (dev, cbuf, len, maxlen, blkgrp);
|
|
if (newbuf == NULL)
|
|
{
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
cache_setbuf (CACHE_DEVBUF, dev->cache, newbuf, maxlen);
|
|
cbuf = newbuf;
|
|
dev->buf = newbuf + CKD_TRKHDR_SIZE;
|
|
dev->buflen = CFBA_BLKGRP_SIZE;
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
|
|
dev->bufupd = 0;
|
|
CCKD_TRACE( "read bkgrp %d uncompressed len %d",
|
|
blkgrp, dev->buflen);
|
|
}
|
|
|
|
dev->comp = cbuf[0] & CCKD_COMPRESS_MASK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
CCKD_TRACE( "read blkgrp %d (%s)", blkgrp, "asynchronous");
|
|
|
|
/* Read the new blkgrp */
|
|
dev->bufupd = 0;
|
|
cache = cckd64_read_trk (dev, blkgrp, 0, unitstat);
|
|
if (cache < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
dev->cache = cache;
|
|
cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
|
|
dev->buf = cbuf + CKD_TRKHDR_SIZE;
|
|
dev->bufcur = blkgrp;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = CFBA_BLKGRP_SIZE;
|
|
dev->buflen = CFBA_BLKGRP_SIZE;
|
|
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
|
|
dev->comp = cbuf[0] & CCKD_COMPRESS_MASK;
|
|
|
|
/* If the image is compressed then call ourself recursively
|
|
to cause the image to get uncompressed. This is because
|
|
`bufcur' will match blkgrp and `comps' won't match `comp' */
|
|
if (dev->comp != 0 && (dev->comp & dev->comps) == 0)
|
|
rc = cfba64_read_block (dev, blkgrp, unitstat);
|
|
else
|
|
rc = 0;
|
|
|
|
return rc;
|
|
} /* end function cfba64_read_block */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Compressed fba write block(s) */
|
|
/*-------------------------------------------------------------------*/
|
|
int cfba64_write_block (DEVBLK *dev, int blkgrp, int off,
|
|
BYTE *buf, int len, BYTE *unitstat)
|
|
{
|
|
CCKD64_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 cfba64_write_block */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return used blocks */
|
|
/*-------------------------------------------------------------------*/
|
|
int cfba64_used (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int L1idx, l2x; /* Lookup table indexes */
|
|
int sfx; /* Shadow file suffix */
|
|
CCKD64_L2ENT l2; /* Copied level 2 entry */
|
|
|
|
if (!dev->cckd64)
|
|
return cfba_used( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* Find the last used level 1 table entry */
|
|
for (L1idx = cckd->cdevhdr[0].num_L1tab - 1; L1idx > 0; L1idx--)
|
|
{
|
|
sfx = cckd->sfn;
|
|
while (cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE && sfx > 0) sfx--;
|
|
if (cckd->L1tab[sfx][L1idx]) break;
|
|
}
|
|
|
|
/* Find the last used level 2 table entry */
|
|
for (l2x = 255; l2x >= 0; l2x--)
|
|
{
|
|
rc = cckd64_read_l2ent (dev, &l2, L1idx * 256 + l2x);
|
|
if (rc < 0 || l2.L2_trkoff != 0) break;
|
|
}
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
return (L1idx * 256 + l2x + CFBA_BLKS_PER_GRP) / CFBA_BLKS_PER_GRP;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read a track image */
|
|
/* */
|
|
/* This routine can be called by the i/o thread (`ra' == 0) or */
|
|
/* by readahead threads (0 < `ra' <= cckdblk.ramax). */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read_trk(DEVBLK *dev, int trk, int ra, BYTE *unitstat)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int fnd; /* Cache index for hit */
|
|
int lru; /* Oldest unused cache index */
|
|
int len; /* Length of track image */
|
|
int maxlen; /* Length for buffer */
|
|
int curtrk = -1; /* Current track (at entry) */
|
|
U16 devnum; /* Device number */
|
|
U32 oldtrk; /* Stolen track number */
|
|
U32 flag; /* Cache flag */
|
|
BYTE *buf; /* Read buffer */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_read_trk( dev, trk, ra, unitstat );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "%d rdtrk %d", ra, trk);
|
|
|
|
maxlen = cckd->ckddasd ? dev->ckdtrksz
|
|
: CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE;
|
|
|
|
if (!ra) obtain_lock (&cckd->cckdiolock);
|
|
|
|
cache_lock (CACHE_DEVBUF);
|
|
|
|
/* Inactivate the old entry */
|
|
if (!ra)
|
|
{
|
|
curtrk = dev->bufcur;
|
|
if (dev->cache >= 0)
|
|
cache_setflag(CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_ACTIVE, 0);
|
|
dev->bufcur = dev->cache = -1;
|
|
}
|
|
|
|
cckd_read_trk_retry:
|
|
|
|
/* scan the cache array for the track */
|
|
fnd = cache_lookup (CACHE_DEVBUF, CCKD_CACHE_SETKEY(dev->devnum, trk), &lru);
|
|
|
|
/* check for cache hit */
|
|
if (fnd >= 0)
|
|
{
|
|
if (ra) /* readahead doesn't care about a cache hit */
|
|
{ cache_unlock (CACHE_DEVBUF);
|
|
return fnd;
|
|
}
|
|
|
|
/* Mark the new entry active */
|
|
cache_setflag(CACHE_DEVBUF, fnd, ~0, CCKD_CACHE_ACTIVE | CCKD_CACHE_USED);
|
|
cache_setage(CACHE_DEVBUF, fnd);
|
|
|
|
/* If the entry is pending write then change it to `updated' */
|
|
if (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_WRITE)
|
|
{
|
|
cache_setflag(CACHE_DEVBUF, fnd, ~CCKD_CACHE_WRITE, CCKD_CACHE_UPDATED);
|
|
cckd->wrpending--;
|
|
if (cckd->cckdwaiters && !cckd->wrpending)
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
}
|
|
buf = cache_getbuf(CACHE_DEVBUF, fnd, 0);
|
|
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
CCKD_TRACE( "%d rdtrk[%d] %d cache hit buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
ra, fnd, trk, buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
|
|
|
|
cckdblk.stats_switches++; cckd->switches++;
|
|
cckdblk.stats_cachehits++; cckd->cachehits++;
|
|
|
|
/* if read/write is in progress then wait for it to finish */
|
|
while (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_IOBUSY)
|
|
{
|
|
cckdblk.stats_iowaits++;
|
|
CCKD_TRACE( "%d rdtrk[%d] %d waiting for %s", ra, fnd, trk,
|
|
cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_READING ?
|
|
"read" : "write");
|
|
cache_setflag (CACHE_DEVBUF, fnd, ~0, CCKD_CACHE_IOWAIT);
|
|
cckd->cckdwaiters++;
|
|
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
|
|
cckd->cckdwaiters--;
|
|
cache_setflag (CACHE_DEVBUF, fnd, ~CCKD_CACHE_IOWAIT, 0);
|
|
CCKD_TRACE( "%d rdtrk[%d] %d io wait complete",
|
|
ra, fnd, trk);
|
|
}
|
|
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
/* Asynchronously schedule readaheads */
|
|
if (curtrk > 0 && trk > curtrk && trk <= curtrk + 2)
|
|
cckd_readahead (dev, trk);
|
|
|
|
return fnd;
|
|
|
|
} /* cache hit */
|
|
|
|
CCKD_TRACE( "%d rdtrk[%d] %d cache miss", ra, lru, trk);
|
|
|
|
/* If no cache entry was stolen, then flush all outstanding writes.
|
|
This requires us to release our locks. cache_wait should be
|
|
called with only the cache_lock held. Fortunately, cache waits
|
|
occur very rarely. */
|
|
if (lru < 0) /* No available entry to be stolen */
|
|
{
|
|
CCKD_TRACE( "%d rdtrk[%d] %d no available cache entry",
|
|
ra, lru, trk);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
if (!ra) release_lock (&cckd->cckdiolock);
|
|
cckd64_flush_cache_all();
|
|
cache_lock (CACHE_DEVBUF);
|
|
cckdblk.stats_cachewaits++;
|
|
cache_wait (CACHE_DEVBUF);
|
|
if (!ra)
|
|
{
|
|
cache_unlock (CACHE_DEVBUF);
|
|
obtain_lock (&cckd->cckdiolock);
|
|
cache_lock (CACHE_DEVBUF);
|
|
}
|
|
goto cckd_read_trk_retry;
|
|
}
|
|
|
|
CCKD_CACHE_GETKEY(lru, devnum, oldtrk);
|
|
if (devnum != 0)
|
|
{
|
|
CCKD_TRACE( "%d rdtrk[%d] %d dropping %4.4X:%d from cache",
|
|
ra, lru, trk, devnum, oldtrk);
|
|
if (!(cache_getflag(CACHE_DEVBUF, lru) & CCKD_CACHE_USED))
|
|
{
|
|
cckdblk.stats_readaheadmisses++; cckd->misses++;
|
|
}
|
|
}
|
|
|
|
/* Initialize the entry */
|
|
cache_setkey(CACHE_DEVBUF, lru, CCKD_CACHE_SETKEY(dev->devnum, trk));
|
|
cache_setflag(CACHE_DEVBUF, lru, 0, CCKD_CACHE_READING);
|
|
cache_setage(CACHE_DEVBUF, lru);
|
|
cache_setval(CACHE_DEVBUF, lru, 0);
|
|
if (!ra)
|
|
{
|
|
cckdblk.stats_switches++; cckd->switches++;
|
|
cckdblk.stats_cachemisses++;
|
|
cache_setflag(CACHE_DEVBUF, lru, ~0, CCKD_CACHE_ACTIVE|CCKD_CACHE_USED);
|
|
}
|
|
cache_setflag(CACHE_DEVBUF, lru, ~CACHE_TYPE,
|
|
cckd->ckddasd ? DEVBUF_TYPE_CCKD : DEVBUF_TYPE_CFBA);
|
|
buf = cache_getbuf(CACHE_DEVBUF, lru, maxlen);
|
|
|
|
CCKD_TRACE( "%d rdtrk[%d] %d buf %p len %d",
|
|
ra, lru, trk, buf, cache_getlen(CACHE_DEVBUF, lru));
|
|
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
if (!ra) release_lock (&cckd->cckdiolock);
|
|
|
|
/* Asynchronously schedule readaheads */
|
|
if (!ra && curtrk > 0 && trk > curtrk && trk <= curtrk + 2)
|
|
cckd_readahead (dev, trk);
|
|
|
|
/* Clear the buffer if batch mode */
|
|
if (dev->batch) memset(buf, 0, maxlen);
|
|
|
|
/* Read the track image */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
len = cckd64_read_trkimg (dev, buf, trk, unitstat);
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
cache_setval (CACHE_DEVBUF, lru, len);
|
|
|
|
obtain_lock (&cckd->cckdiolock);
|
|
|
|
/* Turn off the READING bit */
|
|
cache_lock (CACHE_DEVBUF);
|
|
flag = cache_setflag(CACHE_DEVBUF, lru, ~CCKD_CACHE_READING, 0);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
/* Wakeup other thread waiting for this read */
|
|
if (cckd->cckdwaiters && (flag & CCKD_CACHE_IOWAIT))
|
|
{ CCKD_TRACE( "%d rdtrk[%d] %d signalling read complete",
|
|
ra, lru, trk);
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
}
|
|
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
if (ra)
|
|
{
|
|
cckdblk.stats_readaheads++; cckd->readaheads++;
|
|
}
|
|
|
|
CCKD_TRACE( "%d rdtrk[%d] %d complete buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
ra, lru, trk, buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
|
|
|
|
if (cache_busy_percent(CACHE_DEVBUF) > 80) cckd64_flush_cache_all();
|
|
|
|
return lru;
|
|
|
|
} /* end function cckd64_read_trk */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Flush updated cache entries for a device */
|
|
/* */
|
|
/* Caller holds the cckd->cckdiolock */
|
|
/* cckdblk.wrlock then cache_lock is obtained and released */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_flush_cache(DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
TID tid; /* Writer thread id */
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_flush_cache( dev );
|
|
return;
|
|
}
|
|
|
|
/* Scan cache for updated cache entries */
|
|
obtain_lock (&cckdblk.wrlock);
|
|
cache_lock (CACHE_DEVBUF);
|
|
cache_scan (CACHE_DEVBUF, cckd64_flush_cache_scan, dev);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
/* Schedule the writer if any writes are pending */
|
|
if (cckdblk.wrpending)
|
|
{
|
|
if (cckdblk.wrwaiting)
|
|
signal_condition (&cckdblk.wrcond);
|
|
else if (cckdblk.wrs < cckdblk.wrmax)
|
|
{
|
|
/* Schedule a new writer thread */
|
|
if (!cckdblk.batch || cckdblk.batchml > 1)
|
|
// "Starting thread %s, active=%d, started=%d, max=%d"
|
|
WRMSG(HHC00107, "I", CCKD_WR_THREAD_NAME "()",
|
|
cckdblk.wra, cckdblk.wrs, cckdblk.wrmax);
|
|
|
|
++cckdblk.wrs;
|
|
|
|
/* Release lock across thread create to prevent interlock */
|
|
release_lock(&cckdblk.wrlock);
|
|
{
|
|
rc = create_thread( &tid, JOINABLE, cckd_writer, NULL, CCKD_WR_THREAD_NAME );
|
|
}
|
|
obtain_lock(&cckdblk.wrlock);
|
|
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread() for %s %d of %d: %s"
|
|
WRMSG(HHC00106, "E", CCKD_WR_THREAD_NAME "()",
|
|
cckdblk.wrs-1, cckdblk.wrmax, strerror(rc));
|
|
|
|
--cckdblk.wrs;
|
|
}
|
|
}
|
|
}
|
|
|
|
release_lock (&cckdblk.wrlock);
|
|
}
|
|
|
|
int cckd64_flush_cache_scan (int *answer, int ix, int i, void *data)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
U16 devnum; /* Cached device number */
|
|
U32 trk; /* Cached track */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
|
|
UNREFERENCED(answer);
|
|
cckd = dev->cckd_ext;
|
|
CCKD_CACHE_GETKEY(i, devnum, trk);
|
|
if ((cache_getflag(ix,i) & CACHE_BUSY) == CCKD_CACHE_UPDATED
|
|
&& dev->devnum == devnum)
|
|
{
|
|
cache_setflag (ix, i, ~CCKD_CACHE_UPDATED, CCKD_CACHE_WRITE);
|
|
++cckd->wrpending;
|
|
++cckdblk.wrpending;
|
|
CCKD_TRACE( "flush file[%d] cache[%d] %4.4X trk %d",
|
|
cckd->sfn, i, devnum, trk);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void cckd64_flush_cache_all()
|
|
{
|
|
CCKD64_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)
|
|
cckd64_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 cckd64_purge_cache(DEVBLK *dev)
|
|
{
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_purge_cache( dev );
|
|
return;
|
|
}
|
|
|
|
/* Scan cache and purge entries */
|
|
cache_lock (CACHE_DEVBUF);
|
|
cache_scan (CACHE_DEVBUF, cckd64_purge_cache_scan, dev);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
}
|
|
int cckd64_purge_cache_scan (int *answer, int ix, int i, void *data)
|
|
{
|
|
U16 devnum; /* Cached device number */
|
|
U32 trk; /* Cached track */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
|
|
UNREFERENCED(answer);
|
|
CCKD_CACHE_GETKEY(i, devnum, trk);
|
|
if (dev->devnum == devnum)
|
|
{
|
|
cache_release (ix, i, 0);
|
|
CCKD_TRACE( "purge cache[%d] %4.4X trk %d purged",
|
|
i, devnum, trk);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cckd writer thread helper: write the cached track image */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_writer_write( int writer, int o )
|
|
{
|
|
CCKD64_EXT* cckd; /* -> cckd extension */
|
|
DEVBLK* dev; /* Device block */
|
|
U16 devnum; /* Device number */
|
|
int trk; /* Track number */
|
|
BYTE* buf; /* Buffer */
|
|
BYTE* bufp; /* Buffer to be written */
|
|
int len, bufl; /* Buffer lengths */
|
|
int comp; /* Compression algorithm */
|
|
int parm; /* Compression parameter */
|
|
U32 flag; /* Cache flag */
|
|
BYTE buf2[ 64*1024 ]; /* 64K Compress buffer */
|
|
|
|
/* Prepare to compress */
|
|
CCKD_CACHE_GETKEY( o, devnum, trk );
|
|
dev = cckd_find_device_by_devnum( devnum );
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_writer_write( writer, o );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
buf = cache_getbuf( CACHE_DEVBUF, o, 0 );
|
|
len = cckd_trklen( dev, buf );
|
|
|
|
comp = len < CCKD_COMPRESS_MIN ? CCKD_COMPRESS_NONE :
|
|
cckdblk.comp == 0xff ? cckd->cdevhdr[ cckd->sfn ].cmp_algo
|
|
: cckdblk.comp;
|
|
|
|
parm = cckdblk.compparm < 0 ? cckd->cdevhdr[ cckd->sfn ].cmp_parm
|
|
: cckdblk.compparm;
|
|
|
|
CCKD_TRACE( "%d wrtrk[%d] %d len %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
writer, o, trk, len, buf, buf[0], buf[1],buf[2],buf[3],buf[4] );
|
|
|
|
/* Compress the image if not null */
|
|
if ((len = cckd64_check_null_trk( dev, buf, trk, len )) > CKD_NULLTRK_FMTMAX)
|
|
{
|
|
/* Stress adjustments */
|
|
if (1
|
|
&& !cckdblk.nostress
|
|
&& (0
|
|
|| cache_waiters ( CACHE_DEVBUF )
|
|
|| cache_busy ( CACHE_DEVBUF ) > 90
|
|
)
|
|
)
|
|
{
|
|
cckdblk.stats_stresswrites++;
|
|
|
|
comp = len < CCKD_STRESS_MINLEN ? CCKD_COMPRESS_NONE
|
|
: CCKD_STRESS_COMP;
|
|
|
|
parm = cache_busy(CACHE_DEVBUF) <= 95 ? CCKD_STRESS_PARM1
|
|
: CCKD_STRESS_PARM2;
|
|
}
|
|
|
|
/* Compress the track image */
|
|
CCKD_TRACE( "%d wrtrk[%d] %d comp %s parm %d",
|
|
writer, o, trk, compname[ comp ], parm );
|
|
|
|
bufp = (BYTE*) &buf2;
|
|
bufl = cckd_compress( dev, &bufp, buf, len, comp, parm );
|
|
|
|
CCKD_TRACE( "%d wrtrk[%d] %d compressed length %d",
|
|
writer, o, trk, bufl );
|
|
}
|
|
else
|
|
{
|
|
bufp = buf;
|
|
bufl = len;
|
|
}
|
|
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* Turn on read-write header bits if not already on */
|
|
if (!(cckd->cdevhdr[ cckd->sfn ].cdh_opts & CCKD_OPT_OPENED))
|
|
{
|
|
cckd->cdevhdr[ cckd->sfn ].cdh_opts |= (CCKD_OPT_OPENED | CCKD_OPT_OPENRW);
|
|
cckd64_write_chdr( dev );
|
|
}
|
|
|
|
/* Write the track image */
|
|
cckd64_write_trkimg( dev, bufp, bufl, trk, CCKD_SIZE_ANY );
|
|
cckd->needsdh = 1; /* We've updated the file. */
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cache_lock( CACHE_DEVBUF );
|
|
{
|
|
flag = cache_setflag( CACHE_DEVBUF, o, ~CCKD_CACHE_WRITING, 0 );
|
|
}
|
|
cache_unlock( CACHE_DEVBUF );
|
|
|
|
cckd->wrpending--;
|
|
|
|
if (1
|
|
&& cckd->cckdwaiters
|
|
&& (0
|
|
|| (flag & CCKD_CACHE_IOWAIT)
|
|
|| !cckd->wrpending
|
|
)
|
|
)
|
|
{
|
|
CCKD_TRACE( "writer[%d] cache[%2.2d] %d signalling write complete",
|
|
writer, o, trk );
|
|
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
}
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
CCKD_TRACE( "%d wrtrk[%2.2d] %d complete flags:%8.8x",
|
|
writer, o, trk, cache_getflag( CACHE_DEVBUF, o ));
|
|
|
|
/* Schedule the garbage collector, if it needs to be. */
|
|
if (cckdblk.gcint > 0)
|
|
cckd_gcstart( false );
|
|
|
|
/* Ensure the Dasd Hardener is running, if it's supposed to be. */
|
|
if (cckdblk.dhint > 0)
|
|
cckd_dhstart( false );
|
|
|
|
} /* end function cckd64_writer_write */
|
|
|
|
#if defined( DEBUG_FREESPACE )
|
|
/*-------------------------------------------------------------------*/
|
|
/* Debug routine for checking the free space array */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_chk_space(DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* Shadow file index */
|
|
int err = 0, n = 0, i, p;
|
|
U64 largest=0;
|
|
U64 total=0;
|
|
U64 fpos;
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_chk_space( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
p = -1;
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
n++; total += cckd->ifb[i].ifb_len;
|
|
if (n > cckd->free_count) break;
|
|
if (cckd->ifb[i].ifb_idxprv != p)
|
|
err = 1;
|
|
if (cckd->ifb[i].ifb_idxnxt >= 0)
|
|
{
|
|
if (fpos + cckd->ifb[i].ifb_len > cckd->ifb[i].ifb_offnxt)
|
|
err = 1;
|
|
}
|
|
else
|
|
{
|
|
if (fpos + cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].cdh_size)
|
|
err = 1;
|
|
}
|
|
if (cckd->ifb[i].ifb_pending == 0 && cckd->ifb[i].ifb_len > largest)
|
|
largest = cckd->ifb[i].ifb_len;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
p = i;
|
|
}
|
|
|
|
if (err
|
|
|| (cckd->cdevhdr[sfx].free_off != 0 && cckd->cdevhdr[sfx].free_num == 0)
|
|
|| (cckd->cdevhdr[sfx].free_off == 0 && cckd->cdevhdr[sfx].free_num != 0)
|
|
|| (n != cckd->cdevhdr[sfx].free_num)
|
|
|| (total != cckd->cdevhdr[sfx].free_total - cckd->cdevhdr[sfx].free_imbed)
|
|
|| (cckd->free_idxlast != p)
|
|
|| (largest != cckd->cdevhdr[sfx].free_largest)
|
|
)
|
|
{
|
|
CCKD_TRACE( "cdevhdr[%d] size %10"PRId64" used %10"PRId64" free 0x%16.16"PRIx64,
|
|
sfx, cckd->cdevhdr[sfx].cdh_size, cckd->cdevhdr[sfx].cdh_used,
|
|
cckd->cdevhdr[sfx].free_off);
|
|
CCKD_TRACE( " nbr %10"PRId64" total %10"PRId64" imbed %10"PRId64" largest %10"PRId64,
|
|
cckd->cdevhdr[sfx].free_num,
|
|
cckd->cdevhdr[sfx].free_total, cckd->cdevhdr[sfx].free_imbed,
|
|
cckd->cdevhdr[sfx].free_largest);
|
|
CCKD_TRACE( "free %p nbr %d 1st %d last %d avail %d",
|
|
cckd->ifb,cckd->free_count, cckd->free_idx1st,
|
|
cckd->free_idxlast, cckd->free_idxavail);
|
|
CCKD_TRACE( "found nbr %d total %"PRIu64" largest %"PRIu64,n,total,largest);
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
for (n = 0, i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (++n > cckd->free_count) break;
|
|
CCKD_TRACE( "%4d: [%4d] prev[%4d] next[%4d] pos %16.16"PRIx64" len %8"PRId64" %16.16"PRIx64" pend %d",
|
|
n, i, cckd->ifb[i].ifb_idxprv, cckd->ifb[i].ifb_idxnxt,
|
|
fpos, cckd->ifb[i].ifb_len,
|
|
fpos + cckd->ifb[i].ifb_len, cckd->ifb[i].ifb_pending);
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
cckd_print_itrace();
|
|
}
|
|
} /* end function cckd64_chk_space */
|
|
#endif // defined( DEBUG_FREESPACE )
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Get file space */
|
|
/*-------------------------------------------------------------------*/
|
|
S64 cckd64_get_space(DEVBLK *dev, int *size, int flags)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int i,p,n; /* Free space indexes */
|
|
S64 len2; /* Other lengths */
|
|
S64 fpos; /* Free space offset */
|
|
S64 flen; /* Free space size */
|
|
int sfx; /* Shadow file index */
|
|
int len; /* Requested length */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_get_space( dev, size, flags );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
len = *size;
|
|
|
|
if (flags & CCKD_L2SPACE)
|
|
{
|
|
flags |= CCKD_SIZE_EXACT;
|
|
len = *size = CCKD64_L2TAB_SIZE;
|
|
}
|
|
|
|
CCKD_TRACE( "get_space len %d largest %"PRId64" flags 0x%2.2x",
|
|
len, cckd->cdevhdr[sfx].free_largest, flags);
|
|
|
|
if (len <= CKD_NULLTRK_FMTMAX)
|
|
return 0;
|
|
|
|
if (!cckd->ifb)
|
|
cckd64_read_fsp (dev);
|
|
|
|
len2 = (U64) len + CCKD64_FREEBLK_SIZE;
|
|
|
|
/* Get space at the end if no space is large enough */
|
|
if ((U64)len2 > cckd->cdevhdr[sfx].free_largest
|
|
&& (U64)len != cckd->cdevhdr[sfx].free_largest)
|
|
{
|
|
|
|
cckd_get_space_atend:
|
|
|
|
fpos = cckd->cdevhdr[sfx].cdh_size;
|
|
if ((U64)fpos > (cckd->cckd_maxsize - len))
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: get space error, size exceeds %"PRId64"M"
|
|
WRMSG (HHC00304, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
|
|
(S64) (cckd->cckd_maxsize >> 20) + 1);
|
|
return -1;
|
|
}
|
|
cckd->cdevhdr[sfx].cdh_size += len;
|
|
cckd->cdevhdr[sfx].cdh_used += len;
|
|
|
|
CCKD_TRACE( "get_space atend 0x%16.16"PRIx64" len %d",fpos, len);
|
|
|
|
return fpos;
|
|
}
|
|
|
|
/* Scan free space chain */
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (cckd->ifb[i].ifb_pending == 0
|
|
&& ((U64)len2 <= cckd->ifb[i].ifb_len || (U64)len == cckd->ifb[i].ifb_len)
|
|
&& ((flags & CCKD_L2SPACE) || (U64)fpos >= cckd->L2_bounds))
|
|
break;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
|
|
/* This can happen if largest comes before L2_bounds */
|
|
if (i < 0) goto cckd_get_space_atend;
|
|
|
|
flen = cckd->ifb[i].ifb_len;
|
|
p = cckd->ifb[i].ifb_idxprv;
|
|
n = cckd->ifb[i].ifb_idxnxt;
|
|
|
|
/*
|
|
* If `CCKD_SIZE_ANY' bit is set and the left over space is small
|
|
* enough, then use the entire free space
|
|
*/
|
|
if ((flags & CCKD_SIZE_ANY) && flen <= cckd->free_minsize)
|
|
*size = (int)flen;
|
|
|
|
/* Remove the new space from free space */
|
|
if (*size < (int)flen)
|
|
{
|
|
cckd->ifb[i].ifb_len -= *size;
|
|
if (p >= 0)
|
|
cckd->ifb[p].ifb_offnxt += *size;
|
|
else
|
|
cckd->cdevhdr[sfx].free_off += *size;
|
|
}
|
|
else
|
|
{
|
|
cckd->cdevhdr[sfx].free_num--;
|
|
|
|
/* Remove the free space entry from the chain */
|
|
if (p >= 0)
|
|
{
|
|
cckd->ifb[p].ifb_offnxt = cckd->ifb[i].ifb_offnxt;
|
|
cckd->ifb[p].ifb_idxnxt = n;
|
|
}
|
|
else
|
|
{
|
|
cckd->cdevhdr[sfx].free_off = cckd->ifb[i].ifb_offnxt;
|
|
cckd->free_idx1st = n;
|
|
}
|
|
|
|
if (n >= 0)
|
|
cckd->ifb[n].ifb_idxprv = p;
|
|
else
|
|
cckd->free_idxlast = p;
|
|
|
|
/* Add entry to the available queue */
|
|
cckd->ifb[i].ifb_idxnxt = cckd->free_idxavail;
|
|
cckd->free_idxavail = i;
|
|
}
|
|
|
|
/* Find the largest free space if we got the largest */
|
|
if ((U64)flen >= cckd->cdevhdr[sfx].free_largest)
|
|
{
|
|
int i;
|
|
cckd->cdevhdr[sfx].free_largest = 0;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
if (cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].free_largest
|
|
&& cckd->ifb[i].ifb_pending == 0)
|
|
cckd->cdevhdr[sfx].free_largest = cckd->ifb[i].ifb_len;
|
|
}
|
|
|
|
/* Update free space stats */
|
|
cckd->cdevhdr[sfx].cdh_used += len;
|
|
cckd->cdevhdr[sfx].free_total -= len;
|
|
|
|
cckd->cdevhdr[sfx].free_imbed += *size - len;
|
|
|
|
CCKD_TRACE( "get_space found 0x%16.16"PRIx64" len %d size %d",
|
|
fpos, len, *size);
|
|
|
|
return fpos;
|
|
|
|
} /* end function cckd64_get_space */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Release file space */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_rel_space(DEVBLK *dev, U64 pos, int len, int size)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* Shadow file index */
|
|
U64 ppos, npos; /* Prev/next free offsets */
|
|
int i, p, n; /* Free space indexes */
|
|
int pending; /* Calculated pending value */
|
|
U64 fsize = size; /* Free space size */
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_rel_space( dev, pos, len, size );
|
|
return;
|
|
}
|
|
|
|
if (len <= CKD_NULLTRK_FMTMAX || pos == CCKD64_NOSIZE || pos == CCKD64_MAXSIZE)
|
|
return;
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "rel_space offset 0x%16.16"PRIx64" len %d size %d",
|
|
pos, len, size);
|
|
|
|
if (!cckd->ifb) cckd64_read_fsp (dev);
|
|
|
|
CCKD_CHK_SPACE(dev);
|
|
|
|
/* Scan free space chain */
|
|
ppos = -1;
|
|
npos = cckd->cdevhdr[sfx].free_off;
|
|
for (p = -1, n = cckd->free_idx1st; n >= 0; n = cckd->ifb[n].ifb_idxnxt)
|
|
{
|
|
if (pos < npos) break;
|
|
ppos = npos;
|
|
npos = cckd->ifb[n].ifb_offnxt;
|
|
p = n;
|
|
}
|
|
|
|
/* Calculate the `pending' value */
|
|
pending = cckdblk.freepend >= 0 ? cckdblk.freepend : 1 + (1 - cckdblk.fsync);
|
|
|
|
/* If possible use previous adjacent free space otherwise get an available one */
|
|
if (p >= 0 && ppos + cckd->ifb[p].ifb_len == pos && cckd->ifb[p].ifb_pending == pending)
|
|
{
|
|
cckd->ifb[p].ifb_len += size;
|
|
fsize = cckd->ifb[p].ifb_len;
|
|
}
|
|
else
|
|
{
|
|
/* Increase the size of the free space array if necessary */
|
|
if (cckd->free_idxavail < 0)
|
|
{
|
|
int new_free_count = cckd->free_count + CCKD_IFB_ENTS_INCR;
|
|
if (!(cckd->ifb = cckd_realloc( dev, "ifb", cckd->ifb, new_free_count * CCKD64_IFREEBLK_SIZE )))
|
|
return;
|
|
cckd->free_idxavail = cckd->free_count;
|
|
cckd->free_count = new_free_count;
|
|
for (i = cckd->free_idxavail; i < cckd->free_count; i++)
|
|
cckd->ifb[i].ifb_idxnxt = i + 1;
|
|
cckd->ifb[i-1].ifb_idxnxt = -1;
|
|
cckd->free_minsize = CCKD_MIN_FREESIZE( cckd->free_count );
|
|
}
|
|
|
|
/* Get an available free space entry */
|
|
i = cckd->free_idxavail;
|
|
cckd->free_idxavail = cckd->ifb[i].ifb_idxnxt;
|
|
cckd->cdevhdr[sfx].free_num++;
|
|
|
|
/* Update the new entry */
|
|
cckd->ifb[i].ifb_idxprv = p;
|
|
cckd->ifb[i].ifb_idxnxt = n;
|
|
cckd->ifb[i].ifb_len = size;
|
|
cckd->ifb[i].ifb_pending = pending;
|
|
|
|
/* Update the previous entry */
|
|
if (p >= 0)
|
|
{
|
|
cckd->ifb[i].ifb_offnxt = cckd->ifb[p].ifb_offnxt;
|
|
cckd->ifb[p].ifb_offnxt = pos;
|
|
cckd->ifb[p].ifb_idxnxt = i;
|
|
}
|
|
else
|
|
{
|
|
cckd->ifb[i].ifb_offnxt = cckd->cdevhdr[sfx].free_off;
|
|
cckd->cdevhdr[sfx].free_off = pos;
|
|
cckd->free_idx1st = i;
|
|
}
|
|
|
|
/* Update the next entry */
|
|
if (n >= 0)
|
|
cckd->ifb[n].ifb_idxprv = i;
|
|
else
|
|
cckd->free_idxlast = i;
|
|
}
|
|
|
|
/* Update the free space statistics */
|
|
cckd->cdevhdr[sfx].cdh_used -= len;
|
|
cckd->cdevhdr[sfx].free_total += len;
|
|
cckd->cdevhdr[sfx].free_imbed -= size - len;
|
|
if (!pending && fsize > cckd->cdevhdr[sfx].free_largest)
|
|
cckd->cdevhdr[sfx].free_largest = fsize;
|
|
|
|
CCKD_CHK_SPACE(dev);
|
|
|
|
} /* end function cckd64_rel_space */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Flush pending free space */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_flush_space(DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int p,i,n; /* Free free space indexes */
|
|
int sfx; /* Shadow file index */
|
|
U64 ppos, pos; /* Free space offsets */
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_flush_space( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "flush_space nbr %"PRId64,cckd->cdevhdr[sfx].free_num);
|
|
|
|
/* Make sure the free space chain is built */
|
|
if (!cckd->ifb) cckd64_read_fsp (dev);
|
|
|
|
CCKD_CHK_SPACE(dev);
|
|
|
|
if (cckd->cdevhdr[sfx].free_num == 0 || cckd->cdevhdr[sfx].free_off == 0)
|
|
{
|
|
cckd->cdevhdr[sfx].free_num = 0;
|
|
cckd->cdevhdr[sfx].free_off = 0;
|
|
cckd->free_idx1st = cckd->free_idxlast = cckd->free_idxavail = -1;
|
|
}
|
|
|
|
pos = cckd->cdevhdr[sfx].free_off;
|
|
ppos = p = -1;
|
|
cckd->cdevhdr[sfx].free_num = 0;
|
|
cckd->cdevhdr[sfx].free_largest = 0;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
/* Decrement the pending count */
|
|
if (cckd->ifb[i].ifb_pending)
|
|
--cckd->ifb[i].ifb_pending;
|
|
|
|
/* Combine adjacent free spaces */
|
|
while (pos + cckd->ifb[i].ifb_len == cckd->ifb[i].ifb_offnxt)
|
|
{
|
|
n = cckd->ifb[i].ifb_idxnxt;
|
|
if (cckd->ifb[n].ifb_pending > cckd->ifb[i].ifb_pending + 1
|
|
|| cckd->ifb[n].ifb_pending < cckd->ifb[i].ifb_pending)
|
|
break;
|
|
cckd->ifb[i].ifb_offnxt = cckd->ifb[n].ifb_offnxt;
|
|
cckd->ifb[i].ifb_len += cckd->ifb[n].ifb_len;
|
|
cckd->ifb[i].ifb_idxnxt = cckd->ifb[n].ifb_idxnxt;
|
|
cckd->ifb[n].ifb_idxnxt = cckd->free_idxavail;
|
|
cckd->free_idxavail = n;
|
|
n = cckd->ifb[i].ifb_idxnxt;
|
|
if (n >= 0)
|
|
cckd->ifb[n].ifb_idxprv = i;
|
|
|
|
}
|
|
ppos = pos;
|
|
pos = cckd->ifb[i].ifb_offnxt;
|
|
cckd->cdevhdr[sfx].free_num++;
|
|
if (cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].free_largest
|
|
&& !cckd->ifb[i].ifb_pending)
|
|
cckd->cdevhdr[sfx].free_largest = cckd->ifb[i].ifb_len;
|
|
p = i;
|
|
}
|
|
cckd->free_idxlast = p;
|
|
|
|
CCKD_TRACE( "rel_flush_space nbr %"PRId64" (after merge)",
|
|
cckd->cdevhdr[sfx].free_num);
|
|
|
|
/* If the last free space is at the end of the file then release it */
|
|
if (p >= 0 && ppos + cckd->ifb[p].ifb_len == cckd->cdevhdr[sfx].cdh_size
|
|
&& !cckd->ifb[p].ifb_pending)
|
|
{
|
|
i = p;
|
|
p = cckd->ifb[i].ifb_idxprv;
|
|
|
|
CCKD_TRACE( "file[%d] rel_flush_space atend 0x%16.16"PRIx64" len %"PRId64,
|
|
sfx, ppos, cckd->ifb[i].ifb_len);
|
|
|
|
/* Remove the entry from the chain */
|
|
if (p >= 0)
|
|
{
|
|
cckd->ifb[p].ifb_offnxt = 0;
|
|
cckd->ifb[p].ifb_idxnxt = -1;
|
|
}
|
|
else
|
|
{
|
|
cckd->cdevhdr[sfx].free_off = 0;
|
|
cckd->free_idx1st = -1;
|
|
}
|
|
cckd->free_idxlast = p;
|
|
|
|
/* Add the entry to the available chain */
|
|
cckd->ifb[i].ifb_idxnxt = cckd->free_idxavail;
|
|
cckd->free_idxavail = i;
|
|
|
|
/* Update the device header */
|
|
cckd->cdevhdr[sfx].cdh_size -= cckd->ifb[i].ifb_len;
|
|
cckd->cdevhdr[sfx].free_total -= cckd->ifb[i].ifb_len;
|
|
cckd->cdevhdr[sfx].free_num--;
|
|
if (cckd->ifb[i].ifb_len >= cckd->cdevhdr[sfx].free_largest)
|
|
{
|
|
cckd->cdevhdr[sfx].free_largest = 0;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
if (cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].free_largest
|
|
&& cckd->ifb[i].ifb_pending == 0)
|
|
cckd->cdevhdr[sfx].free_largest = cckd->ifb[i].ifb_len;
|
|
}
|
|
|
|
/* Truncate the file */
|
|
cckd64_ftruncate (dev, sfx, cckd->cdevhdr[sfx].cdh_size);
|
|
|
|
} /* Release space at end of the file */
|
|
|
|
CCKD_CHK_SPACE(dev);
|
|
|
|
} /* end function cckd64_flush_space */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read compressed dasd header */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read_chdr (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_read_chdr( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] read_chdr", sfx);
|
|
|
|
memset(&cckd->cdevhdr[sfx], 0, CCKD64_DEVHDR_SIZE);
|
|
|
|
/* Read the device header */
|
|
if (cckd64_read (dev, sfx, CKD_DEVHDR_SIZE, &cckd->cdevhdr[sfx], CCKD64_DEVHDR_SIZE) < 0)
|
|
return -1;
|
|
|
|
/* Check endian format */
|
|
cckd->swapend[sfx] = 0;
|
|
if ((cckd->cdevhdr[sfx].cdh_opts & CCKD_OPT_BIGEND) != cckd_def_opt_bigend())
|
|
{
|
|
if (cckd->open[sfx] == CCKD_OPEN_RW)
|
|
{
|
|
if (cckd64_swapend (dev) < 0)
|
|
return -1;
|
|
cckd64_swapend_chdr (&cckd->cdevhdr[sfx]);
|
|
}
|
|
else
|
|
{
|
|
cckd->swapend[sfx] = 1;
|
|
cckd64_swapend_chdr (&cckd->cdevhdr[sfx]);
|
|
}
|
|
}
|
|
|
|
/* Set default null format */
|
|
if (cckd->cdevhdr[sfx].cdh_nullfmt > CKD_NULLTRK_FMTMAX)
|
|
cckd->cdevhdr[sfx].cdh_nullfmt = 0;
|
|
|
|
if (cckd->cdevhdr[sfx].cdh_nullfmt == 0 && dev->oslinux && dev->devtype == 0x3390)
|
|
cckd->cdevhdr[sfx].cdh_nullfmt = CKD_NULLTRK_FMT2;
|
|
|
|
if (cckd->cdevhdr[sfx].cdh_nullfmt == CKD_NULLTRK_FMT2)
|
|
dev->oslinux = 1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_read_chdr */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write compressed dasd header */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_write_chdr (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_write_chdr( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] write_chdr", sfx);
|
|
|
|
/* Set version.release.modlvl */
|
|
cckd->cdevhdr[sfx].cdh_vrm[0] = CCKD_VERSION;
|
|
cckd->cdevhdr[sfx].cdh_vrm[1] = CCKD_RELEASE;
|
|
cckd->cdevhdr[sfx].cdh_vrm[2] = CCKD_MODLVL;
|
|
|
|
if (cckd64_write (dev, sfx, CCKD64_DEVHDR_POS, &cckd->cdevhdr[sfx], CCKD64_DEVHDR_SIZE) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_write_chdr */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read the level 1 table */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read_l1 (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
int len; /* Length of level 1 table */
|
|
int i; /* Work integer */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_read_l1( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] read_l1 offset 0x%"PRIx64,
|
|
sfx, (U64)CCKD64_L1TAB_POS);
|
|
|
|
/* Free the old level 1 table if it exists */
|
|
cckd->L1tab[sfx] = cckd_free (dev, "l1", cckd->L1tab[sfx]);
|
|
|
|
/* Allocate the level 1 table */
|
|
len = cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE;
|
|
if ((cckd->L1tab[sfx] = cckd_malloc (dev, "l1", len)) == NULL)
|
|
return -1;
|
|
if ( sfx )
|
|
memset(cckd->L1tab[sfx], 0xFF, len);
|
|
else
|
|
memset(cckd->L1tab[sfx], 0, len);
|
|
|
|
/* Read the level 1 table */
|
|
if (cckd64_read (dev, sfx, CCKD64_L1TAB_POS, cckd->L1tab[sfx], len) < 0)
|
|
return -1;
|
|
|
|
/* Fix endianness */
|
|
if (cckd->swapend[sfx])
|
|
cckd64_swapend_l1 (cckd->L1tab[sfx], cckd->cdevhdr[sfx].num_L1tab);
|
|
|
|
/* Determine bounds */
|
|
cckd->L2_bounds = CCKD64_L1TAB_POS + len;
|
|
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
if (cckd->L1tab[sfx][i] != CCKD64_NOSIZE && cckd->L1tab[sfx][i] != CCKD64_MAXSIZE)
|
|
cckd->L2_bounds += CCKD64_L2TAB_SIZE;
|
|
|
|
/* Check if all l2 tables are within bounds */
|
|
cckd->L2ok = 1;
|
|
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab && cckd->L2ok; i++)
|
|
if (cckd->L1tab[sfx][i] != CCKD64_NOSIZE && cckd->L1tab[sfx][i] != CCKD64_MAXSIZE)
|
|
if (cckd->L1tab[sfx][i] > cckd->L2_bounds - CCKD64_L2TAB_SIZE)
|
|
cckd->L2ok = 0;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_read_l1 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write the level 1 table */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_write_l1 (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
int len; /* Length of level 1 table */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_write_l1( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
len = cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE;
|
|
|
|
CCKD_TRACE( "file[%d] write_l1 0x%"PRIx64" len %d",
|
|
sfx, (U64)CCKD64_L1TAB_POS, len);
|
|
|
|
if (cckd64_write (dev, sfx, CCKD64_L1TAB_POS, cckd->L1tab[sfx], len) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_write_l1 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Update a level 1 table entry */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_write_l1ent (DEVBLK *dev, int L1idx)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
U64 off; /* Offset to l1 entry */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_write_l1ent( dev, L1idx );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
off = (CCKD64_L1TAB_POS + L1idx * CCKD64_L1ENT_SIZE);
|
|
|
|
CCKD_TRACE( "file[%d] write_l1ent[%d] , 0x%16.16"PRIx64,
|
|
sfx, L1idx, off);
|
|
|
|
if (cckd64_write (dev, sfx, off, &cckd->L1tab[sfx][L1idx], CCKD64_L1ENT_SIZE) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_write_l1ent */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initial read */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read_init (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT* cckd; /* -> cckd extension */
|
|
int sfx; /* File index */
|
|
CKD_DEVHDR devhdr; /* Device header */
|
|
U32 imgtyp; /* Dasd device image type */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_read_init( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] read_init", sfx );
|
|
|
|
/* Read the device header */
|
|
if (cckd64_read( dev, sfx, 0, &devhdr, CKD_DEVHDR_SIZE ) < 0)
|
|
return -1;
|
|
|
|
/* Check the device hdr */
|
|
imgtyp = dh_devid_typ( devhdr.dh_devid );
|
|
|
|
if (!sfx && (imgtyp & CKD_C064_TYP)) cckd->ckddasd = 1;
|
|
else if (!sfx && (imgtyp & FBA_C064_TYP)) cckd->fbadasd = 1;
|
|
else if (1
|
|
&& !(sfx && (imgtyp & CKD64_SF_TYP) && cckd->ckddasd)
|
|
&& !(sfx && (imgtyp & FBA64_SF_TYP) && cckd->fbadasd)
|
|
)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: device header id error"
|
|
WRMSG( HHC00305, "E", LCSS_DEVNUM,
|
|
sfx, cckd_sf_name( dev, sfx ));
|
|
return -1;
|
|
}
|
|
|
|
/* Read the compressed header */
|
|
if (cckd64_read_chdr( dev ) < 0)
|
|
return -1;
|
|
|
|
/* Read the level 1 table */
|
|
if (cckd64_read_l1( dev ) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
} /* end function cckd64_read_init */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read free space */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read_fsp( DEVBLK* dev )
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
U64 fpos; /* Free space offset */
|
|
int sfx; /* File index */
|
|
int i; /* Index */
|
|
CCKD64_FREEBLK freeblk; /* First freeblk read */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_read_fsp( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
CCKD_TRACE( "file[%d] read_fsp number %"PRId64,
|
|
sfx, cckd->cdevhdr[sfx].free_num );
|
|
|
|
cckd->ifb = cckd_free( dev, "ifb", cckd->ifb );
|
|
|
|
cckd->free_count = 0;
|
|
cckd->free_idx1st = -1;
|
|
cckd->free_idxlast = -1;
|
|
cckd->free_idxavail = -1;
|
|
|
|
/* Get storage for the internal free space chain
|
|
* in a multiple of 1024 entries
|
|
*/
|
|
i = (int) ROUND_UP( cckd->cdevhdr[sfx].free_num, CCKD_IFB_ENTS_INCR );
|
|
if (!(cckd->ifb = cckd_calloc( dev, "ifb", i, CCKD64_IFREEBLK_SIZE )))
|
|
return -1;
|
|
|
|
cckd->free_count = i;
|
|
|
|
/* Build the doubly linked internal free space chain */
|
|
if (cckd->cdevhdr[sfx].free_num)
|
|
{
|
|
cckd->free_idx1st = 0;
|
|
|
|
/* Read the first freeblk to determine old/new format */
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
if (cckd64_read (dev, sfx, fpos, &freeblk, CCKD64_FREEBLK_SIZE) < 0)
|
|
return -1;
|
|
|
|
if (memcmp(&freeblk, "FREE_BLK", 8) == 0)
|
|
{
|
|
/* new format free space */
|
|
CCKD64_FREEBLK *fsp;
|
|
U64 ofree = cckd->cdevhdr[sfx].free_off;
|
|
S64 n = cckd->cdevhdr[sfx].free_num * CCKD64_FREEBLK_SIZE;
|
|
if (!(fsp = cckd_malloc( dev, "fsp", (size_t) n )))
|
|
return -1;
|
|
fpos += CCKD64_FREEBLK_SIZE;
|
|
if (cckd64_read (dev, sfx, fpos, fsp, (unsigned int) n) < 0)
|
|
return -1;
|
|
for (i = 0; i < cckd->cdevhdr[sfx].free_num; i++)
|
|
{
|
|
if (i == 0)
|
|
cckd->cdevhdr[sfx].free_off = fsp[i].fb_offnxt;
|
|
else
|
|
cckd->ifb[i-1].ifb_offnxt = fsp[i].fb_offnxt;
|
|
cckd->ifb[i].ifb_offnxt = 0;
|
|
cckd->ifb[i].ifb_len = fsp[i].fb_len;
|
|
cckd->ifb[i].ifb_idxprv = i - 1;
|
|
cckd->ifb[i].ifb_idxnxt = i + 1;
|
|
}
|
|
cckd->ifb[i-1].ifb_idxnxt = -1;
|
|
cckd->free_idxlast = i-1;
|
|
fsp = cckd_free (dev, "fsp", fsp);
|
|
|
|
/* truncate if new format free space was at the end */
|
|
if (ofree == cckd->cdevhdr[sfx].cdh_size)
|
|
{
|
|
fpos = cckd->cdevhdr[sfx].cdh_size;
|
|
cckd64_ftruncate(dev, sfx, fpos);
|
|
}
|
|
} /* new format free space */
|
|
else
|
|
{
|
|
/* old format free space */
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
for (i = 0; i < cckd->cdevhdr[sfx].free_num; i++)
|
|
{
|
|
if (cckd64_read (dev, sfx, fpos, &cckd->ifb[i], CCKD64_FREEBLK_SIZE) < 0)
|
|
return -1;
|
|
cckd->ifb[i].ifb_idxprv = i - 1;
|
|
cckd->ifb[i].ifb_idxnxt = i + 1;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
cckd->ifb[i-1].ifb_idxnxt = -1;
|
|
cckd->free_idxlast = i-1;
|
|
} /* old format free space */
|
|
} /* if (cckd->cdevhdr[sfx].free_num) */
|
|
|
|
/* Build singly linked chain of available free space entries */
|
|
if (cckd->cdevhdr[sfx].free_num < cckd->free_count)
|
|
{
|
|
cckd->free_idxavail = (int) cckd->cdevhdr[sfx].free_num;
|
|
for (i = cckd->free_idxavail; i < cckd->free_count; i++)
|
|
cckd->ifb[i].ifb_idxnxt = i + 1;
|
|
cckd->ifb[i-1].ifb_idxnxt = -1;
|
|
}
|
|
|
|
/* Set minimum free space size */
|
|
cckd->free_minsize = CCKD_MIN_FREESIZE( cckd->free_count );
|
|
return 0;
|
|
|
|
} /* end function cckd64_read_fsp */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write the free space */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_write_fsp (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
U64 fpos; /* Free space offset */
|
|
U64 ppos; /* Previous free space offset*/
|
|
int sfx; /* File index */
|
|
S64 i, j, n; /* Work variables */
|
|
int rc; /* Return code */
|
|
CCKD64_FREEBLK *fsp = NULL; /* -> new format free space */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_write_fsp( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
|
|
if (!cckd->ifb)
|
|
return 0;
|
|
|
|
CCKD_TRACE( "file[%d] write_fsp number %"PRId64,
|
|
sfx, cckd->cdevhdr[sfx].free_num);
|
|
|
|
/* get rid of pending free space */
|
|
for (i = 0; i < CCKD_MAX_FREEPEND; i++)
|
|
cckd64_flush_space(dev);
|
|
|
|
/* sanity checks */
|
|
if (cckd->cdevhdr[sfx].free_num == 0 || cckd->cdevhdr[sfx].free_off == 0)
|
|
{
|
|
cckd->cdevhdr[sfx].free_num = cckd->cdevhdr[sfx].free_off = 0;
|
|
cckd->free_idx1st = cckd->free_idxlast = cckd->free_idxavail = -1;
|
|
}
|
|
|
|
/* Write any free spaces */
|
|
if (cckd->cdevhdr[sfx].free_off)
|
|
{
|
|
/* size needed for new format free space */
|
|
n = (cckd->cdevhdr[sfx].free_num+1) * CCKD64_FREEBLK_SIZE;
|
|
|
|
/* look for existing free space to fit new format free space */
|
|
fpos = 0;
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
if ((U64)n <= cckd->ifb[i].ifb_len)
|
|
break;
|
|
if (i >= 0)
|
|
fpos = cckd->ifb[i].ifb_idxprv < 0
|
|
? cckd->cdevhdr[sfx].free_off
|
|
: cckd->ifb[cckd->ifb[i].ifb_idxprv].ifb_offnxt;
|
|
|
|
/* if no applicable space see if we can append to the file */
|
|
if (fpos == 0 && cckd->cckd_maxsize - cckd->cdevhdr[sfx].cdh_size >= (U64)n)
|
|
fpos = cckd->cdevhdr[sfx].cdh_size;
|
|
|
|
if (fpos && !(fsp = cckd_malloc( dev, "fsp", (size_t) n )))
|
|
fpos = 0;
|
|
|
|
if (fpos)
|
|
{
|
|
/* Zero entire block to avoid writing garbage to underlying file */
|
|
memset (&fsp[0], 0, CCKD64_FREEBLK_SIZE);
|
|
|
|
/* New format free space */
|
|
memcpy (&fsp[0], "FREE_BLK", 8);
|
|
ppos = cckd->cdevhdr[sfx].free_off;
|
|
for (i = cckd->free_idx1st, j = 1; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
fsp[j].fb_offnxt = ppos;
|
|
fsp[j++].fb_len = cckd->ifb[i].ifb_len;
|
|
ppos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
rc = cckd64_write (dev, sfx, fpos, fsp, (unsigned int)n);
|
|
fsp = cckd_free (dev, "fsp", fsp);
|
|
if (rc < 0)
|
|
return -1;
|
|
cckd->cdevhdr[sfx].free_off = fpos;
|
|
} /* new format free space */
|
|
else
|
|
{
|
|
/* Old format free space */
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (cckd64_write (dev, sfx, fpos, &cckd->ifb[i], CCKD64_FREEBLK_SIZE) < 0)
|
|
return -1;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
} /* old format free space */
|
|
} /* if (cckd->cdevhdr[sfx].free_off) */
|
|
|
|
/* Free the free space array */
|
|
cckd->ifb = cckd_free (dev, "ifb", cckd->ifb);
|
|
cckd->free_count = 0;
|
|
cckd->free_idx1st = cckd->free_idxlast = cckd->free_idxavail = -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_write_fsp */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read a new level 2 table */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read_l2 (DEVBLK *dev, int sfx, int L1idx)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
U64 off; /* L2 file offset */
|
|
int fnd; /* Found cache */
|
|
int lru; /* Oldest available cache */
|
|
CCKD64_L2ENT *buf; /* -> Cache buffer */
|
|
int i; /* Loop index */
|
|
BYTE nullfmt; /* Null track format */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_read_l2( dev, sfx, L1idx );
|
|
|
|
cckd = dev->cckd_ext;
|
|
nullfmt = cckd->cdevhdr[cckd->sfn].cdh_nullfmt;
|
|
|
|
CCKD_TRACE( "file[%d] read_l2 %d active %d %d %d",
|
|
sfx, L1idx, cckd->sfx, cckd->L1idx, cckd->L2_active);
|
|
|
|
/* Return if table is already active */
|
|
if (sfx == cckd->sfx && L1idx == cckd->L1idx) return 0;
|
|
|
|
cache_lock(CACHE_L2);
|
|
|
|
/* Inactivate the previous entry */
|
|
if (cckd->L2_active >= 0)
|
|
cache_setflag(CACHE_L2, cckd->L2_active, ~L2_CACHE_ACTIVE, 0);
|
|
cckd->L2tab = NULL;
|
|
cckd->L2_active = cckd->sfx = cckd->L1idx = -1;
|
|
|
|
/* scan the cache array for the l2tab */
|
|
fnd = cache_lookup (CACHE_L2, L2_CACHE_SETKEY(sfx, dev->devnum, L1idx), &lru);
|
|
|
|
/* check for level 2 cache hit */
|
|
if (fnd >= 0)
|
|
{
|
|
CCKD_TRACE( "l2[%d,%d] cache[%d] hit", sfx, L1idx, fnd);
|
|
cache_setflag (CACHE_L2, fnd, 0, L2_CACHE_ACTIVE);
|
|
cache_setage (CACHE_L2, fnd);
|
|
cckdblk.stats_l2cachehits++;
|
|
cache_unlock (CACHE_L2);
|
|
cckd->sfx = sfx;
|
|
cckd->L1idx = L1idx;
|
|
cckd->L2tab = cache_getbuf(CACHE_L2, fnd, 0);
|
|
cckd->L2_active = fnd;
|
|
return 1;
|
|
}
|
|
|
|
CCKD_TRACE( "l2[%d,%d] cache[%d] miss", sfx, L1idx, lru);
|
|
|
|
/* Steal an entry if all are busy */
|
|
if (lru < 0) lru = cckd64_steal_l2();
|
|
|
|
/* Make the entry active */
|
|
cache_setkey (CACHE_L2, lru, L2_CACHE_SETKEY(sfx, dev->devnum, L1idx));
|
|
cache_setflag (CACHE_L2, lru, 0, L2_CACHE_ACTIVE);
|
|
cache_setage (CACHE_L2, lru);
|
|
buf = cache_getbuf(CACHE_L2, lru, CCKD64_L2TAB_SIZE);
|
|
cckdblk.stats_l2cachemisses++;
|
|
cache_unlock (CACHE_L2);
|
|
if (buf == NULL) return -1;
|
|
|
|
/* Check for null table */
|
|
if (cckd->L1tab[sfx][L1idx] == 0)
|
|
{
|
|
memset(buf, 0, CCKD64_L2TAB_SIZE);
|
|
if (nullfmt)
|
|
for (i = 0; i < 256; i++)
|
|
buf[i].L2_len = buf[i].L2_size = nullfmt;
|
|
CCKD_TRACE( "l2[%d,%d] cache[%d] null fmt[%d]", sfx, L1idx, lru, nullfmt);
|
|
}
|
|
else if (cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE)
|
|
{
|
|
memset(buf, 0xff, CCKD64_L2TAB_SIZE);
|
|
CCKD_TRACE( "l2[%d,%d] cache[%d] null 0xff", sfx, L1idx, lru);
|
|
}
|
|
/* Read the new level 2 table */
|
|
else
|
|
{
|
|
off = cckd->L1tab[sfx][L1idx];
|
|
if (cckd64_read (dev, sfx, off, buf, CCKD64_L2TAB_SIZE) < 0)
|
|
{
|
|
cache_lock(CACHE_L2);
|
|
cache_setflag(CACHE_L2, lru, 0, 0);
|
|
cache_unlock(CACHE_L2);
|
|
return -1;
|
|
}
|
|
|
|
if (cckd->swapend[sfx])
|
|
cckd64_swapend_l2 (buf);
|
|
|
|
CCKD_TRACE( "file[%d] cache[%d] l2[%d] read offset 0x%16.16"PRIx64,
|
|
sfx, lru, L1idx, cckd->L1tab[sfx][L1idx]);
|
|
|
|
cckd->L2_reads[sfx]++;
|
|
cckd->totl2reads++;
|
|
cckdblk.stats_l2reads++;
|
|
}
|
|
|
|
cckd->sfx = sfx;
|
|
cckd->L1idx = L1idx;
|
|
cckd->L2tab = buf;
|
|
cckd->L2_active = lru;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_read_l2 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Purge all l2tab cache entries for a given device */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_purge_l2 (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_purge_l2( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "purge_l2%s", "");
|
|
|
|
cache_lock (CACHE_L2);
|
|
cckd->L2_active = cckd->sfx = cckd->L1idx = -1;
|
|
cckd->L2tab = NULL;
|
|
cache_scan (CACHE_L2, cckd64_purge_l2_scan, dev);
|
|
cache_unlock (CACHE_L2);
|
|
}
|
|
int cckd64_purge_l2_scan (int *answer, int ix, int i, void *data)
|
|
{
|
|
U16 sfx; /* Cached suffix */
|
|
U16 devnum; /* Cached device number */
|
|
U32 L1idx; /* Cached level 1 index */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
|
|
UNREFERENCED(answer);
|
|
L2_CACHE_GETKEY(i, sfx, devnum, L1idx);
|
|
if (dev == NULL || devnum == dev->devnum)
|
|
{
|
|
CCKD_TRACE( "purge l2cache[%d] %4.4X sfx %hd ix %d purged",
|
|
i, devnum, sfx, L1idx);
|
|
cache_release(ix, i, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Steal an l2tab cache entry */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_steal_l2 ()
|
|
{
|
|
DEVBLK *dev; /* -> device block */
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int i; /* Stolen cache index */
|
|
U16 sfx; /* Cached suffix */
|
|
U16 devnum; /* Cached device number */
|
|
U32 L1idx; /* Cached level 1 index */
|
|
|
|
i = cache_scan (CACHE_L2, cckd_steal_l2_scan, NULL);
|
|
L2_CACHE_GETKEY(i, sfx, devnum, L1idx);
|
|
dev = cckd_find_device_by_devnum(devnum);
|
|
if (!dev->cckd64)
|
|
return cckd_steal_l2();
|
|
cckd = dev->cckd_ext;
|
|
cckd->L2_active = cckd->sfx = cckd->L1idx = -1;
|
|
cckd->L2tab = NULL;
|
|
cache_release(CACHE_L2, i, 0);
|
|
return i;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write the current level 2 table */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_write_l2 (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx,L1idx; /* Lookup table indices */
|
|
U64 off, old_off; /* New/old L2 file offsets */
|
|
int size = CCKD64_L2TAB_SIZE; /* L2 table size */
|
|
int fix; /* Null format type */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_write_l2( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
sfx = cckd->sfn;
|
|
L1idx = cckd->L1idx;
|
|
fix = cckd->cdevhdr[sfx].cdh_nullfmt;
|
|
cckd->L2ok = 0;
|
|
|
|
CCKD_TRACE( "file[%d] write_l2 %d", sfx, L1idx);
|
|
|
|
if (sfx < 0 || L1idx < 0) return -1;
|
|
|
|
old_off = cckd->L1tab[sfx][L1idx];
|
|
|
|
if (cckd->L1tab[sfx][L1idx] == CCKD64_NOSIZE || cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE)
|
|
cckd->L2_bounds += CCKD64_L2TAB_SIZE;
|
|
|
|
/* Write the L2 table if it's not empty */
|
|
if (memcmp( cckd->L2tab, &empty64_l2[fix], CCKD64_L2TAB_SIZE ))
|
|
{
|
|
if ((S64)(off = cckd64_get_space( dev, &size, CCKD_L2SPACE )) < 0)
|
|
return -1;
|
|
if (cckd64_write( dev, sfx, off, cckd->L2tab, CCKD64_L2TAB_SIZE ) < 0)
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
off = 0;
|
|
cckd->L2_bounds -= CCKD64_L2TAB_SIZE;
|
|
}
|
|
|
|
/* Free the old L2 space */
|
|
cckd64_rel_space( dev, old_off, CCKD64_L2TAB_SIZE, CCKD64_L2TAB_SIZE );
|
|
|
|
/* Update level 1 table */
|
|
cckd->L1tab[sfx][L1idx] = off;
|
|
|
|
if (cckd64_write_l1ent( dev, L1idx ) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_write_l2 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return a level 2 entry */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read_l2ent (DEVBLK *dev, CCKD64_L2ENT *l2, int trk)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx,L1idx,l2x; /* Lookup table indices */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_read_l2ent( dev, (CCKD_L2ENT*) l2, trk );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
L1idx = trk >> 8;
|
|
l2x = trk & 0xff;
|
|
|
|
if (l2 != NULL) l2->L2_trkoff = l2->L2_len = l2->L2_size = l2->L2_pad = 0;
|
|
|
|
for (sfx = cckd->sfn; sfx >= 0; sfx--)
|
|
{
|
|
CCKD_TRACE( "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%"PRIx64,
|
|
sfx, L1idx, l2x, trk, cckd->L1tab[sfx][L1idx]);
|
|
|
|
/* Continue if l2 table not in this file */
|
|
if (cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE)
|
|
continue;
|
|
|
|
/* Read l2 table from this file */
|
|
if (cckd64_read_l2 (dev, sfx, L1idx) < 0)
|
|
return -1;
|
|
|
|
/* Exit loop if track is in this file */
|
|
if (cckd->L2tab[l2x].L2_trkoff != CCKD64_MAXSIZE)
|
|
break;
|
|
}
|
|
|
|
CCKD_TRACE( "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%"PRIx64" %hd %hd",
|
|
sfx, L1idx, l2x, trk, (U64)(sfx >= 0 ? cckd->L2tab[l2x].L2_trkoff : 0),
|
|
(U16)(sfx >= 0 ? cckd->L2tab[l2x].L2_len : 0),
|
|
(U16)(sfx >= 0 ? cckd->L2tab[l2x].L2_size : 0));
|
|
|
|
if (l2 != NULL && sfx >= 0)
|
|
{
|
|
l2->L2_trkoff = cckd->L2tab[l2x].L2_trkoff;
|
|
l2->L2_len = cckd->L2tab[l2x].L2_len;
|
|
l2->L2_size = cckd->L2tab[l2x].L2_size;
|
|
}
|
|
|
|
return sfx;
|
|
|
|
} /* end function cckd64_read_l2ent */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Update a level 2 entry */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_write_l2ent (DEVBLK *dev, CCKD64_L2ENT *l2, int trk)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx,L1idx,l2x; /* Lookup table indices */
|
|
U64 off; /* L2 entry offset */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_write_l2ent( dev, (CCKD_L2ENT*) l2, trk );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Error return if no available level 2 table */
|
|
if (!cckd->L2tab) return -1;
|
|
|
|
sfx = cckd->sfn;
|
|
L1idx = trk >> 8;
|
|
l2x = trk & 0xff;
|
|
|
|
/* Copy the new entry if passed */
|
|
if (l2) memcpy (&cckd->L2tab[l2x], l2, CCKD64_L2ENT_SIZE);
|
|
|
|
CCKD_TRACE( "file[%d] l2[%d,%d] trk[%d] write_l2ent 0x%"PRIx64" %hd %hd",
|
|
sfx, L1idx, l2x, trk,
|
|
cckd->L2tab[l2x].L2_trkoff, cckd->L2tab[l2x].L2_len, cckd->L2tab[l2x].L2_size);
|
|
|
|
/* If no level 2 table for this file, then write a new one */
|
|
if (cckd->L1tab[sfx][L1idx] == CCKD64_NOSIZE || cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE)
|
|
return cckd64_write_l2 (dev);
|
|
|
|
/* Write the level 2 table entry */
|
|
off = (cckd->L1tab[sfx][L1idx] + l2x * CCKD64_L2ENT_SIZE);
|
|
if (cckd64_write (dev, sfx, off, &cckd->L2tab[l2x], CCKD64_L2ENT_SIZE) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
} /* end function cckd64_write_l2ent */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read a track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_read_trkimg (DEVBLK *dev, BYTE *buf, int trk, BYTE *unitstat)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int sfx; /* File index */
|
|
CCKD64_L2ENT l2; /* Level 2 entry */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "trk[%d] read_trkimg", trk);
|
|
|
|
/* Read level 2 entry for the track */
|
|
if ((sfx = cckd64_read_l2ent (dev, &l2, trk)) < 0)
|
|
goto cckd_read_trkimg_error;
|
|
|
|
/* Read the track image or build a null track image */
|
|
if (l2.L2_trkoff != 0)
|
|
{
|
|
rc = cckd64_read (dev, sfx, l2.L2_trkoff, buf, l2.L2_len);
|
|
if (rc < 0)
|
|
goto cckd_read_trkimg_error;
|
|
|
|
cckd->reads[sfx]++;
|
|
cckd->totreads++;
|
|
cckdblk.stats_reads++;
|
|
cckdblk.stats_readbytes += rc;
|
|
if (cckd->notnull == 0 && trk > 1) cckd->notnull = 1;
|
|
}
|
|
else
|
|
rc = cckd64_null_trk (dev, buf, trk, l2.L2_len);
|
|
|
|
/* Validate the track image */
|
|
if (cckd64_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 cckd64_null_trk (dev, buf, trk, 0);
|
|
|
|
} /* end function cckd64_read_trkimg */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write a track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_write_trkimg (DEVBLK *dev, BYTE *buf, int len, int trk, int flags)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
U64 off; /* File offset */
|
|
CCKD64_L2ENT l2, oldl2; /* Level 2 entries */
|
|
int sfx,L1idx,l2x; /* Lookup table indices */
|
|
int after = 0; /* 1=New track after old */
|
|
int size; /* Size of new track */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_write_trkimg( dev, buf, len, trk, flags );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
sfx = cckd->sfn;
|
|
L1idx = trk >> 8;
|
|
l2x = trk & 0xff;
|
|
|
|
CCKD_TRACE( "file[%d] trk[%d] write_trkimg len %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
sfx, trk, len, buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
|
|
|
|
/* Validate the new track image */
|
|
if (cckd64_cchh (dev, buf, trk) < 0)
|
|
return -1;
|
|
|
|
/* Get the level 2 table for the track in the active file */
|
|
if (cckd64_read_l2 (dev, sfx, L1idx) < 0)
|
|
return -1;
|
|
|
|
/* Save the level 2 entry for the track */
|
|
oldl2.L2_trkoff = cckd->L2tab[l2x].L2_trkoff;
|
|
oldl2.L2_len = cckd->L2tab[l2x].L2_len;
|
|
oldl2.L2_size = cckd->L2tab[l2x].L2_size;
|
|
CCKD_TRACE( "file[%d] trk[%d] write_trkimg oldl2 0x%"PRIx64" %hd %hd",
|
|
sfx, trk, oldl2.L2_trkoff, oldl2.L2_len, oldl2.L2_size);
|
|
|
|
/* Check if writing a null track */
|
|
len = cckd64_check_null_trk(dev, buf, trk, len);
|
|
|
|
if (len > CKD_NULLTRK_FMTMAX)
|
|
{
|
|
/* Get space for the track image */
|
|
size = len;
|
|
if ((S64)(off = cckd64_get_space (dev, &size, flags)) < 0)
|
|
return -1;
|
|
|
|
l2.L2_trkoff = off;
|
|
l2.L2_len = (U16)len;
|
|
l2.L2_size = (U16)size;
|
|
l2.L2_pad = 0;
|
|
|
|
if (1
|
|
&& oldl2.L2_trkoff != CCKD64_NOSIZE
|
|
&& oldl2.L2_trkoff != CCKD64_MAXSIZE
|
|
&& oldl2.L2_trkoff < l2.L2_trkoff
|
|
)
|
|
after = 1;
|
|
|
|
/* Write the track image */
|
|
if ((rc = cckd64_write (dev, sfx, off, buf, len)) < 0)
|
|
return -1;
|
|
|
|
cckd->writes[sfx]++;
|
|
cckd->totwrites++;
|
|
cckdblk.stats_writes++;
|
|
cckdblk.stats_writebytes += rc;
|
|
}
|
|
else
|
|
{
|
|
l2.L2_trkoff = 0;
|
|
l2.L2_len = l2.L2_size = (U16)len;
|
|
l2.L2_pad = 0;
|
|
}
|
|
|
|
/* Update the level 2 entry */
|
|
if (cckd64_write_l2ent (dev, &l2, trk) < 0)
|
|
return -1;
|
|
|
|
/* Release the previous space */
|
|
cckd64_rel_space (dev, oldl2.L2_trkoff, (int)oldl2.L2_len, (int)oldl2.L2_size);
|
|
|
|
/* `after' is 1 if the new offset is after the old offset */
|
|
return after;
|
|
|
|
} /* end function cckd64_write_trkimg */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Harden the file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_harden(DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc=0; /* Return code */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_harden( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if ((dev->ckdrdonly && cckd->sfn == 0)
|
|
|| cckd->open[cckd->sfn] != CCKD_OPEN_RW)
|
|
return 0;
|
|
|
|
CCKD_TRACE( "file[%d] harden", cckd->sfn);
|
|
|
|
/* Write the compressed device header */
|
|
if (cckd64_write_chdr (dev) < 0)
|
|
rc = -1;
|
|
|
|
/* Write the level 1 table */
|
|
if (cckd64_write_l1 (dev) < 0)
|
|
rc = -1;
|
|
|
|
/* Write the free space chain */
|
|
if (cckd64_write_fsp (dev) < 0)
|
|
rc = -1;
|
|
|
|
/* Re-write the compressed device header */
|
|
cckd->cdevhdr[cckd->sfn].cdh_opts &= ~CCKD_OPT_OPENED;
|
|
if (cckd64_write_chdr (dev) < 0)
|
|
rc = -1;
|
|
|
|
if (cckdblk.fsync)
|
|
{
|
|
fdatasync (cckd->fd[cckd->sfn]);
|
|
cckdblk.stats_fsyncs++;
|
|
}
|
|
cckdblk.stats_hardens++;
|
|
|
|
return rc;
|
|
} /* cckd64_harden */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Build a null track */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_null_trk(DEVBLK *dev, BYTE *buf, int trk, int nullfmt)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int i; /* Loop counter */
|
|
CKD_TRKHDR *trkhdr; /* -> Track header */
|
|
CKD_RECHDR *rechdr; /* -> Record header */
|
|
U32 cyl; /* Cylinder number */
|
|
U32 head; /* Head number */
|
|
BYTE r; /* Record number */
|
|
BYTE *pos; /* -> Next position in buffer*/
|
|
int len; /* Length of null track */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_null_trk( dev, buf, trk, nullfmt );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (nullfmt < 0 || nullfmt > CKD_NULLTRK_FMTMAX)
|
|
nullfmt = cckd->cdevhdr[cckd->sfn].cdh_nullfmt;
|
|
|
|
// FIXME
|
|
// Compatibility check for nullfmt bug and linux -- 18 May 2005
|
|
// Remove at some reasonable date in the future
|
|
else if (nullfmt == 0
|
|
&& cckd->cdevhdr[cckd->sfn].cdh_nullfmt == CKD_NULLTRK_FMT2)
|
|
nullfmt = CKD_NULLTRK_FMT2;
|
|
|
|
if (cckd->ckddasd)
|
|
{
|
|
|
|
/* cylinder and head calculations */
|
|
cyl = trk / dev->ckdheads;
|
|
head = trk % dev->ckdheads;
|
|
|
|
/* Build the track header */
|
|
trkhdr = (CKD_TRKHDR*)buf;
|
|
trkhdr->bin = 0;
|
|
store_hw( &trkhdr->cyl, (U16) cyl );
|
|
store_hw( &trkhdr->head, (U16) head );
|
|
pos = buf + CKD_TRKHDR_SIZE;
|
|
|
|
/* Build record zero (R0): KL=0, DL=8 */
|
|
r = 0;
|
|
rechdr = (CKD_RECHDR*)pos;
|
|
pos += CKD_RECHDR_SIZE;
|
|
store_hw( &rechdr->cyl, (U16) cyl );
|
|
store_hw( &rechdr->head, (U16) head );
|
|
rechdr->rec = r;
|
|
rechdr->klen = 0;
|
|
store_hw( &rechdr->dlen, CKD_R0_DLEN );
|
|
memset( pos, 0, CKD_R0_DLEN );
|
|
pos += CKD_R0_DLEN;
|
|
r++;
|
|
|
|
/* Specific null track formatting */
|
|
if (nullfmt == CKD_NULLTRK_FMT0)
|
|
{
|
|
rechdr = (CKD_RECHDR*)pos;
|
|
pos += CKD_RECHDR_SIZE;
|
|
|
|
/* Build record one (R1): EOF record (KL=0, DL=0) */
|
|
store_hw( &rechdr->cyl, (U16) cyl );
|
|
store_hw( &rechdr->head, (U16) head );
|
|
rechdr->rec = r;
|
|
rechdr->klen = 0;
|
|
store_hw(&rechdr->dlen, 0);
|
|
r++;
|
|
}
|
|
else if (nullfmt == CKD_NULLTRK_FMT2)
|
|
{
|
|
/* Build 12 binary-0 data blocks, 4K each (KL=0, DL=4K) */
|
|
for (i = 0; i < 12; i++)
|
|
{
|
|
rechdr = (CKD_RECHDR*)pos;
|
|
pos += CKD_RECHDR_SIZE;
|
|
|
|
store_hw( &rechdr->cyl, (U16) cyl);
|
|
store_hw( &rechdr->head, (U16) head);
|
|
rechdr->rec = r;
|
|
rechdr->klen = 0;
|
|
store_hw( &rechdr->dlen, CKD_NULL_FMT2_DLEN );
|
|
r++;
|
|
memset( pos, 0, CKD_NULL_FMT2_DLEN );
|
|
pos += CKD_NULL_FMT2_DLEN;
|
|
}
|
|
}
|
|
|
|
/* Build the end of track marker */
|
|
memcpy( pos, &CKD_ENDTRK, CKD_ENDTRK_SIZE );
|
|
pos += CKD_ENDTRK_SIZE;
|
|
len = (int)(pos - buf);
|
|
}
|
|
else
|
|
{
|
|
len = CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE;
|
|
memset( buf, 0, len );
|
|
store_fw( buf+1, trk );
|
|
}
|
|
|
|
CCKD_TRACE( "null_trk %s %d format %d size %d",
|
|
cckd->ckddasd ? "trk" : "blkgrp", trk, nullfmt, len);
|
|
|
|
return len;
|
|
|
|
} /* end function cckd64_null_trk */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return a number 0 .. CKD_NULLTRK_FMTMAX if track is null */
|
|
/* else return the original length */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_check_null_trk (DEVBLK *dev, BYTE *buf, int trk, int len)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
BYTE buf2[65536]; /* Null track buffer */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_check_null_trk( dev, buf, trk, len );
|
|
|
|
cckd = dev->cckd_ext;
|
|
rc = len;
|
|
|
|
if (len == CKD_NULLTRK_SIZE0)
|
|
rc = CKD_NULLTRK_FMT0;
|
|
else if (len == CKD_NULLTRK_SIZE1)
|
|
rc = CKD_NULLTRK_FMT1;
|
|
else if (len == CKD_NULLTRK_SIZE2 && dev->oslinux
|
|
&& (!cckd->notnull || cckdblk.linuxnull))
|
|
{
|
|
cckd64_null_trk (dev, buf2, trk, 0);
|
|
if (memcmp(buf, buf2, len) == 0)
|
|
rc = CKD_NULLTRK_FMT2;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Verify a track/block header and return track/block number */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_cchh( DEVBLK* dev, BYTE* buf, int trk )
|
|
{
|
|
CCKD64_EXT* cckd; /* -> cckd extension */
|
|
U16 cyl; /* Cylinder */
|
|
U16 head; /* Head */
|
|
int t; /* Calculated track */
|
|
BYTE badcomp=0; /* 1=Unsupported compression */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* CKD dasd header verification */
|
|
if (cckd->ckddasd)
|
|
{
|
|
cyl = fetch_hw( buf + 1 );
|
|
head = fetch_hw( buf + 3 );
|
|
t = cyl * dev->ckdheads + head;
|
|
|
|
if (1
|
|
&& cyl < dev->ckdcyls
|
|
&& head < dev->ckdheads
|
|
&& (trk == -1 || t == trk)
|
|
)
|
|
{
|
|
if (buf[0] & ~cckdblk.comps)
|
|
{
|
|
if (buf[0] & ~CCKD_COMPRESS_MASK)
|
|
{
|
|
if (cckdblk.bytemsgs++ < 10)
|
|
// "%1d:%04X CCKD file[%d] %s: invalid byte 0 trk %d, buf %2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
WRMSG( HHC00307, "E", LCSS_DEVNUM, cckd->sfn,
|
|
cckd_sf_name( dev, cckd->sfn ), t, buf[0],buf[1],buf[2],buf[3],buf[4]);
|
|
buf[0] &= CCKD_COMPRESS_MASK;
|
|
}
|
|
}
|
|
if (buf[0] & ~cckdblk.comps)
|
|
badcomp = 1;
|
|
else
|
|
return t;
|
|
}
|
|
}
|
|
/* FBA dasd header verification */
|
|
else
|
|
{
|
|
t = fetch_fw (buf + 1);
|
|
if (t < dev->fbanumblk && (trk == -1 || t == trk))
|
|
{
|
|
if (buf[0] & ~cckdblk.comps)
|
|
{
|
|
if (buf[0] & ~CCKD_COMPRESS_MASK)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: invalid byte 0 blkgrp %d, buf %2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
WRMSG( HHC00308, "E", LCSS_DEVNUM, cckd->sfn,
|
|
cckd_sf_name( dev, cckd->sfn ), t, buf[0],buf[1],buf[2],buf[3],buf[4]);
|
|
buf[0] &= CCKD_COMPRESS_MASK;
|
|
}
|
|
}
|
|
if (buf[0] & ~cckdblk.comps)
|
|
badcomp = 1;
|
|
else
|
|
return t;
|
|
}
|
|
}
|
|
|
|
if (badcomp)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: invalid %s hdr %s %d: %s compression unsupported"
|
|
WRMSG( HHC00309, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ),
|
|
cckd->ckddasd ? "trk" : "blk",
|
|
cckd->ckddasd ? "trk" : "blk", t, compname[buf[0]]);
|
|
}
|
|
else
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: invalid %s hdr %s %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
WRMSG( HHC00310, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ),
|
|
cckd->ckddasd ? "trk" : "blk",
|
|
cckd->ckddasd ? "trk" : "blk", trk,
|
|
buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
|
|
cckd_print_itrace();
|
|
}
|
|
|
|
return -1;
|
|
} /* end function cckd64_cchh */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Validate a track image */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_validate (DEVBLK *dev, BYTE *buf, int trk, int len)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int r; /* Record number */
|
|
int sz; /* Track size */
|
|
int vlen; /* Validation length */
|
|
int kl, dl; /* Key/Data lengths */
|
|
CKD_RECHDR rn; /* Record-n (r0, r1 ... rn) */
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (!buf || len < 0)
|
|
return -1;
|
|
|
|
CCKD_TRACE( "validating %s %d len %d %2.2x%2.2x%2.2x%2.2x%2.2x "
|
|
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
cckd->ckddasd ? "trk" : "blkgrp", trk, len,
|
|
buf[0], buf[ 1], buf[ 2], buf[ 3], buf[4], // trkhdr
|
|
buf[5], buf[ 6], buf[ 7], buf[ 8], // r0 (0-3)
|
|
buf[9], buf[10], buf[11], buf[12] ); // r0 (4-7)
|
|
|
|
/* FBA dasd check */
|
|
if (cckd->fbadasd)
|
|
{
|
|
if (!len || len == CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE)
|
|
return len;
|
|
CCKD_TRACE( "validation failed: bad length%s", "" );
|
|
return -1;
|
|
}
|
|
|
|
/* validate record 0 */
|
|
|
|
memcpy( &rn, &buf[CKD_TRKHDR_SIZE], CKD_R0_SIZE );
|
|
rn.cyl[0] &= 0x7f; /* fix for ovflow */
|
|
|
|
if (0
|
|
|| rn.rec != 0
|
|
|| rn.klen != 0
|
|
|| fetch_hw( rn.dlen ) != CKD_R0_DLEN
|
|
)
|
|
{
|
|
CCKD_TRACE( "validation failed: bad r0%s", "" );
|
|
return -1;
|
|
}
|
|
|
|
/* validate records 1 thru n */
|
|
|
|
vlen = len > 0 ? len : dev->ckdtrksz;
|
|
|
|
for (r = 1, sz = CKD_TRKHDR_SIZE + (CKD_R0_SIZE + CKD_R0_DLEN);
|
|
sz + CKD_ENDTRK_SIZE <= vlen;
|
|
sz += CKD_RECHDR_SIZE + kl + dl, r++)
|
|
{
|
|
memcpy( &rn, &buf[sz], CKD_RECHDR_SIZE );
|
|
|
|
if (memcmp( &rn, &CKD_ENDTRK, CKD_ENDTRK_SIZE ) == 0)
|
|
break;
|
|
|
|
kl = rn.klen;
|
|
dl = fetch_hw( rn.dlen );
|
|
|
|
if (rn.rec == 0 || sz + CKD_RECHDR_SIZE + kl + dl >= vlen)
|
|
{
|
|
CCKD_TRACE( "validation failed: bad r%d "
|
|
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
|
|
r, buf[sz+0], buf[sz+1], buf[sz+2], buf[sz+3],
|
|
buf[sz+4], buf[sz+5], buf[sz+6], buf[sz+7] );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
sz += CKD_ENDTRK_SIZE;
|
|
|
|
if ((len > 0 && sz != len) || sz > vlen)
|
|
{
|
|
CCKD_TRACE( "validation failed: no eot%s", "" );
|
|
return -1;
|
|
}
|
|
|
|
return sz;
|
|
|
|
} /* end function cckd64_validate */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize shadow files */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_sf_init (DEVBLK *dev)
|
|
{
|
|
CKD_DEVHDR devhdr; /* Device header */
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int i; /* Index */
|
|
struct stat st; /* stat() buffer */
|
|
char pathname[MAX_PATH]; /* file path in host format */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_sf_init( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* return if no shadow files */
|
|
if (dev->dasdsfn == NULL) return 0;
|
|
|
|
/* Check for shadow file name collision */
|
|
for (i = 1; i <= CCKD_MAX_SF && dev->dasdsfn != NULL; i++)
|
|
{
|
|
DEVBLK *dev2;
|
|
CCKD64_EXT *cckd2;
|
|
int j;
|
|
|
|
for (dev2 = cckdblk.dev1st; dev2; dev2 = cckd2->devnext)
|
|
{
|
|
cckd2 = dev2->cckd_ext;
|
|
if (dev2 == dev) continue;
|
|
for (j = 0; j <= CCKD_MAX_SF && dev2->dasdsfn != NULL; j++)
|
|
{
|
|
if (strcmp (cckd_sf_name(dev, i),cckd_sf_name(dev2, j)) == 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file name collides with %1d:%04X file[%d] %s"
|
|
WRMSG (HHC00311, "E", LCSS_DEVNUM, i, cckd_sf_name(dev, i),
|
|
SSID_TO_LCSS(dev2->ssid), dev2->devnum, j, cckd_sf_name(dev2, j));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* open all existing shadow files */
|
|
for (cckd->sfn = 1; cckd->sfn <= CCKD_MAX_SF; cckd->sfn++)
|
|
{
|
|
/* If no more shadow files remaining then we're done */
|
|
hostpath( pathname, cckd_sf_name( dev, cckd->sfn ), sizeof( pathname ));
|
|
if (stat( pathname, &st ) < 0)
|
|
break;
|
|
|
|
/* Try to open the shadow file read-write then read-only */
|
|
if ( cckd64_open( dev, cckd->sfn, O_RDWR | O_BINARY, 1 ) < 0)
|
|
if (cckd64_open( dev, cckd->sfn, O_RDONLY | O_BINARY, 0 ) < 0)
|
|
break;
|
|
|
|
/* Make sure shadow file type matches base image type */
|
|
if (cckd_read( dev, cckd->sfn, 0, &devhdr, CKD_DEVHDR_SIZE ) < 0)
|
|
return -1;
|
|
if (!is_dh_devid_typ( devhdr.dh_devid, ANY64_SF_TYP ))
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: cckd/64 format differs from base"
|
|
WRMSG( HHC00351, "E", LCSS_DEVNUM, cckd->sfn, pathname );
|
|
return -1;
|
|
}
|
|
|
|
/* Call the chkdsk function */
|
|
if ((rc = cckd64_chkdsk( dev, 0 )) < 0)
|
|
return -1;
|
|
|
|
/* Perform initial read */
|
|
rc = cckd64_read_init( dev );
|
|
}
|
|
|
|
/* Backup to the last opened file number */
|
|
cckd->sfn--;
|
|
|
|
/* If the last file was opened read-only then create a new one */
|
|
if (cckd->open[cckd->sfn] == CCKD_OPEN_RO)
|
|
{
|
|
/* but ONLY IF not explicit batch utility READ-ONLY open */
|
|
if (!(1
|
|
&& dev->batch
|
|
&& dev->ckdrdonly
|
|
))
|
|
{
|
|
/* NOT explicit batch utility read-only open: create new shadow file */
|
|
if (cckd64_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 (cckd64_open (dev, i, O_RDONLY|O_BINARY, 0) < 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: error re-opening readonly: %s"
|
|
WRMSG (HHC00312, "E", LCSS_DEVNUM, i, cckd_sf_name(dev, i), strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
} /* end function cckd64_sf_init */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Create a new shadow file */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_sf_new (DEVBLK *dev)
|
|
{
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int l1size; /* Size of level 1 table */
|
|
CKD_DEVHDR devhdr; /* Device header */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_sf_new( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
CCKD_TRACE( "file[%d] sf_new %s", cckd->sfn+1,
|
|
cckd_sf_name(dev, cckd->sfn+1) ?
|
|
(char *)cckd_sf_name(dev, cckd->sfn+1) : "(none)");
|
|
|
|
/* Error if no shadow file name */
|
|
if (dev->dasdsfn == NULL)
|
|
{
|
|
WRMSG (HHC00313, "E", LCSS_DEVNUM, cckd->sfn+1);
|
|
return -1;
|
|
}
|
|
|
|
/* Error if max number of shadow files exceeded */
|
|
if (cckd->sfn+1 == CCKD_MAX_SF)
|
|
{
|
|
WRMSG (HHC00314, "E", LCSS_DEVNUM, cckd->sfn+1, dev->dasdsfn);
|
|
return -1;
|
|
}
|
|
|
|
/* Harden the current file */
|
|
cckd64_harden (dev);
|
|
|
|
/* Open the new shadow file */
|
|
if (cckd64_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 (cckd64_read (dev, cckd->sfn, 0, &devhdr, CKD_DEVHDR_SIZE) < 0)
|
|
goto sf_new_error;
|
|
|
|
/* Make sure identifier is CKD_S370 or FBA_S370 */
|
|
devhdr.dh_devid[4] = 'S';
|
|
|
|
/* Write new file's device header */
|
|
if (cckd64_write (dev, cckd->sfn+1, 0, &devhdr, CKD_DEVHDR_SIZE) < 0)
|
|
goto sf_new_error;
|
|
|
|
/* Build the compressed device header */
|
|
memcpy (&cckd->cdevhdr[cckd->sfn+1], &cckd->cdevhdr[cckd->sfn], CCKD64_DEVHDR_SIZE);
|
|
l1size = cckd->cdevhdr[cckd->sfn+1].num_L1tab * CCKD64_L1ENT_SIZE;
|
|
cckd->cdevhdr[cckd->sfn+1].cdh_size =
|
|
cckd->cdevhdr[cckd->sfn+1].cdh_used = CKD_DEVHDR_SIZE + CCKD64_DEVHDR_SIZE + l1size;
|
|
cckd->cdevhdr[cckd->sfn+1].free_off =
|
|
cckd->cdevhdr[cckd->sfn+1].free_total =
|
|
cckd->cdevhdr[cckd->sfn+1].free_largest =
|
|
cckd->cdevhdr[cckd->sfn+1].free_num =
|
|
cckd->cdevhdr[cckd->sfn+1].free_imbed = 0;
|
|
|
|
/* Init the level 1 table */
|
|
if ((cckd->L1tab[cckd->sfn+1] = cckd_malloc (dev, "l1", l1size)) == NULL)
|
|
goto sf_new_error;
|
|
memset (cckd->L1tab[cckd->sfn+1], 0xff, l1size);
|
|
|
|
/* Make the new file active */
|
|
cckd->sfn++;
|
|
|
|
/* Harden the new file */
|
|
if (cckd64_harden (dev) < 0)
|
|
{
|
|
cckd->sfn--;
|
|
goto sf_new_error;
|
|
}
|
|
|
|
/* Re-read the l1 to set L2_bounds, L2ok */
|
|
cckd64_read_l1 (dev);
|
|
|
|
return 0;
|
|
|
|
sf_new_error:
|
|
cckd->L1tab[cckd->sfn+1] = cckd_free(dev, "l1", cckd->L1tab[cckd->sfn+1]);
|
|
cckd64_close (dev, cckd->sfn+1);
|
|
cckd->open[cckd->sfn+1] = CCKD_OPEN_NONE;
|
|
unlink (cckd_sf_name (dev, cckd->sfn+1));
|
|
|
|
/* Re-read the l1 to set L2_bounds, L2ok */
|
|
cckd64_read_l1 (dev);
|
|
|
|
return -1;
|
|
|
|
} /* end function cckd64_sf_new */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Add a shadow file (sf+) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* cckd64_sf_add( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
|
|
if (dev == NULL)
|
|
{
|
|
int n = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
if (dev->cckd_ext)
|
|
{
|
|
WRMSG (HHC00315, "I", LCSS_DEVNUM );
|
|
cckd64_sf_add (dev);
|
|
n++;
|
|
}
|
|
WRMSG(HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
if (!cckd)
|
|
{
|
|
WRMSG (HHC00317, "E", LCSS_DEVNUM);
|
|
return NULL;
|
|
}
|
|
|
|
/* Schedule updated track entries to be written */
|
|
obtain_lock (&cckd->cckdiolock);
|
|
if (cckd->merging)
|
|
{
|
|
release_lock (&cckd->cckdiolock);
|
|
WRMSG (HHC00318, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name (dev, cckd->sfn));
|
|
return NULL;
|
|
}
|
|
cckd->merging = 1;
|
|
cckd64_flush_cache (dev);
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
|
|
cckd->cckdwaiters--;
|
|
cckd64_flush_cache (dev);
|
|
}
|
|
cckd64_purge_cache (dev); cckd64_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 */
|
|
cckd64_harden (dev);
|
|
|
|
/* Create a new shadow file */
|
|
if (cckd64_sf_new (dev) < 0)
|
|
{
|
|
WRMSG (HHC00319, "E", LCSS_DEVNUM, cckd->sfn+1,
|
|
cckd_sf_name(dev, cckd->sfn+1)?cckd_sf_name(dev, cckd->sfn+1):"(null)");
|
|
goto cckd_sf_add_exit;
|
|
}
|
|
|
|
/* Re-open the previous file if opened read-write */
|
|
if (cckd->open[cckd->sfn-1] == CCKD_OPEN_RW)
|
|
cckd64_open (dev, cckd->sfn-1, O_RDONLY|O_BINARY, 0);
|
|
|
|
WRMSG (HHC00320, "I", LCSS_DEVNUM, cckd->sfn, cckd_sf_name (dev, cckd->sfn));
|
|
|
|
cckd_sf_add_exit:
|
|
|
|
/* Re-read the l1 to set L2_bounds, L2ok */
|
|
cckd64_read_l1 (dev);
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock (&cckd->cckdiolock);
|
|
cckd->merging = 0;
|
|
if (cckd->cckdwaiters)
|
|
broadcast_condition (&cckd->cckdiocond);
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
cckd64_sf_stats (dev);
|
|
return NULL;
|
|
} /* end function cckd64_sf_add */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Remove a shadow file (sf-) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* cckd64_sf_remove( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int from_sfx, to_sfx; /* From/to file index */
|
|
int fix; /* nullfmt index */
|
|
int add = 0; /* 1=Add shadow file back */
|
|
int l2updated = 0; /* 1=L2 table was updated */
|
|
int i,j; /* Loop indexes */
|
|
int merge, force; /* Flags */
|
|
U64 pos; /* File offset */
|
|
unsigned int len; /* Length to read/write */
|
|
int size; /* Image size */
|
|
int trk = -1; /* Track being read/written */
|
|
CCKD64_L2ENT from_l2[256], /* Level 2 tables */
|
|
to_l2[256];
|
|
CCKD64_L2ENT new_l2; /* New level 2 table entry */
|
|
BYTE buf[64*1024]; /* Buffer */
|
|
|
|
/* NULL DEVBLK == all devices? */
|
|
if (!dev)
|
|
{
|
|
int n = 0;
|
|
merge = cckdblk.sfmerge;
|
|
force = cckdblk.sfforce;
|
|
cckdblk.sfmerge = cckdblk.sfforce = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
{
|
|
if ((cckd = dev->cckd_ext)) /* Compressed device? */
|
|
{
|
|
if (cckd->sfn) /* Any active shadow file(s)? */
|
|
{
|
|
// "%1d:%04X CCKD file: merging shadow files..."
|
|
WRMSG( HHC00321, "I", LCSS_DEVNUM );
|
|
cckd->sfmerge = merge;
|
|
cckd->sfforce = force;
|
|
cckd64_sf_remove( dev );
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
// "CCKD file number of devices processed: %d"
|
|
WRMSG( HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
/* Specific device... */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_sf_remove( data );
|
|
|
|
if (!(cckd = dev->cckd_ext))
|
|
{
|
|
// "%1d:%04X CCKD file: device is not a cckd device"
|
|
WRMSG( HHC00317, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
if (!cckd->sfn)
|
|
{
|
|
// "%1d:%04X CCKD file: device has no shadow files"
|
|
WRMSG( HHC00390, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
/* Set flags */
|
|
merge = cckd->sfmerge || cckd->sfforce;
|
|
force = cckd->sfforce;
|
|
cckd->sfmerge = cckd->sfforce = 0;
|
|
|
|
CCKD_TRACE( "merge starting: %s %s",
|
|
merge ? "merge" : "nomerge", force ? "force" : "");
|
|
|
|
/* Schedule updated track entries to be written */
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
if (cckd->merging)
|
|
{
|
|
release_lock( &cckd->cckdiolock );
|
|
// "%1d:%04X CCKD file[%d] %s: error merging shadow file, sf command busy on device"
|
|
WRMSG( HHC00322, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn) );
|
|
return NULL;
|
|
}
|
|
cckd->merging = 1;
|
|
cckd64_flush_cache (dev);
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
wait_condition( &cckd->cckdiocond, &cckd->cckdiolock );
|
|
cckd->cckdwaiters--;
|
|
cckd64_flush_cache( dev );
|
|
}
|
|
cckd64_purge_cache( dev );
|
|
cckd64_purge_l2( dev );
|
|
dev->bufcur = dev->cache = -1;
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
obtain_lock( &cckd->filelock );
|
|
|
|
if (!cckd->sfn)
|
|
{
|
|
release_lock( &cckd->filelock );
|
|
// "%1d:%04X CCKD file[%d] %s: cannot remove base file"
|
|
WRMSG( HHC00323, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn) );
|
|
cckd->merging = 0;
|
|
return NULL;
|
|
}
|
|
|
|
from_sfx = cckd->sfn;
|
|
to_sfx = cckd->sfn - 1;
|
|
fix = cckd->cdevhdr[ to_sfx ].cdh_nullfmt;
|
|
|
|
/* Harden the `from' file */
|
|
if (cckd64_harden( dev ) < 0)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged: file[%d] %s%s"
|
|
WRMSG( HHC00324, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, cckd->sfx ),
|
|
from_sfx, "not hardened", "" );
|
|
goto sf_remove_exit;
|
|
}
|
|
|
|
/* Attempt to re-open the `to' file read-write */
|
|
cckd64_close( dev, to_sfx );
|
|
if (to_sfx > 0 || !dev->ckdrdonly || force)
|
|
cckd64_open( dev, to_sfx, O_RDWR | O_BINARY, 1 );
|
|
if (cckd->fd[to_sfx] < 0)
|
|
{
|
|
/* `from' file can't be opened read-write */
|
|
cckd64_open( dev, to_sfx, O_RDONLY | O_BINARY, 0 );
|
|
if (merge)
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged: file[%d] %s%s"
|
|
WRMSG( HHC00324, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ),
|
|
to_sfx, "cannot be opened read-write",
|
|
to_sfx == 0 && dev->ckdrdonly && !force ? ", try 'force'" : "" );
|
|
goto sf_remove_exit;
|
|
}
|
|
else
|
|
add = 1;
|
|
}
|
|
else
|
|
{
|
|
/* `from' file opened read-write */
|
|
cckd->sfn = to_sfx;
|
|
|
|
if (cckd64_chkdsk( dev, 0 ) < 0)
|
|
{
|
|
cckd->sfn = from_sfx;
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged: file[%d] %s%s"
|
|
WRMSG( HHC00324, "E", LCSS_DEVNUM, to_sfx, cckd_sf_name( dev, to_sfx ),
|
|
to_sfx, "check failed", "" );
|
|
goto sf_remove_exit;
|
|
}
|
|
}
|
|
|
|
cckd->sfn = to_sfx;
|
|
|
|
/* Perform backwards merge */
|
|
if (merge)
|
|
{
|
|
CCKD_TRACE( "merging to file[%d]", to_sfx );
|
|
|
|
/* Make the target file the active file */
|
|
cckd->sfn = to_sfx;
|
|
cckd->cdevhdr[to_sfx].cdh_opts |= (CCKD_OPT_OPENED | CCKD_OPT_OPENRW);
|
|
|
|
/* Loop for each level 1 table entry */
|
|
for (i = 0; i < cckd->cdevhdr[from_sfx].num_L1tab; i++)
|
|
{
|
|
l2updated = 0;
|
|
/* Continue if from L2 doesn't exist */
|
|
if (cckd->L1tab[from_sfx][i] == CCKD64_MAXSIZE
|
|
|| (cckd->L1tab[from_sfx][i] == 0 && cckd->L1tab[to_sfx][i] == 0))
|
|
continue;
|
|
|
|
trk = i*256;
|
|
|
|
/* Read `from' l2 table */
|
|
if (cckd->L1tab[from_sfx][i] == 0)
|
|
memset( &from_l2, 0, CCKD64_L2TAB_SIZE );
|
|
else
|
|
{
|
|
pos = cckd->L1tab[from_sfx][i];
|
|
if (cckd64_read( dev, from_sfx, pos, &from_l2, CCKD64_L2TAB_SIZE ) < 0)
|
|
goto sf_merge_error;
|
|
}
|
|
|
|
/* Read `to' l2 table */
|
|
if (cckd->L1tab[to_sfx][i] == 0)
|
|
memset( &to_l2, 0, CCKD64_L2TAB_SIZE );
|
|
else if (cckd->L1tab[to_sfx][i] == CCKD64_MAXSIZE)
|
|
memset (&to_l2, 0xff, CCKD64_L2TAB_SIZE);
|
|
else
|
|
{
|
|
pos = cckd->L1tab[to_sfx][i];
|
|
if (cckd64_read( dev, to_sfx, pos, &to_l2, CCKD64_L2TAB_SIZE ) < 0)
|
|
goto sf_merge_error;
|
|
}
|
|
|
|
/* Loop for each level 2 table entry */
|
|
for (j = 0; j < 256; j++)
|
|
{
|
|
trk = i*256 + j;
|
|
/* Continue if from L2 entry doesn't exist */
|
|
if (from_l2[j].L2_trkoff == CCKD64_MAXSIZE
|
|
|| (from_l2[j].L2_trkoff == 0 && to_l2[j].L2_trkoff == 0))
|
|
continue;
|
|
|
|
/* Read the `from' track/blkgrp image */
|
|
len = (unsigned int)from_l2[j].L2_len;
|
|
if (len > CKD_NULLTRK_FMTMAX)
|
|
{
|
|
pos = from_l2[j].L2_trkoff;
|
|
if (cckd64_read( dev, from_sfx, pos, buf, len ) < 0)
|
|
goto sf_merge_error;
|
|
|
|
/* Get space for the `to' track/blkgrp image */
|
|
size = len;
|
|
if ((S64)(pos = cckd64_get_space (dev, &size, CCKD_SIZE_EXACT)) < 0)
|
|
goto sf_merge_error;
|
|
|
|
new_l2.L2_trkoff = pos;
|
|
new_l2.L2_len = (U16)len;
|
|
new_l2.L2_size = (U16)size;
|
|
|
|
/* Write the `to' track/blkgrp image */
|
|
if (cckd64_write( dev, to_sfx, pos, buf, len ) < 0)
|
|
goto sf_merge_error;
|
|
}
|
|
else
|
|
{
|
|
new_l2.L2_trkoff = 0;
|
|
new_l2.L2_len = new_l2.L2_size = (U16)len;
|
|
}
|
|
|
|
/* Release space occupied by old `to' entry */
|
|
cckd64_rel_space( dev, to_l2[j].L2_trkoff, (int)to_l2[j].L2_len,
|
|
(int)to_l2[j].L2_size );
|
|
|
|
/* Update `to' l2 table entry */
|
|
l2updated = 1;
|
|
to_l2[j].L2_trkoff = new_l2.L2_trkoff;
|
|
to_l2[j].L2_len = new_l2.L2_len;
|
|
to_l2[j].L2_size = new_l2.L2_size;
|
|
} /* for each level 2 table entry */
|
|
|
|
/* Update the `to' level 2 table */
|
|
if (l2updated)
|
|
{
|
|
l2updated = 0;
|
|
pos = cckd->L1tab[to_sfx][i];
|
|
if (memcmp( &to_l2, &empty64_l2[fix], CCKD64_L2TAB_SIZE ) == 0)
|
|
{
|
|
cckd64_rel_space( dev, pos, CCKD64_L2TAB_SIZE, CCKD64_L2TAB_SIZE );
|
|
pos = 0;
|
|
}
|
|
else
|
|
{
|
|
size = CCKD64_L2TAB_SIZE;
|
|
if (pos == CCKD64_NOSIZE || pos == CCKD64_MAXSIZE)
|
|
if ((S64)(pos = cckd64_get_space( dev, &size, CCKD_L2SPACE )) < 0)
|
|
goto sf_merge_error;
|
|
if (cckd64_write( dev, to_sfx, pos, &to_l2, CCKD64_L2TAB_SIZE ) < 0)
|
|
goto sf_merge_error;
|
|
} /* `to' level 2 table not null */
|
|
|
|
/* Update the level 1 table index */
|
|
cckd->L1tab[to_sfx][i] = pos;
|
|
|
|
/* Flush free space */
|
|
cckd64_flush_space( dev );
|
|
|
|
} /* Update level 2 table */
|
|
|
|
} /* For each level 1 table entry */
|
|
|
|
/* Validate the merge */
|
|
cckd64_harden( dev );
|
|
cckd64_chkdsk( dev, 0 );
|
|
cckd64_read_init( dev );
|
|
|
|
} /* if merge */
|
|
|
|
/* Remove the old file */
|
|
cckd64_close( dev, from_sfx );
|
|
cckd->L1tab[from_sfx] = cckd_free( dev, "l1", cckd->L1tab[ from_sfx ]);
|
|
memset( &cckd->cdevhdr[from_sfx], 0, CCKD64_DEVHDR_SIZE );
|
|
rc = unlink (cckd_sf_name (dev, from_sfx));
|
|
|
|
/* adjust the stats */
|
|
cckd->reads [to_sfx] += cckd->reads [from_sfx];
|
|
cckd->writes [to_sfx] += cckd->writes [from_sfx];
|
|
cckd->L2_reads[to_sfx] += cckd->L2_reads[from_sfx];
|
|
|
|
cckd->reads [from_sfx] = 0;
|
|
cckd->writes [from_sfx] = 0;
|
|
cckd->L2_reads[from_sfx] = 0;
|
|
|
|
/* Add the file back if necessary */
|
|
if (add) rc = cckd64_sf_new( dev ) ;
|
|
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file successfully %s"
|
|
WRMSG( HHC00325, "I", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ),
|
|
merge ? "merged" : add ? "re-added" : "removed" );
|
|
|
|
sf_remove_exit:
|
|
|
|
/* Re-read the l1 to set L2_bounds, L2ok */
|
|
cckd64_read_l1( dev );
|
|
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cckd64_purge_cache( dev);
|
|
cckd64_purge_l2( dev );
|
|
dev->bufcur = dev->cache = -1;
|
|
cckd->merging = 0;
|
|
if (cckd->cckdwaiters)
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
CCKD_TRACE( "merge complete%s", "" );
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
cckd64_sf_stats( dev );
|
|
return NULL;
|
|
|
|
sf_merge_error:
|
|
|
|
if (trk < 0)
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged, error during merge"
|
|
WRMSG( HHC00326, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ));
|
|
else
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file not merged, error processing trk(%d)"
|
|
WRMSG( HHC00327, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ), trk );
|
|
|
|
if (l2updated && cckd->L1tab[ to_sfx ][i] && cckd->L1tab[ to_sfx ][i] != CCKD64_SHADOW_NO_OFFSET)
|
|
{
|
|
l2updated = 0;
|
|
pos = cckd->L1tab[to_sfx][i];
|
|
cckd64_write( dev, to_sfx, pos, &to_l2, CCKD64_L2TAB_SIZE );
|
|
}
|
|
cckd64_harden( dev );
|
|
cckd64_chkdsk( dev, 2 );
|
|
cckd->sfn = from_sfx;
|
|
cckd64_harden( dev );
|
|
cckd64_chkdsk( dev, 2 );
|
|
goto sf_remove_exit;
|
|
|
|
} /* end function cckd64_sf_remove */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check and compress a shadow file (sfc) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void *cckd64_sf_comp( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
|
|
/* NULL DEVBLK == all devices? */
|
|
if (!dev)
|
|
{
|
|
int n = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
{
|
|
if (dev->cckd_ext) /* Is this a compressed device? */
|
|
{
|
|
// "%1d:%04X CCKD file: compressing shadow files..."
|
|
WRMSG( HHC00328, "I", LCSS_DEVNUM );
|
|
cckd64_sf_comp( dev );
|
|
n++;
|
|
}
|
|
}
|
|
// "CCKD file number of devices processed: %d"
|
|
WRMSG ( HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_sf_comp( data );
|
|
|
|
if (!(cckd = dev->cckd_ext))
|
|
{
|
|
// "%1d:%04X CCKD file: device is not a cckd device"
|
|
WRMSG( HHC00317, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
/* schedule updated track entries to be written */
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
if (cckd->merging)
|
|
{
|
|
release_lock( &cckd->cckdiolock );
|
|
// "%1d:%04X CCKD file[%d] %s: error compressing shadow file, sf command busy on device"
|
|
WRMSG( HHC00329, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn) );
|
|
return NULL;
|
|
}
|
|
cckd->merging = 1;
|
|
cckd64_flush_cache( dev );
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
wait_condition( &cckd->cckdiocond, &cckd->cckdiolock );
|
|
cckd->cckdwaiters--;
|
|
cckd64_flush_cache( dev );
|
|
}
|
|
cckd64_purge_cache( dev );
|
|
cckd64_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 */
|
|
cckd64_harden( dev );
|
|
|
|
/* Call the compress function */
|
|
rc = cckd64_comp( dev );
|
|
|
|
/* Perform initial read */
|
|
rc = cckd64_read_init( dev );
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cckd->merging = 0;
|
|
if (cckd->cckdwaiters)
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* Display the shadow file statistics */
|
|
cckd64_sf_stats( dev );
|
|
|
|
return NULL;
|
|
} /* end function cckd64_sf_comp */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check a shadow file (sfk) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* cckd64_sf_chk( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int rc; /* Return code */
|
|
int level = 2; /* Check level */
|
|
|
|
/* NULL DEVBLK == all devices? */
|
|
if (!dev)
|
|
{
|
|
int n = 0;
|
|
level = cckdblk.sflevel;
|
|
cckdblk.sflevel = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
{
|
|
if ((cckd = dev->cckd_ext)) /* Compressed device? */
|
|
{
|
|
// "%1d:%04X CCKD file: checking level %d..."
|
|
WRMSG( HHC00330, "I", LCSS_DEVNUM, level );
|
|
cckd->sflevel = level;
|
|
cckd64_sf_chk( dev );
|
|
n++;
|
|
}
|
|
}
|
|
// "CCKD file number of devices processed: %d"
|
|
WRMSG( HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_sf_chk( data );
|
|
|
|
if (!(cckd = dev->cckd_ext))
|
|
{
|
|
// "%1d:%04X CCKD file: device is not a cckd device"
|
|
WRMSG( HHC00317, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
level = cckd->sflevel;
|
|
cckd->sflevel = 0;
|
|
|
|
/* schedule updated track entries to be written */
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
if (cckd->merging)
|
|
{
|
|
release_lock( &cckd->cckdiolock );
|
|
// "%1d:%04X CCKD file[%d] %s: shadow file check failed, sf command busy on device"
|
|
WRMSG( HHC00331, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ));
|
|
return NULL;
|
|
}
|
|
|
|
cckd->merging = 1;
|
|
cckd64_flush_cache( dev );
|
|
|
|
while (cckd->wrpending || cckd->cckdioact)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
{
|
|
wait_condition( &cckd->cckdiocond, &cckd->cckdiolock );
|
|
}
|
|
cckd->cckdwaiters--;
|
|
|
|
cckd64_flush_cache( dev );
|
|
}
|
|
cckd64_purge_cache( dev );
|
|
cckd64_purge_l2( dev );
|
|
dev->bufcur = -1;
|
|
dev->cache = -1;
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* obtain control of the file */
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
/* harden the current file */
|
|
cckd64_harden( dev );
|
|
|
|
/* Call the chkdsk function */
|
|
rc = cckd64_chkdsk( dev, level );
|
|
|
|
/* Perform initial read */
|
|
rc = cckd64_read_init( dev );
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
|
|
obtain_lock( &cckd->cckdiolock );
|
|
{
|
|
cckd->merging = 0;
|
|
if (cckd->cckdwaiters)
|
|
broadcast_condition( &cckd->cckdiocond );
|
|
}
|
|
release_lock( &cckd->cckdiolock );
|
|
|
|
/* Display the shadow file statistics */
|
|
cckd64_sf_stats( dev );
|
|
|
|
return NULL;
|
|
} /* end function cckd64_sf_chk */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Display shadow file statistics (sfd) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* cckd64_sf_stats( void* data )
|
|
{
|
|
DEVBLK *dev = data; /* -> DEVBLK */
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
struct stat st = {0}; /* File information */
|
|
int i; /* Index */
|
|
char *ost[] = {" ", "ro", "rd", "rw"};
|
|
U64 usize=0,ufree=0; /* Total size, free space */
|
|
S64 free_count=0; /* Total number free spaces */
|
|
|
|
/* NULL DEVBLK == all devices? */
|
|
if (!dev)
|
|
{
|
|
int n = 0;
|
|
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
|
|
{
|
|
if (dev->cckd_ext) /* Is this a compressed device? */
|
|
{
|
|
// "%1d:%04X CCKD file: display cckd statistics"
|
|
WRMSG( HHC00332, "I", LCSS_DEVNUM );
|
|
cckd64_sf_stats( dev );
|
|
n++;
|
|
}
|
|
}
|
|
// "CCKD file number of devices processed: %d"
|
|
WRMSG( HHC00316, "I", n );
|
|
return NULL;
|
|
}
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_sf_stats( data );
|
|
|
|
if (!(cckd = dev->cckd_ext))
|
|
{
|
|
// "%1d:%04X CCKD file: device is not a cckd device"
|
|
WRMSG( HHC00317, "W", LCSS_DEVNUM );
|
|
return NULL;
|
|
}
|
|
|
|
/* Calculate totals */
|
|
if (fstat( cckd->fd[0], &st ) != 0)
|
|
{
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC00075, "E", "fstat()", strerror( errno ));
|
|
memset( &st, 0, sizeof( st ));
|
|
}
|
|
for (i=0; i <= cckd->sfn; i++)
|
|
{
|
|
if (!i) usize = st.st_size;
|
|
else usize += cckd->cdevhdr[i].cdh_size;
|
|
ufree += cckd->cdevhdr[i].free_total;
|
|
free_count += cckd->cdevhdr[i].free_num;
|
|
}
|
|
|
|
/* header */
|
|
|
|
// "%1d:%04X 32/64 size free nbr st reads writes l2reads hits switches"
|
|
WRMSG (HHC00333, "I", LCSS_DEVNUM);
|
|
|
|
if (cckd->readaheads || cckd->misses)
|
|
// "%1d:%04X readaheads misses"
|
|
WRMSG (HHC00334, "I", LCSS_DEVNUM);
|
|
|
|
// "%1d:%04X ------------------------------------------------------------------------"
|
|
WRMSG (HHC00335, "I", LCSS_DEVNUM);
|
|
|
|
/* total statistics */
|
|
|
|
// "%1d:%04X [*] %s %11.11"PRId64" %3.3"PRId64"%% %4.4"PRId64" %7.7d %7.7d %7.7d %7.7d %7.7d"
|
|
WRMSG( HHC00336, "I", LCSS_DEVNUM, dev->cckd64 ? "64" : "32", usize
|
|
|
|
, usize ? (ufree * 100) / usize : 999
|
|
, free_count
|
|
, cckd->totreads
|
|
, cckd->totwrites
|
|
, cckd->totl2reads
|
|
, cckd->cachehits
|
|
, cckd->switches
|
|
);
|
|
|
|
if (cckd->readaheads || cckd->misses)
|
|
// "%1d:%04X %7.7d %7.7d"
|
|
WRMSG (HHC00337, "I", LCSS_DEVNUM,
|
|
cckd->readaheads, cckd->misses);
|
|
|
|
/* base file statistics */
|
|
|
|
// "%1d:%04X %s"
|
|
WRMSG (HHC00338, "I", LCSS_DEVNUM, dev->filename);
|
|
|
|
// "%1d:%04X [0] %s %11.11"PRId64" %3.3"PRId64"%% %4.4"PRId64" %s %7.7d %7.7d %7.7d"
|
|
WRMSG( HHC00339, "I", LCSS_DEVNUM, dev->cckd64 ? "64" : "32", st.st_size
|
|
|
|
, st.st_size ?
|
|
((S64)(cckd->cdevhdr[0].free_total * 100) / st.st_size) : 999
|
|
, cckd->cdevhdr[0].free_num
|
|
, ost[cckd->open[0]]
|
|
, cckd->reads[0]
|
|
, cckd->writes[0]
|
|
, cckd->L2_reads[0]
|
|
);
|
|
|
|
if (dev->dasdsfn != NULL && CCKD_MAX_SF > 0)
|
|
// "%1d:%04X %s"
|
|
WRMSG (HHC00340, "I", LCSS_DEVNUM, cckd_sf_name(dev, -1));
|
|
|
|
/* shadow file statistics */
|
|
for (i = 1; i <= cckd->sfn; i++)
|
|
{
|
|
// "%1d:%04X [%d] %s %11.11"PRId64" %3.3"PRId64"%% %4.4"PRId64" %s %7.7d %7.7d %7.7d"
|
|
WRMSG( HHC00341, "I", LCSS_DEVNUM, i, dev->cckd64 ? "64" : "32", (S64)cckd->cdevhdr[i].cdh_size
|
|
|
|
, cckd->cdevhdr[i].cdh_size ?
|
|
((S64)(cckd->cdevhdr[i].free_total * 100) / cckd->cdevhdr[i].cdh_size) : 999
|
|
, cckd->cdevhdr[i].free_num
|
|
, ost[cckd->open[i]]
|
|
, cckd->reads[i]
|
|
, cckd->writes[i]
|
|
, cckd->L2_reads[i]
|
|
);
|
|
}
|
|
|
|
return NULL;
|
|
} /* end function cckd64_sf_stats */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Report Garbage Collection state for a given CCKD device */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_gc_rpt_state( DEVBLK* dev )
|
|
{
|
|
CCKD64_EXT* cckd;
|
|
int gc;
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_gc_rpt_state( dev );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Retrieve and report garbage collector state, but ONLY if
|
|
the image is over 100MB in size. This prevents "scaring"
|
|
the user about very fragmented files when the file is
|
|
too small to be much of a concern, as is usually the case
|
|
with e.g. shadow files.
|
|
*/
|
|
if (cckd->cdevhdr->cdh_size < (100 * _1M))
|
|
return;
|
|
|
|
gc = cckd_gc_state( dev );
|
|
|
|
switch (gc)
|
|
{
|
|
case 0: // critical!
|
|
case 1: // severe
|
|
|
|
// "%1d:%04X CCKD%s image %s is very fragmented."
|
|
WRMSG( HHC00387, "W", LCSS_DEVNUM, "64",
|
|
TRIMLOC( cckd_sf_name( dev, cckd->sfn )));
|
|
break;
|
|
|
|
case 2: // moderate
|
|
|
|
// "%1d:%04X CCKD%s image %s is moderately fragmented."
|
|
WRMSG( HHC00388, "I", LCSS_DEVNUM, "64",
|
|
TRIMLOC( cckd_sf_name( dev, cckd->sfn )));
|
|
break;
|
|
|
|
case 3: // light
|
|
|
|
// "%1d:%04X CCKD%s image %s is slightly fragmented."
|
|
WRMSG( HHC00389, "I", LCSS_DEVNUM, "64",
|
|
TRIMLOC( cckd_sf_name( dev, cckd->sfn )));
|
|
break;
|
|
|
|
default: // less than light
|
|
|
|
break; // (don't bother reporting it)
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return Garbage Collection State for a given CCKD device */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_gc_state( DEVBLK* dev )
|
|
{
|
|
CCKD64_EXT* cckd; /* -> cckd extension */
|
|
S64 size, fsiz; /* File size, free size */
|
|
int gc; /* Garbage collection state */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_gc_state( dev );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
/* Determine garbage state */
|
|
size = (S64) cckd->cdevhdr[ cckd->sfn ].cdh_size;
|
|
fsiz = (S64) cckd->cdevhdr[ cckd->sfn ].free_total;
|
|
|
|
if (fsiz >= (size = size/2)) gc = 0; // critical 50% - 100%
|
|
else if (fsiz >= (size = size/2)) gc = 1; // severe 25% - 50%
|
|
else if (fsiz >= (size = size/2)) gc = 2; // moderate 12.5% - 25%
|
|
else if (fsiz >= (size = size/2)) gc = 3; // light 6.3% - 12.5%
|
|
else gc = 4; // none 0% - 6.3%
|
|
|
|
/* Adjust the state based on the number of free spaces */
|
|
if (cckd->cdevhdr[ cckd->sfn ].free_num > 800 && gc > 0) gc--;
|
|
if (cckd->cdevhdr[ cckd->sfn ].free_num > 1800 && gc > 0) gc--;
|
|
if (cckd->cdevhdr[ cckd->sfn ].free_num > 3000) gc = 0;
|
|
|
|
return gc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Perform garbage collection for a given CCKD64 device */
|
|
/*-------------------------------------------------------------------*/
|
|
void cckd64_gcol_dev( DEVBLK* dev, struct timeval* tv_now )
|
|
{
|
|
int rc; /* Return code */
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
U64 size; /* Percolate size */
|
|
int gc; /* Garbage collection state */
|
|
|
|
if (!dev->cckd64)
|
|
{
|
|
cckd_gcol_dev( dev, tv_now );
|
|
return;
|
|
}
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
obtain_lock (&cckd->cckdiolock);
|
|
{
|
|
/* Bypass if merging or stopping */
|
|
if (cckd->merging || cckd->stopping)
|
|
{
|
|
release_lock (&cckd->cckdiolock);
|
|
return;
|
|
}
|
|
|
|
/* Bypass if not opened read-write */
|
|
if (cckd->open[cckd->sfn] != CCKD_OPEN_RW)
|
|
{
|
|
release_lock (&cckd->cckdiolock);
|
|
return;
|
|
}
|
|
|
|
/* Free newbuf if it hasn't been used */
|
|
if (!cckd->cckdioact && !cckd->bufused && cckd->newbuf)
|
|
cckd->newbuf = cckd_free (dev, "newbuf", cckd->newbuf);
|
|
|
|
cckd->bufused = 0;
|
|
|
|
/* If OPENED bit not on then flush if updated */
|
|
if (!(cckd->cdevhdr[cckd->sfn].cdh_opts & CCKD_OPT_OPENED))
|
|
{
|
|
if (cckd->updated) cckd64_flush_cache (dev);
|
|
release_lock (&cckd->cckdiolock);
|
|
return;
|
|
}
|
|
|
|
/* Determine garbage state */
|
|
gc = cckd64_gc_state( dev );
|
|
|
|
/* Set the size */
|
|
if (cckdblk.gcparm > 0) size = gctab[gc] << cckdblk.gcparm;
|
|
else if (cckdblk.gcparm < 0) size = gctab[gc] >> abs(cckdblk.gcparm);
|
|
else size = gctab[gc];
|
|
|
|
if (size > cckd->cdevhdr[cckd->sfn].cdh_used >> SHIFT_1K)
|
|
size = cckd->cdevhdr[cckd->sfn].cdh_used >> SHIFT_1K;
|
|
if (size < 64)
|
|
size = 64;
|
|
}
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
/* Call the garbage collector */
|
|
cckd64_gc_percolate( dev, size );
|
|
|
|
/* Schedule any updated tracks to be written */
|
|
obtain_lock (&cckd->cckdiolock);
|
|
{
|
|
cckd64_flush_cache (dev);
|
|
|
|
while (cckdblk.fsync && cckd->wrpending)
|
|
{
|
|
cckd->cckdwaiters++;
|
|
{
|
|
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
|
|
}
|
|
cckd->cckdwaiters--;
|
|
}
|
|
}
|
|
release_lock (&cckd->cckdiolock);
|
|
|
|
/* Sync the file */
|
|
if (cckdblk.fsync && cckd->lastsync + 10 <= tv_now->tv_sec)
|
|
{
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
rc = fdatasync (cckd->fd[cckd->sfn]);
|
|
cckd->lastsync = tv_now->tv_sec;
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
}
|
|
|
|
/* Flush the free space */
|
|
if (cckd->cdevhdr[cckd->sfn].free_num)
|
|
{
|
|
obtain_lock( &cckd->filelock );
|
|
{
|
|
cckd64_flush_space (dev);
|
|
}
|
|
release_lock( &cckd->filelock );
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Garbage Collection error handler helper functions/macros */
|
|
/*-------------------------------------------------------------------*/
|
|
static int cckd64_gc_perc_error( DEVBLK* dev, CCKD64_EXT* cckd, U64 moved,
|
|
const char* file, int line )
|
|
{
|
|
cckd_trace( file, line, dev, "gcperc exiting due to error, moved %"PRIu64, moved );
|
|
release_lock( &cckd->filelock );
|
|
return (int) moved;
|
|
}
|
|
#define GC64_PERC_ERROR() cckd64_gc_perc_error( dev, cckd, moved, __FILE__, __LINE__ )
|
|
|
|
static int cckd64_gc_perc_space_error( DEVBLK* dev, CCKD64_EXT* cckd, U64 upos, S64 i, BYTE* buf, U64 moved,
|
|
const char* file, int line )
|
|
{
|
|
// "%1d:%04X CCKD file[%d] %s: %s(%d): offset 0x%16.16"PRIx64" unknown space %2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
|
|
WRMSG( HHC00342, "E", LCSS_DEVNUM,
|
|
cckd->sfn, cckd_sf_name( dev, cckd->sfn ), TRIMLOC( file ), line,
|
|
upos + i, buf[i], buf[i+1],buf[i+2], buf[i+3], buf[i+4]);
|
|
|
|
cckd->cdevhdr[ cckd->sfn ].cdh_opts |= CCKD_OPT_SPERRS;
|
|
cckd_print_itrace();
|
|
return cckd64_gc_perc_error( dev, cckd, moved, file, line );
|
|
}
|
|
#define GC64_PERC_SPACE_ERROR() cckd64_gc_perc_space_error( dev, cckd, upos, i, buf, moved, __FILE__, __LINE__ )
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Garbage Collection -- Percolate algorithm */
|
|
/*-------------------------------------------------------------------*/
|
|
int cckd64_gc_percolate( DEVBLK* dev, U64 size )
|
|
{
|
|
CCKD64_EXT* cckd; /* -> cckd extension */
|
|
bool didmsg = false; /* HHC00384 issued */
|
|
int rc; /* Return code */
|
|
U64 moved = 0; /* Space moved */
|
|
S64 after = 0, a; /* New space after old */
|
|
S64 sfx; /* File index */
|
|
S64 i, j, k; /* Indexes */
|
|
int flags; /* Write trkimg flags */
|
|
U64 fpos, upos; /* File offsets */
|
|
U64 flen, ulen, len; /* Lengths */
|
|
int trk; /* Track number */
|
|
int L1idx, l2x; /* Table Indexes */
|
|
CCKD64_L2ENT l2; /* Copied level 2 entry */
|
|
BYTE buf[256*1024]; /* Buffer */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_gc_percolate( dev, size );
|
|
|
|
cckd = dev->cckd_ext;
|
|
size = size << SHIFT_1K;
|
|
|
|
if (cckd->cdevhdr[ cckd->sfn ].cdh_opts & CCKD_OPT_SPERRS)
|
|
{
|
|
// "Skipping garbage collection for CCKD%s file[%d] %1d:%04X %s due to space errors"
|
|
if (cckdblk.gcmsgs)
|
|
WRMSG( HHC00385, "I", "64", cckd->sfn, LCSS_DEVNUM, cckd_sf_name( dev, cckd->sfn ));
|
|
return (int) moved;
|
|
}
|
|
|
|
/* Debug */
|
|
OBTAIN_TRACE_LOCK();
|
|
if (cckdblk.itracen)
|
|
{
|
|
RELEASE_TRACE_LOCK();
|
|
|
|
CCKD_TRACE( "gcperc size %d 1st 0x%"PRIx64" nbr %"PRId64" largest %"PRIu64,
|
|
size, cckd->cdevhdr[cckd->sfn].free_off,
|
|
cckd->cdevhdr[cckd->sfn].free_num,
|
|
cckd->cdevhdr[cckd->sfn].free_largest);
|
|
|
|
fpos = cckd->cdevhdr[cckd->sfn].free_off;
|
|
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
CCKD_TRACE( "gcperc free[%4d]:%16.16"PRIx64" end %16.16"PRIx64" len %10"PRId64"%cpend %d",
|
|
i, fpos, (fpos+cckd->ifb[i].ifb_len), cckd->ifb[i].ifb_len,
|
|
fpos+cckd->ifb[i].ifb_len == cckd->ifb[i].ifb_offnxt ?
|
|
'*' : ' ', cckd->ifb[i].ifb_pending);
|
|
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
}
|
|
else
|
|
RELEASE_TRACE_LOCK();
|
|
|
|
if (!cckd->L2ok)
|
|
cckd64_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 (int) moved;
|
|
}
|
|
|
|
/* Make sure the free space chain is built */
|
|
if (!cckd->ifb) cckd64_read_fsp (dev);
|
|
|
|
/* Find a space to start with */
|
|
k = -1;
|
|
upos = ulen = flen = 0;
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
|
|
/* First non-pending free space */
|
|
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (!cckd->ifb[i].ifb_pending)
|
|
{
|
|
flen += cckd->ifb[i].ifb_len;
|
|
break;
|
|
}
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
|
|
/* Continue to largest if non-zero `after' */
|
|
for ( ; i >= 0 && after; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
k = i;
|
|
if (!cckd->ifb[i].ifb_pending) flen += cckd->ifb[i].ifb_len;
|
|
if (cckd->ifb[i].ifb_len == cckd->cdevhdr[sfx].free_largest)
|
|
break;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
|
|
/* Skip following free spaces */
|
|
for ( ; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
|
|
{
|
|
if (!cckd->ifb[i].ifb_pending) flen += cckd->ifb[i].ifb_len;
|
|
if (fpos + cckd->ifb[i].ifb_len != cckd->ifb[i].ifb_offnxt) break;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
}
|
|
|
|
/* Space preceding largest if largest is at the end */
|
|
if (i < 0 && k >= 0)
|
|
{
|
|
if (!cckd->ifb[k].ifb_pending) flen -= cckd->ifb[i].ifb_len;
|
|
for (i = cckd->ifb[k].ifb_idxprv; i >= 0; i = cckd->ifb[i].ifb_idxprv)
|
|
{
|
|
fpos = cckd->ifb[i].ifb_idxprv >= 0
|
|
? cckd->ifb[cckd->ifb[i].ifb_idxprv].ifb_offnxt
|
|
: cckd->cdevhdr[sfx].free_off;
|
|
if (fpos + cckd->ifb[i].ifb_len < cckd->ifb[i].ifb_offnxt) break;
|
|
if (!cckd->ifb[i].ifb_pending) flen -= cckd->ifb[i].ifb_len;
|
|
}
|
|
}
|
|
|
|
/* Calculate the offset/length of the used space.
|
|
* If only imbedded free space is left, then start
|
|
* with the first used space that is not an l2 table.
|
|
*/
|
|
if (i >= 0)
|
|
{
|
|
upos = fpos + cckd->ifb[i].ifb_len;
|
|
ulen = (cckd->ifb[i].ifb_offnxt ? cckd->ifb[i].ifb_offnxt : cckd->cdevhdr[sfx].cdh_size) - upos;
|
|
}
|
|
else if (!cckd->cdevhdr[sfx].free_num && cckd->cdevhdr[sfx].free_imbed)
|
|
{
|
|
upos = (CCKD64_L1TAB_POS + cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE);
|
|
while (1)
|
|
{
|
|
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
if (cckd->L1tab[sfx][i] == upos)
|
|
break;
|
|
if (i >= cckd->cdevhdr[sfx].num_L1tab)
|
|
break;
|
|
upos += CCKD64_L2TAB_SIZE;
|
|
}
|
|
ulen = cckd->cdevhdr[sfx].cdh_size - upos;
|
|
}
|
|
|
|
/* Return if no applicable used space */
|
|
if (ulen == 0)
|
|
{
|
|
CCKD_TRACE( "gcperc no applicable space, moved %"PRIu64, moved);
|
|
release_lock( &cckd->filelock );
|
|
return (int) moved;
|
|
}
|
|
|
|
/* Reduce ulen size to minimize `after' relocations */
|
|
if (ulen > flen + 65536) ulen = flen + 65536;
|
|
if (ulen > sizeof(buf)) ulen = sizeof(buf);
|
|
|
|
CCKD_TRACE( "gcperc selected space 0x%16.16"PRIx64" len %"PRId64, upos, ulen);
|
|
|
|
if (cckd64_read (dev, (int) sfx, upos, buf, (unsigned int) ulen) < 0)
|
|
return GC64_PERC_ERROR();
|
|
|
|
/* Process each space in the buffer */
|
|
flags = cckd->cdevhdr[sfx].free_num < 100 ? CCKD_SIZE_EXACT : CCKD_SIZE_ANY;
|
|
for (i = a = 0; (U64)i + CKD_TRKHDR_SIZE <= ulen; i += len)
|
|
{
|
|
/* Check for level 2 table */
|
|
for (j = 0; j < cckd->cdevhdr[sfx].num_L1tab; j++)
|
|
if (cckd->L1tab[sfx][j] == (upos + i)) break;
|
|
|
|
if (!didmsg)
|
|
{
|
|
// "Collecting garbage for CCKD%s file[%d] %1d:%04X %s..."
|
|
if (cckdblk.gcmsgs)
|
|
WRMSG( HHC00384, "I", "64", cckd->sfn, LCSS_DEVNUM, cckd_sf_name( dev, cckd->sfn ));
|
|
didmsg = true;
|
|
}
|
|
|
|
if (j < cckd->cdevhdr[sfx].num_L1tab)
|
|
{
|
|
/* Moving a level 2 table */
|
|
len = CCKD64_L2TAB_SIZE;
|
|
if (i + len > ulen) break;
|
|
CCKD_TRACE( "gcperc move l2tab[%"PRId64"] at pos 0x%16.16"PRIx64" len %"PRId64,
|
|
j, upos + i, len);
|
|
|
|
/* Make the level 2 table active */
|
|
if (cckd64_read_l2 (dev, (int)sfx, (int)j) < 0)
|
|
return GC64_PERC_ERROR();
|
|
|
|
/* Write the level 2 table */
|
|
if (cckd64_write_l2 (dev) < 0)
|
|
return GC64_PERC_ERROR();
|
|
}
|
|
else
|
|
{
|
|
/* Moving a track image */
|
|
if ((trk = cckd64_cchh (dev, buf + i, -1)) < 0)
|
|
return GC64_PERC_SPACE_ERROR();
|
|
|
|
L1idx = trk >> 8;
|
|
l2x = trk & 0xff;
|
|
|
|
/* Read the lookup entry for the track */
|
|
if (cckd64_read_l2ent (dev, &l2, trk) < 0)
|
|
return GC64_PERC_ERROR();
|
|
if (l2.L2_trkoff != (upos + i))
|
|
return GC64_PERC_SPACE_ERROR();
|
|
len = l2.L2_size;
|
|
if ((U64)i + l2.L2_len > ulen) break;
|
|
|
|
CCKD_TRACE( "gcperc move trk %d at pos 0x%16.16"PRIx64" len %hu",
|
|
trk, upos + i, l2.L2_len);
|
|
|
|
/* Relocate the track image somewhere else */
|
|
if ((rc = cckd64_write_trkimg (dev, buf + i, (int)l2.L2_len, trk, flags)) < 0)
|
|
return GC64_PERC_ERROR();
|
|
a += rc;
|
|
}
|
|
} /* for each space in the used space */
|
|
|
|
/* Set `after' to 1 if first time space was relocated after */
|
|
after += after ? a : (a > 0);
|
|
moved += i;
|
|
|
|
cckdblk.stats_gcolmoves++;
|
|
cckdblk.stats_gcolbytes += i;
|
|
|
|
release_lock( &cckd->filelock );
|
|
|
|
} /* while (moved < size) */
|
|
|
|
CCKD_TRACE( "gcperc moved %d 1st 0x%"PRIx64" nbr %"PRIu64, moved,
|
|
cckd->cdevhdr[cckd->sfn].free_off, cckd->cdevhdr[cckd->sfn].free_num);
|
|
// "Collected %u bytes of garbage for CCKD%s file[%d] %1d:%04X %s..."
|
|
if (cckdblk.gcmsgs)
|
|
WRMSG( HHC00386, "I", (unsigned int) moved, "64", cckd->sfn, LCSS_DEVNUM, cckd_sf_name( dev, cckd->sfn ));
|
|
return (int) moved;
|
|
} /* end function cckd64_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 cckd64_gc_l2(DEVBLK *dev, BYTE *buf)
|
|
{
|
|
#if 1 // (deprecate this function!)
|
|
|
|
UNREFERENCED( dev );
|
|
UNREFERENCED( buf );
|
|
|
|
return 0;
|
|
|
|
#else // (deprecate this function!)
|
|
|
|
CCKD64_EXT *cckd; /* -> cckd extension */
|
|
int sfx; /* Shadow file index */
|
|
int i, j; /* Work variables */
|
|
int trk; /* Track number */
|
|
int len; /* Track length */
|
|
U64 pos, fpos; /* File offsets */
|
|
|
|
if (!dev->cckd64)
|
|
return cckd_gc_l2( dev, buf );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
obtain_lock (&cckd->filelock);
|
|
sfx = cckd->sfn;
|
|
|
|
if (cckd->L2ok || cckd->cdevhdr[cckd->sfn].free_total == 0)
|
|
goto cckd_gc_l2_exit;
|
|
|
|
/* Find any level 2 table out of bounds */
|
|
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
if (cckd->L1tab[sfx][i] != CCKD64_NOSIZE && cckd->L1tab[sfx][i] != CCKD64_MAXSIZE
|
|
&& cckd->L2_bounds - CCKD64_L2TAB_SIZE < cckd->L1tab[sfx][i])
|
|
break;
|
|
|
|
/* Return OK if no l2 tables out of bounds */
|
|
if (i >= cckd->cdevhdr[sfx].num_L1tab)
|
|
goto cckd_gc_l2_exit_ok;
|
|
|
|
/* Relocate all track images within the bounds */
|
|
pos = CCKD64_L1TAB_POS + (cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE);
|
|
i = cckd->free_idx1st;
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
while (pos < cckd->L2_bounds)
|
|
{
|
|
if (i >= 0 && pos == fpos)
|
|
{
|
|
pos += cckd->ifb[i].ifb_len;
|
|
fpos = cckd->ifb[i].ifb_offnxt;
|
|
i = cckd->ifb[i].ifb_idxnxt;
|
|
j = 0;
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < cckd->cdevhdr[sfx].num_L1tab; j++)
|
|
if (pos == cckd->L1tab[sfx][j])
|
|
{
|
|
pos += CCKD64_L2TAB_SIZE;
|
|
break;
|
|
}
|
|
}
|
|
if (j >= cckd->cdevhdr[sfx].num_L1tab)
|
|
{
|
|
/* Found a track to relocate */
|
|
if (cckd64_read (dev, sfx, pos, buf, CKD_TRKHDR_SIZE) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
if ((trk = cckd64_cchh (dev, buf, -1)) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
CCKD_TRACE( "gc_l2 relocate trk[%d] offset 0x%"PRIx64, trk, pos);
|
|
if ((len = cckd64_read_trkimg (dev, buf, trk, NULL)) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
if (cckd64_write_trkimg (dev, buf, len, trk, CCKD_SIZE_EXACT) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
/* Start over */
|
|
pos = CCKD64_L1TAB_POS + (cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE);
|
|
i = cckd->free_idx1st;
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
}
|
|
}
|
|
|
|
do {
|
|
/* Find a level 2 table to relocate */
|
|
|
|
i = cckd->free_idx1st;
|
|
fpos = cckd->cdevhdr[sfx].free_off;
|
|
|
|
CCKD_TRACE( "gc_l2 first free[%d] pos 0x%"PRIx64" len %"PRId64" pending %d",
|
|
i, fpos, (U64)(i >= 0 ? cckd->ifb[i].ifb_len : -1),
|
|
(int)(i >= 0 ? cckd->ifb[i].ifb_pending : -1 ));
|
|
|
|
if (i < 0 || fpos >= cckd->L2_bounds || cckd->ifb[i].ifb_pending)
|
|
goto cckd_gc_l2_exit;
|
|
|
|
CCKD_TRACE( "gc_l2 bounds 0x%"PRIx64" sfx %d num_L1tab %d",
|
|
cckd->L2_bounds, sfx, cckd->cdevhdr[sfx].num_L1tab );
|
|
|
|
if ( cckd->ifb[i].ifb_len < CCKD64_L2TAB_SIZE
|
|
|| (cckd->ifb[i].ifb_len != CCKD64_L2TAB_SIZE
|
|
&& cckd->ifb[i].ifb_len < CCKD64_L2TAB_SIZE + CCKD64_FREEBLK_SIZE)
|
|
)
|
|
{
|
|
for (i=0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
{
|
|
if (fpos + cckd->ifb[i].ifb_len == cckd->L1tab[sfx][i])
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
|
|
{
|
|
if (cckd->L2_bounds - CCKD64_L2TAB_SIZE < cckd->L1tab[sfx][i]
|
|
&& cckd->L1tab[sfx][i] != CCKD64_MAXSIZE)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < cckd->cdevhdr[sfx].num_L1tab)
|
|
{
|
|
CCKD_TRACE( "gc_l2 relocate l2[%d] pos 0x%"PRIx64,
|
|
i, cckd->L1tab[sfx][i] );
|
|
|
|
if (cckd64_read_l2( dev, sfx, i ) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
|
|
if (cckd64_write_l2( dev ) < 0)
|
|
goto cckd_gc_l2_exit;
|
|
}
|
|
}
|
|
while (i < cckd->cdevhdr[sfx].num_L1tab);
|
|
|
|
cckd_gc_l2_exit:
|
|
release_lock (&cckd->filelock);
|
|
return 0;
|
|
|
|
cckd_gc_l2_exit_ok:
|
|
CCKD_TRACE( "gc_l2 ok%s", "" );
|
|
cckd->L2ok = 1;
|
|
goto cckd_gc_l2_exit;
|
|
|
|
#endif // (deprecate this function!)
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Uncompress a track image */
|
|
/*-------------------------------------------------------------------*/
|
|
BYTE *cckd64_uncompress (DEVBLK *dev, BYTE *from, int len, int maxlen,
|
|
int trk)
|
|
{
|
|
CCKD64_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( "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 = cckd64_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 = cckd64_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 = cckd64_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 = cckd64_validate (dev, to, trk, newlen);
|
|
if (newlen > 0)
|
|
{
|
|
cckd->newbuf = from;
|
|
cckd->bufused = 1;
|
|
return to;
|
|
}
|
|
|
|
// "%1d:%04X CCKD file[%d] %s: uncompress error trk %d: %2.2x%2.2x%2.2x%2.2x%2.2x"
|
|
WRMSG (HHC00343, "E",
|
|
LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn), trk,
|
|
from[0], from[1], from[2], from[3], from[4]);
|
|
if (comp & ~cckdblk.comps)
|
|
WRMSG (HHC00344, "E",
|
|
LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn), compress[comp]);
|
|
return NULL;
|
|
}
|