/* CCKDDASD.C (c) Copyright Roger Bowler, 1999-2012 */ /* (c) Copyright Greg Smith, 2002-2012 */ /* */ /* ESA/390 Compressed CKD Direct Access Storage Device Handler */ /* */ /* Released under "The Q Public License Version 1" */ /* (http://www.hercules-390.org/herclic.html) as modifications to */ /* Hercules. */ /*-------------------------------------------------------------------*/ /* This module contains device functions for compressed emulated */ /* count-key-data direct access storage devices. */ /*-------------------------------------------------------------------*/ #include "hstdinc.h" #define _CCKDDASD_C_ #define _HDASD_DLL_ #include "hercules.h" #include "devtype.h" #include "opcode.h" /*-------------------------------------------------------------------*/ /* Internal functions */ /*-------------------------------------------------------------------*/ int cckddasd_init(int argc, BYTE *argv[]); int cckddasd_term(); int cckddasd_init_handler( DEVBLK *dev, int argc, char *argv[] ); int cckddasd_close_device(DEVBLK *dev); void cckddasd_start(DEVBLK *dev); void cckddasd_end(DEVBLK *dev); int cckd_open (DEVBLK *dev, int sfx, int flags, mode_t mode); int cckd_close (DEVBLK *dev, int sfx); int cckd_read (DEVBLK *dev, int sfx, off_t off, void *buf, unsigned int len); int cckd_write (DEVBLK *dev, int sfx, off_t off, void *buf, unsigned int len); int cckd_ftruncate(DEVBLK *dev, int sfx, off_t off); void *cckd_malloc(DEVBLK *dev, char *id, size_t size); void *cckd_calloc(DEVBLK *dev, char *id, size_t n, size_t size); void *cckd_free(DEVBLK *dev, char *id,void *p); int cckd_read_track(DEVBLK *dev, int trk, BYTE *unitstat); int cckd_update_track(DEVBLK *dev, int trk, int off, BYTE *buf, int len, BYTE *unitstat); int cckd_used(DEVBLK *dev); int cfba_read_block(DEVBLK *dev, int blkgrp, BYTE *unitstat); int cfba_write_block(DEVBLK *dev, int blkgrp, int off, BYTE *buf, int wrlen, BYTE *unitstat); int cfba_used(DEVBLK *dev); int cckd_read_trk(DEVBLK *dev, int trk, int ra, BYTE *unitstat); void cckd_readahead(DEVBLK *dev, int trk); int cckd_readahead_scan(int *answer, int ix, int i, void *data); void* cckd_ra(void* arg); void cckd_flush_cache(DEVBLK *dev); int cckd_flush_cache_scan(int *answer, int ix, int i, void *data); void cckd_flush_cache_all(); void cckd_purge_cache(DEVBLK *dev); int cckd_purge_cache_scan(int *answer, int ix, int i, void *data); void* cckd_writer(void *arg); int cckd_writer_scan(int *o, int ix, int i, void *data); off_t cckd_get_space(DEVBLK *dev, int *size, int flags); void cckd_rel_space(DEVBLK *dev, off_t pos, int len, int size); void cckd_flush_space(DEVBLK *dev); int cckd_read_chdr(DEVBLK *dev); int cckd_write_chdr(DEVBLK *dev); int cckd_read_l1(DEVBLK *dev); int cckd_write_l1(DEVBLK *dev); int cckd_write_l1ent(DEVBLK *dev, int l1x); int cckd_read_init(DEVBLK *dev); int cckd_read_fsp(DEVBLK *dev); int cckd_write_fsp(DEVBLK *dev); int cckd_read_l2(DEVBLK *dev, int sfx, int l1x); void cckd_purge_l2(DEVBLK *dev); int cckd_purge_l2_scan(int *answer, int ix, int i, void *data); int cckd_steal_l2(); int cckd_steal_l2_scan(int *answer, int ix, int i, void *data); int cckd_write_l2(DEVBLK *dev); int cckd_read_l2ent(DEVBLK *dev, CCKD_L2ENT *l2, int trk); int cckd_write_l2ent(DEVBLK *dev, CCKD_L2ENT *l2, int trk); int cckd_read_trkimg(DEVBLK *dev, BYTE *buf, int trk, BYTE *unitstat); int cckd_write_trkimg(DEVBLK *dev, BYTE *buf, int len, int trk, int flags); int cckd_harden(DEVBLK *dev); int cckd_trklen(DEVBLK *dev, BYTE *buf); int cckd_null_trk(DEVBLK *dev, BYTE *buf, int trk, int nullfmt); int cckd_check_null_trk (DEVBLK *dev, BYTE *buf, int trk, int len); int cckd_cchh(DEVBLK *dev, BYTE *buf, int trk); int cckd_validate(DEVBLK *dev, BYTE *buf, int trk, int len); char *cckd_sf_name(DEVBLK *dev, int sfx); int cckd_sf_init(DEVBLK *dev); int cckd_sf_new(DEVBLK *dev); DLL_EXPORT void *cckd_sf_add(void *data); DLL_EXPORT void *cckd_sf_remove(void *data); DLL_EXPORT void *cckd_sf_comp(void *data); DLL_EXPORT void *cckd_sf_chk(void *data); DLL_EXPORT void *cckd_sf_stats(void *data); #ifdef OPTION_SYNCIO int cckd_disable_syncio(DEVBLK *dev); #endif // OPTION_SYNCIO void cckd_lock_devchain(int flag); void cckd_unlock_devchain(); void* cckd_gcol(void* arg); int cckd_gc_percolate(DEVBLK *dev, unsigned int size); int cckd_gc_l2(DEVBLK *dev, BYTE *buf); DEVBLK *cckd_find_device_by_devnum (U16 devnum); BYTE *cckd_uncompress(DEVBLK *dev, BYTE *from, int len, int maxlen, int trk); int cckd_uncompress_zlib(DEVBLK *dev, BYTE *to, BYTE *from, int len, int maxlen); int cckd_uncompress_bzip2(DEVBLK *dev, BYTE *to, BYTE *from, int len, int maxlen); int cckd_compress(DEVBLK *dev, BYTE **to, BYTE *from, int len, int comp, int parm); int cckd_compress_none(DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm); int cckd_compress_zlib(DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm); int cckd_compress_bzip2(DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm); void cckd_command_help(); void cckd_command_opts(); void cckd_command_stats(); void cckd_command_debug(); int cckd_command(char *op, int cmd); DLL_EXPORT void cckd_print_itrace(); void cckd_trace(DEVBLK *dev, char *msg, ...); /*-------------------------------------------------------------------*/ /* Definitions for sense data format codes and message codes */ /*-------------------------------------------------------------------*/ #define FORMAT_0 0 /* Program or System Checks */ #define FORMAT_1 1 /* Device Equipment Checks */ #define FORMAT_2 2 /* 3990 Equipment Checks */ #define FORMAT_3 3 /* 3990 Control Checks */ #define FORMAT_4 4 /* Data Checks */ #define FORMAT_5 5 /* Data Check + Displacement */ #define FORMAT_6 6 /* Usage Stats/Overrun Errors*/ #define FORMAT_7 7 /* Device Control Checks */ #define FORMAT_8 8 /* Device Equipment Checks */ #define FORMAT_9 9 /* Device Rd/Wrt/Seek Checks */ #define FORMAT_F 15 /* Cache Storage Checks */ #define MESSAGE_0 0 /* Message 0 */ #define MESSAGE_1 1 /* Message 1 */ #define MESSAGE_2 2 /* Message 2 */ #define MESSAGE_3 3 /* Message 3 */ #define MESSAGE_4 4 /* Message 4 */ #define MESSAGE_5 5 /* Message 5 */ #define MESSAGE_6 6 /* Message 6 */ #define MESSAGE_7 7 /* Message 7 */ #define MESSAGE_8 8 /* Message 8 */ #define MESSAGE_9 9 /* Message 9 */ #define MESSAGE_A 10 /* Message A */ #define MESSAGE_B 11 /* Message B */ #define MESSAGE_C 12 /* Message C */ #define MESSAGE_D 13 /* Message D */ #define MESSAGE_E 14 /* Message E */ #define MESSAGE_F 15 /* Message F */ /*-------------------------------------------------------------------*/ /* Data areas */ /*-------------------------------------------------------------------*/ static CCKD_L2ENT empty_l2[CKDDASD_NULLTRK_FMTMAX+1][256]; static BYTE eighthexFF[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; DEVHND cckddasd_device_hndinfo; DEVHND cfbadasd_device_hndinfo; DLL_EXPORT CCKDBLK cckdblk; /* cckd global area */ /*-------------------------------------------------------------------*/ /* CCKD global initialization */ /*-------------------------------------------------------------------*/ int cckddasd_init (int argc, BYTE *argv[]) { int i, j; /* Loop indexes */ UNREFERENCED(argc); UNREFERENCED(argv); if (memcmp (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id)) == 0) return 0; /* Clear the cckdblk */ memset(&cckdblk, 0, sizeof(CCKDBLK)); /* Initialize locks and conditions */ memcpy (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id)); initialize_lock (&cckdblk.gclock); initialize_lock (&cckdblk.ralock); initialize_lock (&cckdblk.wrlock); initialize_lock (&cckdblk.devlock); initialize_condition (&cckdblk.gccond); initialize_condition (&cckdblk.racond); initialize_condition (&cckdblk.wrcond); initialize_condition (&cckdblk.devcond); initialize_condition (&cckdblk.termcond); /* Initialize some variables */ cckdblk.wrprio = 16; cckdblk.ranbr = CCKD_DEFAULT_RA_SIZE; cckdblk.ramax = CCKD_DEFAULT_RA; cckdblk.wrmax = CCKD_DEFAULT_WRITER; cckdblk.gcmax = CCKD_DEFAULT_GCOL; cckdblk.gcwait = CCKD_DEFAULT_GCOLWAIT; cckdblk.gcparm = CCKD_DEFAULT_GCOLPARM; cckdblk.readaheads = CCKD_DEFAULT_READAHEADS; cckdblk.freepend = CCKD_DEFAULT_FREEPEND; #ifdef HAVE_LIBZ cckdblk.comps |= CCKD_COMPRESS_ZLIB; #endif #ifdef CCKD_BZIP2 cckdblk.comps |= CCKD_COMPRESS_BZIP2; #endif cckdblk.comp = 0xff; cckdblk.compparm = -1; /* Initialize the readahead queue */ cckdblk.ra1st = cckdblk.ralast = -1; cckdblk.rafree = 0; for (i = 0; i < cckdblk.ranbr; i++) cckdblk.ra[i].next = i + 1; cckdblk.ra[cckdblk.ranbr - 1].next = -1; /* Clear the empty L2 tables */ for (i = 0; i <= CKDDASD_NULLTRK_FMTMAX; i++) for (j = 0; j < 256; j++) { empty_l2[i][j].pos = 0; empty_l2[i][j].len = empty_l2[i][j].size = i; } return 0; } /* end function cckddasd_init */ /*-------------------------------------------------------------------*/ /* CCKD dasd global termination */ /*-------------------------------------------------------------------*/ int cckddasd_term () { /* Terminate the readahead threads */ obtain_lock (&cckdblk.ralock); cckdblk.ramax = 0; if (cckdblk.ras) { broadcast_condition (&cckdblk.racond); wait_condition (&cckdblk.termcond, &cckdblk.ralock); } release_lock (&cckdblk.ralock); destroy_lock (&cckdblk.ralock); destroy_condition (&cckdblk.racond); /* Terminate the garbage collection threads */ obtain_lock (&cckdblk.gclock); cckdblk.gcmax = 0; if (cckdblk.gcs) { broadcast_condition (&cckdblk.gccond); wait_condition (&cckdblk.termcond, &cckdblk.gclock); } release_lock (&cckdblk.gclock); destroy_lock (&cckdblk.gclock); destroy_condition (&cckdblk.gccond); /* Terminate the writer threads */ obtain_lock (&cckdblk.wrlock); cckdblk.wrmax = 0; if (cckdblk.wrs) { broadcast_condition (&cckdblk.wrcond); wait_condition (&cckdblk.termcond, &cckdblk.wrlock); } release_lock (&cckdblk.wrlock); destroy_lock (&cckdblk.wrlock); destroy_condition (&cckdblk.wrcond); destroy_lock (&cckdblk.devlock); destroy_condition (&cckdblk.devcond); destroy_condition (&cckdblk.termcond); memset(&cckdblk, 0, sizeof(CCKDBLK)); return 0; } /* end function cckddasd_term */ /*-------------------------------------------------------------------*/ /* CKD dasd initialization */ /*-------------------------------------------------------------------*/ int cckddasd_init_handler ( DEVBLK *dev, int argc, char *argv[] ) { CCKDDASD_EXT *cckd; /* -> cckd extension */ DEVBLK *dev2; /* -> device in cckd queue */ int i; /* Counter */ int fdflags; /* File flags */ UNREFERENCED(argc); UNREFERENCED(argv); /* Initialize the global cckd block if necessary */ if (memcmp (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id))) cckddasd_init (0, NULL); /* Obtain area for cckd extension */ dev->cckd_ext = cckd = cckd_calloc (dev, "ext", 1, sizeof(CCKDDASD_EXT)); if (cckd == NULL) return -1; /* Initialize locks and conditions */ initialize_lock (&cckd->cckdiolock); initialize_lock (&cckd->filelock); initialize_condition (&cckd->cckdiocond); /* Initialize some variables */ obtain_lock (&cckd->filelock); cckd->l1x = cckd->sfx = cckd->l2active = -1; dev->cache = cckd->free1st = -1; cckd->fd[0] = dev->fd; fdflags = get_file_accmode_flags( dev->fd ); cckd->open[0] = (fdflags & O_RDWR) ? CCKD_OPEN_RW : CCKD_OPEN_RO; for (i = 1; i <= CCKD_MAX_SF; i++) { cckd->fd[i] = -1; cckd->open[i] = CCKD_OPEN_NONE; } cckd->maxsize = sizeof(off_t) > 4 ? 0xffffffffll : 0x7fffffffll; /* call the chkdsk function */ if (cckd_chkdsk (dev, 0) < 0) return -1; /* Perform initial read */ if (cckd_read_init (dev) < 0) return -1; if (cckd->fbadasd) dev->ckdtrksz = CFBA_BLOCK_SIZE; /* open the shadow files */ if (cckd_sf_init (dev) < 0) { WRMSG (HHC00300, "E", SSID_TO_LCSS(dev->ssid), dev->devnum); return -1; } /* Update the device handler routines */ if (cckd->ckddasd) dev->hnd = &cckddasd_device_hndinfo; else dev->hnd = &cfbadasd_device_hndinfo; release_lock (&cckd->filelock); /* Insert the device into the cckd device queue */ cckd_lock_devchain(1); for (cckd = NULL, dev2 = cckdblk.dev1st; dev2; dev2 = cckd->devnext) cckd = dev2->cckd_ext; if (cckd) cckd->devnext = dev; else cckdblk.dev1st = dev; cckd_unlock_devchain(); cckdblk.batch = dev->batch; if (cckdblk.batch) { cckdblk.nostress = 1; cckdblk.freepend = 0; cckdblk.linuxnull = 1; } return 0; } /* end function cckddasd_init_handler */ /*-------------------------------------------------------------------*/ /* Close a Compressed CKD Device */ /*-------------------------------------------------------------------*/ int cckddasd_close_device (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int i; /* Index */ cckd = dev->cckd_ext; /* Wait for readaheads to finish */ obtain_lock(&cckdblk.ralock); cckd->stopping = 1; while (cckd->ras) { release_lock(&cckdblk.ralock); usleep(1); obtain_lock(&cckdblk.ralock); } release_lock(&cckdblk.ralock); /* Flush the cache and wait for the writes to complete */ obtain_lock (&cckd->cckdiolock); cckd->stopping = 1; cckd_flush_cache (dev); while (cckd->wrpending || cckd->cckdioact) { cckd->cckdwaiters++; wait_condition (&cckd->cckdiocond, &cckd->cckdiolock); cckd->cckdwaiters--; cckd_flush_cache (dev); } broadcast_condition (&cckd->cckdiocond); cckd_purge_cache (dev); cckd_purge_l2 (dev); dev->bufcur = dev->cache = -1; if (cckd->newbuf) cckd_free (dev, "newbuf", cckd->newbuf); release_lock (&cckd->cckdiolock); /* Remove the device from the cckd queue */ cckd_lock_devchain(1); if (dev == cckdblk.dev1st) cckdblk.dev1st = cckd->devnext; else { DEVBLK *dev2 = cckdblk.dev1st; CCKDDASD_EXT *cckd2 = dev2->cckd_ext; while (cckd2->devnext != dev) { dev2 = cckd2->devnext; cckd2 = dev2->cckd_ext; } cckd2->devnext = cckd->devnext; } cckd_unlock_devchain(); /* harden the file */ obtain_lock (&cckd->filelock); cckd_harden (dev); /* close the shadow files */ for (i = 1; i <= cckd->sfn; i++) { cckd_close (dev, i); cckd->open[i] = 0; } /* free the level 1 tables */ for (i = 0; i <= cckd->sfn; i++) cckd->l1[i] = cckd_free (dev, "l1", cckd->l1[i]); /* reset the device handler */ if (cckd->ckddasd) dev->hnd = &ckddasd_device_hndinfo; else dev->hnd = &fbadasd_device_hndinfo; /* write some statistics */ if (!dev->batch) cckd_sf_stats (dev); release_lock (&cckd->filelock); /* free the cckd extension */ dev->cckd_ext= cckd_free (dev, "ext", cckd); if (dev->dasdsfn) free (dev->dasdsfn); dev->dasdsfn = NULL; close (dev->fd); dev->fd = -1; /* If no more devices then perform global termination */ if (cckdblk.dev1st == NULL) cckddasd_term (); return 0; } /* end function cckddasd_close_device */ /*-------------------------------------------------------------------*/ /* Compressed ckd start/resume channel program */ /*-------------------------------------------------------------------*/ void cckddasd_start (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ U16 devnum = 0; /* Last active device number */ int trk = 0; /* Last active track */ cckd = dev->cckd_ext; cckd_trace (dev, "start i/o file[%d] bufcur %d cache[%d]", cckd->sfn, dev->bufcur, dev->cache); /* Reset buffer offsets */ dev->bufoff = 0; dev->bufoffhi = cckd->ckddasd ? dev->ckdtrksz : CFBA_BLOCK_SIZE; #ifdef OPTION_SYNCIO /* Check for merge - synchronous i/o should be disabled */ #else // OPTION_NOSYNCIO /* Check for merge */ #endif // OPTION_SYNCIO obtain_lock(&cckd->cckdiolock); if (cckd->merging) { cckd_trace (dev, "start i/o waiting for merge%s",""); while (cckd->merging) { cckd->cckdwaiters++; wait_condition (&cckd->cckdiocond, &cckd->cckdiolock); cckd->cckdwaiters--; } dev->bufcur = dev->cache = -1; } cckd->cckdioact = 1; cache_lock(CACHE_DEVBUF); if (dev->cache >= 0) CCKD_CACHE_GETKEY(dev->cache, devnum, trk); /* Check if previous active entry is still valid and not busy */ if (dev->cache >= 0 && dev->devnum == devnum && dev->bufcur == trk && !(cache_getflag(CACHE_DEVBUF, dev->cache) & CCKD_CACHE_IOBUSY)) { /* Make the entry active again */ cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_ACTIVE); /* If the entry is pending write then change it to `updated' */ if (cache_getflag(CACHE_DEVBUF, dev->cache) & CCKD_CACHE_WRITE) { cache_setflag (CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_WRITE, CCKD_CACHE_UPDATED); cckd->wrpending--; if (cckd->cckdwaiters && !cckd->wrpending) broadcast_condition (&cckd->cckdiocond); } } else dev->bufcur = dev->cache = -1; cache_unlock (CACHE_DEVBUF); release_lock (&cckd->cckdiolock); return; } /* end function cckddasd_start */ /*-------------------------------------------------------------------*/ /* Compressed ckd end/suspend channel program */ /*-------------------------------------------------------------------*/ void cckddasd_end (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ cckd = dev->cckd_ext; /* Update length if previous image was updated */ if (dev->bufupd && dev->bufcur >= 0 && dev->cache >= 0) { dev->buflen = cckd_trklen (dev, dev->buf); cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen); } dev->bufupd = 0; cckd_trace (dev, "end i/o bufcur %d cache[%d] waiters %d", dev->bufcur, dev->cache, cckd->cckdwaiters); obtain_lock (&cckd->cckdiolock); cckd->cckdioact = 0; /* Make the current entry inactive */ if (dev->cache >= 0) { cache_lock (CACHE_DEVBUF); cache_setflag (CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_ACTIVE, 0); cache_unlock (CACHE_DEVBUF); } /* Cause writers to start after first update */ if (cckd->updated && (cckdblk.wrs == 0 || cckd->cckdwaiters != 0)) cckd_flush_cache (dev); else if (cckd->cckdwaiters) broadcast_condition (&cckd->cckdiocond); release_lock (&cckd->cckdiolock); } /* end function cckddasd_end */ /*-------------------------------------------------------------------*/ /* Open a cckd file */ /* */ /* If O_CREAT is not set and mode is non-zero then the error message */ /* will be supressed. */ /*-------------------------------------------------------------------*/ int cckd_open (DEVBLK *dev, int sfx, int flags, mode_t mode) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int err; /* 1 = issue error message */ char pathname[MAX_PATH]; /* file path in host format */ cckd = dev->cckd_ext; err = !((flags & O_CREAT) == 0 && mode != 0); if (cckd->fd[sfx] >= 0) cckd_close (dev, sfx); hostpath(pathname, cckd_sf_name (dev, sfx), sizeof(pathname)); cckd->fd[sfx] = HOPEN (pathname, flags, mode); if (sfx == 0) dev->fd = cckd->fd[sfx]; if (cckd->fd[sfx] >= 0) cckd->open[sfx] = flags & O_RDWR ? CCKD_OPEN_RW : cckd->open[sfx] == CCKD_OPEN_RW ? CCKD_OPEN_RD : CCKD_OPEN_RO; else { if (err) { WRMSG (HHC00301, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), "open()", strerror(errno)); cckd_trace (dev, "file[%d] fd[%d] open %s error flags %8.8x mode %8.8x", sfx, cckd->fd[sfx], cckd_sf_name (dev, sfx), flags, mode); cckd_print_itrace (); } cckd->open[sfx] = CCKD_OPEN_NONE; } cckd_trace (dev, "file[%d] fd[%d] open %s, flags %8.8x mode %8.8x", sfx, cckd->fd[sfx], cckd_sf_name (dev, sfx), flags, mode); return cckd->fd[sfx]; } /* end function cckd_open */ /*-------------------------------------------------------------------*/ /* Close a cckd file */ /*-------------------------------------------------------------------*/ int cckd_close (DEVBLK *dev, int sfx) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc = 0; /* Return code */ cckd = dev->cckd_ext; cckd_trace (dev, "file[%d] fd[%d] close %s", sfx, cckd->fd[sfx], cckd_sf_name(dev, sfx)); if (cckd->fd[sfx] >= 0) rc = close (cckd->fd[sfx]); if (rc < 0) { WRMSG (HHC00301, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), "close()", strerror(errno)); cckd_print_itrace (); } cckd->fd[sfx] = -1; if (sfx == 0) dev->fd = -1; return rc; } /* end function cckd_close */ /*-------------------------------------------------------------------*/ /* Read from a cckd file */ /*-------------------------------------------------------------------*/ int cckd_read (DEVBLK *dev, int sfx, off_t off, void *buf, unsigned int len) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ cckd = dev->cckd_ext; cckd_trace (dev, "file[%d] fd[%d] read, off 0x"I64_FMTx" len %d", sfx, cckd->fd[sfx], off, len); /* Seek to specified offset */ if (lseek (cckd->fd[sfx], off, SEEK_SET) < 0) { WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), "lseek()", off, strerror(errno)); cckd_print_itrace (); return -1; } /* Read the data */ rc = read (cckd->fd[sfx], buf, len); if (rc < (int)len) { if (rc < 0) WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), "read()", off, strerror(errno)); else { char buf[128]; MSGBUF( buf, "read incomplete: read %d, expected %d", rc, len); WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), "read()", off, buf); } cckd_print_itrace (); return -1; } return rc; } /* end function cckd_read */ /*-------------------------------------------------------------------*/ /* Write to a cckd file */ /*-------------------------------------------------------------------*/ int cckd_write (DEVBLK *dev, int sfx, off_t off, void *buf, unsigned int len) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc = 0; /* Return code */ cckd = dev->cckd_ext; cckd_trace (dev, "file[%d] fd[%d] write, off 0x"I64_FMTx" len %d", sfx, cckd->fd[sfx], off, len); /* Seek to specified offset */ if (lseek (cckd->fd[sfx], off, SEEK_SET) < 0) { WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), "lseek()", off, strerror(errno)); return -1; } /* Write the data */ rc = write (cckd->fd[sfx], buf, len); if (rc < (int)len) { if (rc < 0) WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), "write()", off, strerror(errno)); else { char buf[128]; MSGBUF( buf, "write incomplete: write %d, expected %d", rc, len); WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), "write()", off, buf); } cckd_print_itrace (); return -1; } return rc; } /* end function cckd_write */ /*-------------------------------------------------------------------*/ /* Truncate a cckd file */ /*-------------------------------------------------------------------*/ int cckd_ftruncate(DEVBLK *dev, int sfx, off_t off) { CCKDDASD_EXT *cckd; /* -> cckd extension */ cckd = dev->cckd_ext; cckd_trace (dev, "file[%d] fd[%d] ftruncate, off 0x"I64_FMTx, sfx, cckd->fd[sfx], off); /* Truncate the file */ if (ftruncate (cckd->fd[sfx], off) < 0) { WRMSG (HHC00302, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), "ftruncate()", off, strerror(errno)); cckd_print_itrace (); return -1; } return 0; } /* end function cckd_ftruncate */ /*-------------------------------------------------------------------*/ /* malloc */ /*-------------------------------------------------------------------*/ void *cckd_malloc (DEVBLK *dev, char *id, size_t size) { void *p; /* Pointer */ p = malloc (size); cckd_trace (dev, "%s malloc %p len %ld", id, p, (long)size); if (p == NULL) { char buf[64]; MSGBUF( buf, "malloc(%d)", (int)size); WRMSG (HHC00303, "E", dev ? SSID_TO_LCSS(dev->ssid) : 0, dev ? dev->devnum : 0, buf, strerror(errno)); cckd_print_itrace (); } return p; } /* end function cckd_malloc */ /*-------------------------------------------------------------------*/ /* calloc */ /*-------------------------------------------------------------------*/ void *cckd_calloc (DEVBLK *dev, char *id, size_t n, size_t size) { void *p; /* Pointer */ p = calloc (n, size); cckd_trace (dev, "%s calloc %p len %ld", id, p, n*(long)size); if (p == NULL) { char buf[64]; MSGBUF( buf, "calloc(%d, %d)", (int)n, (int)size); WRMSG (HHC00303, "E", dev ? SSID_TO_LCSS(dev->ssid) : 0, dev ? dev->devnum : 0, buf, strerror(errno)); cckd_print_itrace (); } return p; } /* end function cckd_calloc */ /*-------------------------------------------------------------------*/ /* free */ /*-------------------------------------------------------------------*/ void *cckd_free (DEVBLK *dev, char *id, void *p) { cckd_trace (dev, "%s free %p", id, p); if (p) free (p); return (void*)NULL; } /* end function cckd_free */ /*-------------------------------------------------------------------*/ /* Compressed ckd read track image */ /*-------------------------------------------------------------------*/ int cckd_read_track (DEVBLK *dev, int trk, BYTE *unitstat) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ int len; /* Compressed length */ BYTE *newbuf; /* Uncompressed buffer */ int cache; /* New active cache entry */ #ifdef OPTION_SYNCIO int syncio; /* Syncio indicator */ #endif // OPTION_SYNCIO cckd = dev->cckd_ext; /* Update length if previous image was updated */ if (dev->bufupd && dev->bufcur >= 0 && dev->cache >= 0) { dev->buflen = cckd_trklen (dev, dev->buf); cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen); } #ifdef OPTION_SYNCIO /* Turn off the synchronous I/O bit if trk overflow or trk 0 */ syncio = dev->syncio_active; if (dev->ckdtrkof || trk == 0) dev->syncio_active = 0; #endif // OPTION_SYNCIO /* Reset buffer offsets */ dev->bufoff = 0; dev->bufoffhi = dev->ckdtrksz; /* Check if reading the same track image */ if (trk == dev->bufcur && dev->cache >= 0) { /* Track image may be compressed */ if ((dev->buf[0] & CCKD_COMPRESS_MASK) != 0 && (dev->buf[0] & dev->comps) == 0) { #ifdef OPTION_SYNCIO #if 0 /* Return if synchronous i/o */ if (dev->syncio_active) { cckd_trace (dev, "read trk %d syncio compressed", trk); cckdblk.stats_synciomisses++; dev->syncio_retry = 1; return -1; } #endif #endif // OPTION_SYNCIO len = cache_getval(CACHE_DEVBUF, dev->cache); newbuf = cckd_uncompress (dev, dev->buf, len, dev->ckdtrksz, trk); if (newbuf == NULL) { ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0); *unitstat = CSW_CE | CSW_DE | CSW_UC; dev->bufcur = dev->cache = -1; #ifdef OPTION_SYNCIO dev->syncio_active = syncio; #endif // OPTION_SYNCIO return -1; } cache_setbuf (CACHE_DEVBUF, dev->cache, newbuf, dev->ckdtrksz); dev->buf = newbuf; dev->buflen = cckd_trklen (dev, newbuf); cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen); dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache); dev->bufupd = 0; cckd_trace (dev, "read trk %d uncompressed len %d", trk, dev->buflen); } dev->comp = dev->buf[0] & CCKD_COMPRESS_MASK; if (dev->comp != 0) dev->compoff = CKDDASD_TRKHDR_SIZE; return 0; } cckd_trace (dev, "read trk %d (%s)", trk, #ifdef OPTION_SYNCIO dev->syncio_active ? "synchronous" : #endif // OPTION_SYNCIO "asynchronous"); /* read the new track */ dev->bufupd = 0; *unitstat = 0; cache = cckd_read_trk (dev, trk, 0, unitstat); if (cache < 0) { dev->bufcur = dev->cache = -1; return -1; } dev->cache = cache; dev->buf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0); dev->bufcur = trk; dev->bufoff = 0; dev->bufoffhi = dev->ckdtrksz; dev->buflen = cache_getval (CACHE_DEVBUF, dev->cache); dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache); dev->comp = dev->buf[0] & CCKD_COMPRESS_MASK; if (dev->comp != 0) dev->compoff = CKDDASD_TRKHDR_SIZE; /* If the image is compressed then call ourself recursively to cause the image to get uncompressed */ if (dev->comp != 0 && (dev->comp & dev->comps) == 0) rc = cckd_read_track (dev, trk, unitstat); else rc = 0; #ifdef OPTION_SYNCIO dev->syncio_active = syncio; #endif // OPTION_SYNCIO return rc; } /* end function cckd_read_track */ /*-------------------------------------------------------------------*/ /* Compressed ckd update track image */ /*-------------------------------------------------------------------*/ int cckd_update_track (DEVBLK *dev, int trk, int off, BYTE *buf, int len, BYTE *unitstat) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ cckd = dev->cckd_ext; /* Error if opened read-only */ if (dev->ckdrdonly && cckd->sfn == 0) { ckd_build_sense (dev, SENSE_EC, SENSE1_WRI, 0,FORMAT_1, MESSAGE_0); *unitstat = CSW_CE | CSW_DE | CSW_UC; dev->bufcur = dev->cache = -1; return -1; } /* If the track is not current or compressed then read it. `dev->comps' is set to zero forcing the read routine to uncompress the image. */ if (trk != dev->bufcur || (dev->buf[0] & CCKD_COMPRESS_MASK) != 0) { dev->comps = 0; rc = (dev->hnd->read) (dev, trk, unitstat); if (rc < 0) { dev->bufcur = dev->cache = -1; return -1; } } /* Invalid track format if going past buffer end */ if (off + len > dev->ckdtrksz) { ckd_build_sense (dev, 0, SENSE1_ITF, 0, 0, 0); *unitstat = CSW_CE | CSW_DE | CSW_UC; dev->bufcur = dev->cache = -1; return -1; } /* Copy the data into the buffer */ if (buf && len > 0) memcpy (dev->buf + off, buf, len); cckd_trace (dev, "updt trk %d offset %d length %d", trk, off, len); /* Update the cache entry */ cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_UPDATED | CCKD_CACHE_USED); cckd->updated = 1; /* Notify the shared server of the update */ if (!dev->bufupd) { dev->bufupd = 1; shared_update_notify (dev, trk); } return len; } /* end function cckd_update_track */ /*-------------------------------------------------------------------*/ /* Return used cylinders */ /*-------------------------------------------------------------------*/ int cckd_used (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ int l1x, l2x; /* Lookup table indexes */ int sfx; /* Shadow file suffix */ CCKD_L2ENT l2; /* Copied level 2 entry */ cckd = dev->cckd_ext; obtain_lock (&cckd->filelock); /* Find the last used level 1 table entry */ for (l1x = cckd->cdevhdr[0].numl1tab - 1; l1x > 0; l1x--) { sfx = cckd->sfn; while (cckd->l1[sfx][l1x] == 0xffffffff && sfx > 0) sfx--; if (cckd->l1[sfx][l1x]) break; } /* Find the last used level 2 table entry */ for (l2x = 255; l2x >= 0; l2x--) { rc = cckd_read_l2ent (dev, &l2, l1x * 256 + l2x); if (rc < 0 || l2.pos != 0) break; } release_lock (&cckd->filelock); return (l1x * 256 + l2x + dev->ckdheads) / dev->ckdheads; } /*-------------------------------------------------------------------*/ /* Compressed fba read block(s) */ /*-------------------------------------------------------------------*/ int cfba_read_block (DEVBLK *dev, int blkgrp, BYTE *unitstat) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ int cache; /* New active cache entry */ BYTE *cbuf; /* -> cache buffer */ BYTE *newbuf; /* Uncompressed buffer */ int len; /* Compressed length */ int maxlen; /* Size for cache entry */ cckd = dev->cckd_ext; if (dev->cache >= 0) cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0); else cbuf = NULL; maxlen = CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE; /* Return if reading the same track image */ if (blkgrp == dev->bufcur && dev->cache >= 0) { /* Block group image may be compressed */ if ((cbuf[0] & CCKD_COMPRESS_MASK) != 0 && (cbuf[0] & dev->comps) == 0) { #ifdef OPTION_SYNCIO #if 0 /* Return if synchronous i/o */ if (dev->syncio_active) { cckd_trace (dev, "read blkgrp %d syncio compressed", blkgrp); cckdblk.stats_synciomisses++; dev->syncio_retry = 1; return -1; } #endif #endif // OPTION_SYNCIO len = cache_getval(CACHE_DEVBUF, dev->cache) + CKDDASD_TRKHDR_SIZE; newbuf = cckd_uncompress (dev, cbuf, len, maxlen, blkgrp); if (newbuf == NULL) { dev->sense[0] = SENSE_EC; *unitstat = CSW_CE | CSW_DE | CSW_UC; dev->bufcur = dev->cache = -1; return -1; } cache_setbuf (CACHE_DEVBUF, dev->cache, newbuf, maxlen); cbuf = newbuf; dev->buf = newbuf + CKDDASD_TRKHDR_SIZE; dev->buflen = CFBA_BLOCK_SIZE; cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen); dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache); dev->bufupd = 0; cckd_trace (dev, "read bkgrp %d uncompressed len %d", blkgrp, dev->buflen); } dev->comp = cbuf[0] & CCKD_COMPRESS_MASK; return 0; } cckd_trace (dev, "read blkgrp %d (%s)", blkgrp, #ifdef OPTION_SYNCIO dev->syncio_active ? "synchronous" : #endif // OPTION_SYNCIO "asynchronous"); /* Read the new blkgrp */ dev->bufupd = 0; *unitstat = 0; cache = cckd_read_trk (dev, blkgrp, 0, unitstat); if (cache < 0) { dev->bufcur = dev->cache = -1; return -1; } dev->cache = cache; cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0); dev->buf = cbuf + CKDDASD_TRKHDR_SIZE; dev->bufcur = blkgrp; dev->bufoff = 0; dev->bufoffhi = CFBA_BLOCK_SIZE; dev->buflen = CFBA_BLOCK_SIZE; cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen); dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache); dev->comp = cbuf[0] & CCKD_COMPRESS_MASK; /* If the image is compressed then call ourself recursively to cause the image to get uncompressed. This is because `bufcur' will match blkgrp and `comps' won't match `comp' */ if (dev->comp != 0 && (dev->comp & dev->comps) == 0) rc = cfba_read_block (dev, blkgrp, unitstat); else rc = 0; return rc; } /* end function cfba_read_block */ /*-------------------------------------------------------------------*/ /* Compressed fba write block(s) */ /*-------------------------------------------------------------------*/ int cfba_write_block (DEVBLK *dev, int blkgrp, int off, BYTE *buf, int len, BYTE *unitstat) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ BYTE *cbuf; /* -> cache buffer */ cckd = dev->cckd_ext; if (dev->cache >= 0) cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0); else cbuf = NULL; /* Read the block group if it's not current or compressed. `dev->comps' is set to zero forcing the read routine to uncompress the image. */ if (blkgrp != dev->bufcur || (cbuf[0] & CCKD_COMPRESS_MASK) != 0) { dev->comps = 0; rc = (dev->hnd->read) (dev, blkgrp, unitstat); if (rc < 0) { dev->bufcur = dev->cache = -1; return -1; } } /* Copy the data into the buffer */ if (buf) memcpy (dev->buf + off, buf, len); /* Update the cache entry */ cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_UPDATED|CCKD_CACHE_USED); cckd->updated = 1; /* Notify the shared server of the update */ if (!dev->bufupd) { dev->bufupd = 1; shared_update_notify (dev, blkgrp); } return len; } /* end function cfba_write_block */ /*-------------------------------------------------------------------*/ /* Return used blocks */ /*-------------------------------------------------------------------*/ int cfba_used (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ int l1x, l2x; /* Lookup table indexes */ int sfx; /* Shadow file suffix */ CCKD_L2ENT l2; /* Copied level 2 entry */ cckd = dev->cckd_ext; obtain_lock (&cckd->filelock); /* Find the last used level 1 table entry */ for (l1x = cckd->cdevhdr[0].numl1tab - 1; l1x > 0; l1x--) { sfx = cckd->sfn; while (cckd->l1[sfx][l1x] == 0xffffffff && sfx > 0) sfx--; if (cckd->l1[sfx][l1x]) break; } /* Find the last used level 2 table entry */ for (l2x = 255; l2x >= 0; l2x--) { rc = cckd_read_l2ent (dev, &l2, l1x * 256 + l2x); if (rc < 0 || l2.pos != 0) break; } release_lock (&cckd->filelock); return (l1x * 256 + l2x + CFBA_BLOCK_NUM) / CFBA_BLOCK_NUM; } /*-------------------------------------------------------------------*/ /* Read a track image */ /* */ /* This routine can be called by the i/o thread (`ra' == 0) or */ /* by readahead threads (0 < `ra' <= cckdblk.ramax). */ /* */ /*-------------------------------------------------------------------*/ int cckd_read_trk(DEVBLK *dev, int trk, int ra, BYTE *unitstat) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int fnd; /* Cache index for hit */ int lru; /* Oldest unused cache index */ int len; /* Length of track image */ int maxlen; /* Length for buffer */ int curtrk = -1; /* Current track (at entry) */ U16 devnum; /* Device number */ U32 oldtrk; /* Stolen track number */ U32 flag; /* Cache flag */ BYTE *buf; /* Read buffer */ cckd = dev->cckd_ext; cckd_trace (dev, "%d rdtrk %d", ra, trk); maxlen = cckd->ckddasd ? dev->ckdtrksz : CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE; if (!ra) obtain_lock (&cckd->cckdiolock); cache_lock (CACHE_DEVBUF); /* Inactivate the old entry */ if (!ra) { curtrk = dev->bufcur; if (dev->cache >= 0) cache_setflag(CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_ACTIVE, 0); dev->bufcur = dev->cache = -1; } cckd_read_trk_retry: /* scan the cache array for the track */ fnd = cache_lookup (CACHE_DEVBUF, CCKD_CACHE_SETKEY(dev->devnum, trk), &lru); /* check for cache hit */ if (fnd >= 0) { if (ra) /* readahead doesn't care about a cache hit */ { cache_unlock (CACHE_DEVBUF); return fnd; } #ifdef OPTION_SYNCIO /* If synchronous I/O and I/O is active then return with syncio_retry bit on */ if (dev->syncio_active) { if (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_IOBUSY) { cckd_trace (dev, "%d rdtrk[%d] %d syncio %s", ra, fnd, trk, cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_READING ? "reading" : "writing"); cckdblk.stats_synciomisses++; dev->syncio_retry = 1; cache_unlock (CACHE_DEVBUF); release_lock (&cckd->cckdiolock); return -1; } else cckdblk.stats_syncios++; } #endif // OPTION_SYNCIO /* Mark the new entry active */ cache_setflag(CACHE_DEVBUF, fnd, ~0, CCKD_CACHE_ACTIVE | CCKD_CACHE_USED); cache_setage(CACHE_DEVBUF, fnd); /* If the entry is pending write then change it to `updated' */ if (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_WRITE) { cache_setflag(CACHE_DEVBUF, fnd, ~CCKD_CACHE_WRITE, CCKD_CACHE_UPDATED); cckd->wrpending--; if (cckd->cckdwaiters && !cckd->wrpending) broadcast_condition (&cckd->cckdiocond); } buf = cache_getbuf(CACHE_DEVBUF, fnd, 0); cache_unlock (CACHE_DEVBUF); cckd_trace (dev, "%d rdtrk[%d] %d cache hit buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x", ra, fnd, trk, buf, buf[0], buf[1], buf[2], buf[3], buf[4]); cckdblk.stats_switches++; cckd->switches++; cckdblk.stats_cachehits++; cckd->cachehits++; /* if read/write is in progress then wait for it to finish */ while (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_IOBUSY) { cckdblk.stats_iowaits++; cckd_trace (dev, "%d rdtrk[%d] %d waiting for %s", ra, fnd, trk, cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_READING ? "read" : "write"); cache_setflag (CACHE_DEVBUF, fnd, ~0, CCKD_CACHE_IOWAIT); cckd->cckdwaiters++; wait_condition (&cckd->cckdiocond, &cckd->cckdiolock); cckd->cckdwaiters--; cache_setflag (CACHE_DEVBUF, fnd, ~CCKD_CACHE_IOWAIT, 0); cckd_trace (dev, "%d rdtrk[%d] %d io wait complete", ra, fnd, trk); } release_lock (&cckd->cckdiolock); /* Asynchrously schedule readaheads */ if (curtrk > 0 && trk > curtrk && trk <= curtrk + 2) cckd_readahead (dev, trk); return fnd; } /* cache hit */ #ifdef OPTION_SYNCIO /* If not readahead and synchronous I/O then retry */ if (!ra && dev->syncio_active) { cache_unlock(CACHE_DEVBUF); release_lock (&cckd->cckdiolock); cckd_trace (dev, "%d rdtrk[%d] %d syncio cache miss", ra, lru, trk); cckdblk.stats_synciomisses++; dev->syncio_retry = 1; return -1; } #endif // OPTION_SYNCIO cckd_trace (dev, "%d rdtrk[%d] %d cache miss", ra, lru, trk); /* If no cache entry was stolen, then flush all outstanding writes. This requires us to release our locks. cache_wait should be called with only the cache_lock held. Fortunately, cache waits occur very rarely. */ if (lru < 0) /* No available entry to be stolen */ { cckd_trace (dev, "%d rdtrk[%d] %d no available cache entry", ra, lru, trk); cache_unlock (CACHE_DEVBUF); if (!ra) release_lock (&cckd->cckdiolock); cckd_flush_cache_all(); cache_lock (CACHE_DEVBUF); cckdblk.stats_cachewaits++; cache_wait (CACHE_DEVBUF); if (!ra) { cache_unlock (CACHE_DEVBUF); obtain_lock (&cckd->cckdiolock); cache_lock (CACHE_DEVBUF); } goto cckd_read_trk_retry; } CCKD_CACHE_GETKEY(lru, devnum, oldtrk); if (devnum != 0) { cckd_trace (dev, "%d rdtrk[%d] %d dropping %4.4X:%d from cache", ra, lru, trk, devnum, oldtrk); if (!(cache_getflag(CACHE_DEVBUF, lru) & CCKD_CACHE_USED)) { cckdblk.stats_readaheadmisses++; cckd->misses++; } } /* Initialize the entry */ cache_setkey(CACHE_DEVBUF, lru, CCKD_CACHE_SETKEY(dev->devnum, trk)); cache_setflag(CACHE_DEVBUF, lru, 0, CCKD_CACHE_READING); cache_setage(CACHE_DEVBUF, lru); cache_setval(CACHE_DEVBUF, lru, 0); if (!ra) { cckdblk.stats_switches++; cckd->switches++; cckdblk.stats_cachemisses++; cache_setflag(CACHE_DEVBUF, lru, ~0, CCKD_CACHE_ACTIVE|CCKD_CACHE_USED); } cache_setflag(CACHE_DEVBUF, lru, ~CACHE_TYPE, cckd->ckddasd ? DEVBUF_TYPE_CCKD : DEVBUF_TYPE_CFBA); buf = cache_getbuf(CACHE_DEVBUF, lru, maxlen); cckd_trace (dev, "%d rdtrk[%d] %d buf %p len %d", ra, lru, trk, buf, cache_getlen(CACHE_DEVBUF, lru)); cache_unlock (CACHE_DEVBUF); if (!ra) release_lock (&cckd->cckdiolock); /* Asynchronously schedule readaheads */ if (!ra && curtrk > 0 && trk > curtrk && trk <= curtrk + 2) cckd_readahead (dev, trk); /* Clear the buffer if batch mode */ if (dev->batch) memset(buf, 0, maxlen); /* Read the track image */ obtain_lock (&cckd->filelock); len = cckd_read_trkimg (dev, buf, trk, unitstat); release_lock (&cckd->filelock); cache_setval (CACHE_DEVBUF, lru, len); obtain_lock (&cckd->cckdiolock); /* Turn off the READING bit */ cache_lock (CACHE_DEVBUF); flag = cache_setflag(CACHE_DEVBUF, lru, ~CCKD_CACHE_READING, 0); cache_unlock (CACHE_DEVBUF); /* Wakeup other thread waiting for this read */ if (cckd->cckdwaiters && (flag & CCKD_CACHE_IOWAIT)) { cckd_trace (dev, "%d rdtrk[%d] %d signalling read complete", ra, lru, trk); broadcast_condition (&cckd->cckdiocond); } release_lock (&cckd->cckdiolock); if (ra) { cckdblk.stats_readaheads++; cckd->readaheads++; } cckd_trace (dev, "%d rdtrk[%d] %d complete buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x", ra, lru, trk, buf, buf[0], buf[1], buf[2], buf[3], buf[4]); if (cache_busy_percent(CACHE_DEVBUF) > 80) cckd_flush_cache_all(); return lru; } /* end function cckd_read_trk */ /*-------------------------------------------------------------------*/ /* Schedule asynchronous readaheads */ /*-------------------------------------------------------------------*/ void cckd_readahead (DEVBLK *dev, int trk) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int i, r; /* Indexes */ TID tid; /* Readahead thread id */ int rc; cckd = dev->cckd_ext; if (cckdblk.ramax < 1 || cckdblk.readaheads < 1) return; obtain_lock (&cckdblk.ralock); /* Scan the cache to see if the tracks are already there */ memset( cckd->ralkup, 0, sizeof(cckd->ralkup) ); cckd->ratrk = trk; cache_lock(CACHE_DEVBUF); cache_scan(CACHE_DEVBUF, cckd_readahead_scan, dev); cache_unlock(CACHE_DEVBUF); /* Scan the queue to see if the tracks are already there */ for (r = cckdblk.ra1st; r >= 0; r = cckdblk.ra[r].next) if (cckdblk.ra[r].dev == dev) { i = cckdblk.ra[r].trk - trk; if (i > 0 && i <= cckdblk.readaheads) cckd->ralkup[i-1] = 1; } /* Queue the tracks to the readahead queue */ for (i = 1; i <= cckdblk.readaheads && cckdblk.rafree >= 0; i++) { if (cckd->ralkup[i-1]) continue; if (trk + i >= dev->ckdtrks) break; r = cckdblk.rafree; cckdblk.rafree = cckdblk.ra[r].next; if (cckdblk.ralast < 0) { cckdblk.ra1st = cckdblk.ralast = r; cckdblk.ra[r].prev = cckdblk.ra[r].next = -1; } else { cckdblk.ra[cckdblk.ralast].next = r; cckdblk.ra[r].prev = cckdblk.ralast; cckdblk.ra[r].next = -1; cckdblk.ralast = r; } cckdblk.ra[r].trk = trk + i; cckdblk.ra[r].dev = dev; } /* Schedule the readahead if any are pending */ if (cckdblk.ra1st >= 0) { if (cckdblk.rawaiting) signal_condition (&cckdblk.racond); else if (cckdblk.ras < cckdblk.ramax) { rc = create_thread (&tid, JOINABLE, cckd_ra, NULL, "cckd_ra"); if (rc) WRMSG(HHC00102, "E", strerror(rc)); } } release_lock (&cckdblk.ralock); } /* end function cckd_readahead */ int cckd_readahead_scan (int *answer, int ix, int i, void *data) { CCKDDASD_EXT *cckd; /* -> cckd extension */ U16 devnum; /* Cached device number */ U32 trk; /* Cached track */ DEVBLK *dev = data; /* -> device block */ int k; /* Index */ UNREFERENCED(answer); UNREFERENCED(ix); cckd = dev->cckd_ext; CCKD_CACHE_GETKEY(i, devnum, trk); if (devnum == dev->devnum) { k = (int)trk - cckd->ratrk; if (k > 0 && k <= cckdblk.readaheads) cckd->ralkup[k-1] = 1; } return 0; } /*-------------------------------------------------------------------*/ /* Asynchronous readahead thread */ /*-------------------------------------------------------------------*/ void* cckd_ra (void* arg) { CCKDDASD_EXT *cckd; /* -> cckd extension */ DEVBLK *dev; /* Readahead devblk */ int trk; /* Readahead track */ int ra; /* Readahead index */ int r; /* Readahead queue index */ TID tid; /* Readahead thread id */ char threadname[40]; int rc; UNREFERENCED( arg ); obtain_lock (&cckdblk.ralock); ra = ++cckdblk.ras; /* Return without messages if too many already started */ if (ra > cckdblk.ramax) { --cckdblk.ras; release_lock (&cckdblk.ralock); return NULL; } if (!cckdblk.batch) { MSGBUF( threadname, "Read-ahead thread-%d", ra); WRMSG (HHC00100, "I", thread_id(), get_thread_priority(0), threadname); } while (ra <= cckdblk.ramax) { if (cckdblk.ra1st < 0) { cckdblk.rawaiting++; wait_condition (&cckdblk.racond, &cckdblk.ralock); cckdblk.rawaiting--; } /* Possibly shutting down if no writes pending */ if (cckdblk.ra1st < 0) continue; r = cckdblk.ra1st; trk = cckdblk.ra[r].trk; dev = cckdblk.ra[r].dev; cckd = dev->cckd_ext; /* Requeue the 1st entry to the readahead free queue */ cckdblk.ra1st = cckdblk.ra[r].next; if (cckdblk.ra[r].next > -1) cckdblk.ra[cckdblk.ra[r].next].prev = -1; else cckdblk.ralast = -1; cckdblk.ra[r].next = cckdblk.rafree; cckdblk.rafree = r; /* Schedule the other readaheads if any are still pending */ if (cckdblk.ra1st) { if (cckdblk.rawaiting) signal_condition (&cckdblk.racond); else if (cckdblk.ras < cckdblk.ramax) { rc = create_thread (&tid, JOINABLE, cckd_ra, dev, "cckd_ra"); if (rc) WRMSG(HHC00102, "E", strerror(rc)); } } if (!cckd || cckd->stopping || cckd->merging) continue; cckd->ras++; release_lock (&cckdblk.ralock); /* Read the readahead track */ cckd_read_trk (dev, trk, ra, NULL); obtain_lock (&cckdblk.ralock); cckd->ras--; } if (!cckdblk.batch) WRMSG (HHC00101, "I", thread_id(), get_thread_priority(0), threadname); --cckdblk.ras; if (!cckdblk.ras) signal_condition(&cckdblk.termcond); release_lock(&cckdblk.ralock); return NULL; } /* end thread cckd_ra_thread */ /*-------------------------------------------------------------------*/ /* Flush updated cache entries for a device */ /* */ /* Caller holds the cckd->cckdiolock */ /* cckdblk.wrlock then cache_lock is obtained and released */ /*-------------------------------------------------------------------*/ void cckd_flush_cache(DEVBLK *dev) { int rc; /* Return code */ TID tid; /* Writer thread id */ /* Scan cache for updated cache entries */ obtain_lock (&cckdblk.wrlock); cache_lock (CACHE_DEVBUF); //BHe: rc is never used? rc = cache_scan (CACHE_DEVBUF, cckd_flush_cache_scan, dev); cache_unlock (CACHE_DEVBUF); /* Schedule the writer if any writes are pending */ if (cckdblk.wrpending) { if (cckdblk.wrwaiting) signal_condition (&cckdblk.wrcond); else if (cckdblk.wrs < cckdblk.wrmax) { rc = create_thread (&tid, JOINABLE, cckd_writer, NULL, "cckd_writer"); if (rc) WRMSG(HHC00102, "E", strerror(rc)); } } release_lock (&cckdblk.wrlock); } int cckd_flush_cache_scan (int *answer, int ix, int i, void *data) { CCKDDASD_EXT *cckd; /* -> cckd extension */ U16 devnum; /* Cached device number */ U32 trk; /* Cached track */ DEVBLK *dev = data; /* -> device block */ UNREFERENCED(answer); cckd = dev->cckd_ext; CCKD_CACHE_GETKEY(i, devnum, trk); if ((cache_getflag(ix,i) & CACHE_BUSY) == CCKD_CACHE_UPDATED && dev->devnum == devnum) { cache_setflag (ix, i, ~CCKD_CACHE_UPDATED, CCKD_CACHE_WRITE); ++cckd->wrpending; ++cckdblk.wrpending; cckd_trace (dev, "flush file[%d] cache[%d] %4.4X trk %d", cckd->sfn, i, devnum, trk); } return 0; } void cckd_flush_cache_all() { CCKDDASD_EXT *cckd; /* -> cckd extension */ DEVBLK *dev = NULL; /* -> device block */ cckd_lock_devchain(0); for (dev = cckdblk.dev1st; dev; dev = cckd->devnext) { cckd = dev->cckd_ext; obtain_lock (&cckd->cckdiolock); if (!cckd->merging && !cckd->stopping) cckd_flush_cache(dev); release_lock (&cckd->cckdiolock); } cckd_unlock_devchain(); } /*-------------------------------------------------------------------*/ /* Purge cache entries for a device */ /* */ /* Caller holds the cckdiolock */ /* cache_lock is obtained and released */ /*-------------------------------------------------------------------*/ void cckd_purge_cache(DEVBLK *dev) { /* Scan cache and purge entries */ cache_lock (CACHE_DEVBUF); cache_scan (CACHE_DEVBUF, cckd_purge_cache_scan, dev); cache_unlock (CACHE_DEVBUF); } int cckd_purge_cache_scan (int *answer, int ix, int i, void *data) { U16 devnum; /* Cached device number */ U32 trk; /* Cached track */ DEVBLK *dev = data; /* -> device block */ UNREFERENCED(answer); CCKD_CACHE_GETKEY(i, devnum, trk); if (dev->devnum == devnum) { cache_release (ix, i, 0); cckd_trace (dev, "purge cache[%d] %4.4X trk %d purged", i, devnum, trk); } return 0; } /*-------------------------------------------------------------------*/ /* Writer thread */ /*-------------------------------------------------------------------*/ void* cckd_writer(void *arg) { DEVBLK *dev; /* Device block */ CCKDDASD_EXT *cckd; /* -> cckd extension */ int writer; /* Writer identifier */ int o; /* Cache entry found */ U16 devnum; /* Device number */ BYTE *buf; /* Buffer */ BYTE *bufp; /* Buffer to be written */ int len, bufl; /* Buffer lengths */ int trk; /* Track number */ int comp; /* Compression algorithm */ int parm; /* Compression parameter */ TID tid; /* Writer thead id */ U32 flag; /* Cache flag */ static char *compress[] = {"none", "zlib", "bzip2"}; BYTE buf2[65536]; /* Compress buffer */ char threadname[40]; int rc; UNREFERENCED(arg); #ifndef WIN32 /* Set writer priority just below cpu priority to mimimize the compression effect */ if(cckdblk.wrprio >= 0) set_thread_priority(0, cckdblk.wrprio); #endif obtain_lock (&cckdblk.wrlock); writer = ++cckdblk.wrs; /* Return without messages if too many already started */ if (writer > cckdblk.wrmax) { --cckdblk.wrs; release_lock (&cckdblk.wrlock); return NULL; } if (!cckdblk.batch) { MSGBUF( threadname, "Writer thread-%d", writer); WRMSG ( HHC00100, "I", thread_id(), get_thread_priority(0), threadname); } while (writer <= cckdblk.wrmax || cckdblk.wrpending) { /* Wait for work */ if (cckdblk.wrpending == 0) { cckdblk.wrwaiting++; wait_condition (&cckdblk.wrcond, &cckdblk.wrlock); cckdblk.wrwaiting--; } /* Scan the cache for the oldest pending write */ cache_lock (CACHE_DEVBUF); o = cache_scan (CACHE_DEVBUF, cckd_writer_scan, NULL); /* Possibly shutting down if no writes pending */ if (o < 0) { cache_unlock (CACHE_DEVBUF); cckdblk.wrpending = 0; continue; } cache_setflag (CACHE_DEVBUF, o, ~CCKD_CACHE_WRITE, CCKD_CACHE_WRITING); cache_unlock (CACHE_DEVBUF); /* Schedule the other writers if any writes are still pending */ cckdblk.wrpending--; if (cckdblk.wrpending) { if (cckdblk.wrwaiting) signal_condition (&cckdblk.wrcond); else if (cckdblk.wrs < cckdblk.wrmax) { rc = create_thread (&tid, JOINABLE, cckd_writer, NULL, "cckd_writer"); if (rc) WRMSG(HHC00102, "E", strerror(rc)); } } release_lock (&cckdblk.wrlock); /* Prepare to compress */ CCKD_CACHE_GETKEY(o, devnum, trk); dev = cckd_find_device_by_devnum (devnum); cckd = dev->cckd_ext; buf = cache_getbuf(CACHE_DEVBUF, o, 0); len = cckd_trklen (dev, buf); comp = len < CCKD_COMPRESS_MIN ? CCKD_COMPRESS_NONE : cckdblk.comp == 0xff ? cckd->cdevhdr[cckd->sfn].compress : cckdblk.comp; parm = cckdblk.compparm < 0 ? cckd->cdevhdr[cckd->sfn].compress_parm : cckdblk.compparm; cckd_trace (dev, "%d wrtrk[%d] %d len %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x", writer, o, trk, len, buf, buf[0], buf[1],buf[2],buf[3],buf[4]); /* Compress the image if not null */ if ((len = cckd_check_null_trk (dev, buf, trk, len)) > CKDDASD_NULLTRK_FMTMAX) { /* Stress adjustments */ if ((cache_waiters(CACHE_DEVBUF) || cache_busy(CACHE_DEVBUF) > 90) && !cckdblk.nostress) { cckdblk.stats_stresswrites++; comp = len < CCKD_STRESS_MINLEN ? CCKD_COMPRESS_NONE : CCKD_STRESS_COMP; parm = cache_busy(CACHE_DEVBUF) <= 95 ? CCKD_STRESS_PARM1 : CCKD_STRESS_PARM2; } /* Compress the track image */ cckd_trace (dev, "%d wrtrk[%d] %d comp %s parm %d", writer, o, trk, compress[comp], parm); bufp = (BYTE *)&buf2; bufl = cckd_compress(dev, &bufp, buf, len, comp, parm); cckd_trace (dev, "%d wrtrk[%d] %d compressed length %d", writer, o, trk, bufl); } else { bufp = buf; bufl = len; } obtain_lock (&cckd->filelock); /* Turn on read-write header bits if not already on */ if (!(cckd->cdevhdr[cckd->sfn].options & CCKD_OPENED)) { cckd->cdevhdr[cckd->sfn].options |= (CCKD_OPENED | CCKD_ORDWR); cckd_write_chdr (dev); } /* Write the track image */ cckd_write_trkimg (dev, bufp, bufl, trk, CCKD_SIZE_ANY); release_lock (&cckd->filelock); /* Schedule the garbage collector */ if (cckdblk.gcs < cckdblk.gcmax) { rc = create_thread (&tid, JOINABLE, cckd_gcol, NULL, "cckd_gcol"); if (rc) WRMSG(HHC00102, "E", strerror(rc)); } obtain_lock (&cckd->cckdiolock); cache_lock (CACHE_DEVBUF); flag = cache_setflag (CACHE_DEVBUF, o, ~CCKD_CACHE_WRITING, 0); cache_unlock (CACHE_DEVBUF); cckd->wrpending--; if (cckd->cckdwaiters && ((flag & CCKD_CACHE_IOWAIT) || !cckd->wrpending)) { cckd_trace (dev, "writer[%d] cache[%2.2d] %d signalling write complete", writer, o, trk); broadcast_condition (&cckd->cckdiocond); } release_lock(&cckd->cckdiolock); cckd_trace (dev, "%d wrtrk[%2.2d] %d complete flags:%8.8x", writer, o, trk, cache_getflag(CACHE_DEVBUF,o)); obtain_lock(&cckdblk.wrlock); } if (!cckdblk.batch) WRMSG (HHC00101, "I", thread_id(), get_thread_priority(0), threadname); cckdblk.wrs--; if (cckdblk.wrs == 0) signal_condition(&cckdblk.termcond); release_lock(&cckdblk.wrlock); return NULL; } /* end thread cckd_writer */ int cckd_writer_scan (int *o, int ix, int i, void *data) { UNREFERENCED(data); if ((cache_getflag(ix,i) & DEVBUF_TYPE_COMP) && (cache_getflag(ix,i) & CCKD_CACHE_WRITE) && (*o == -1 || cache_getage(ix, i) < cache_getage(ix, *o))) *o = i; return 0; } /*-------------------------------------------------------------------*/ /* Debug routine for checking the free space array */ /*-------------------------------------------------------------------*/ void cckd_chk_space(DEVBLK *dev) { #if 1 CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx; /* Shadow file index */ int err = 0, n = 0, i, p; size_t largest=0; size_t total=0; off_t fpos; cckd = dev->cckd_ext; sfx = cckd->sfn; p = -1; fpos = cckd->cdevhdr[sfx].free; for (i = cckd->free1st; i >= 0; i = cckd->free[i].next) { n++; total += cckd->free[i].len; if (n > cckd->freenbr) break; if (cckd->free[i].prev != p) err = 1; if (cckd->free[i].next >= 0) { if (fpos + cckd->free[i].len > cckd->free[i].pos) err = 1; } else { if (fpos + cckd->free[i].len > cckd->cdevhdr[sfx].size) err = 1; } if (cckd->free[i].pending == 0 && cckd->free[i].len > largest) largest = cckd->free[i].len; fpos = cckd->free[i].pos; p = i; } if (err || (cckd->cdevhdr[sfx].free != 0 && cckd->cdevhdr[sfx].free_number == 0) || (cckd->cdevhdr[sfx].free == 0 && cckd->cdevhdr[sfx].free_number != 0) || (n != cckd->cdevhdr[sfx].free_number) || (total != cckd->cdevhdr[sfx].free_total - cckd->cdevhdr[sfx].free_imbed) || (cckd->freelast != p) || (largest != cckd->cdevhdr[sfx].free_largest) ) { cckd_trace (dev, "cdevhdr[%d] size %10d used %10d free 0x%8.8x", sfx,cckd->cdevhdr[sfx].size,cckd->cdevhdr[sfx].used, cckd->cdevhdr[sfx].free); cckd_trace (dev, " nbr %10d total %10d imbed %10d largest %10d", cckd->cdevhdr[sfx].free_number, cckd->cdevhdr[sfx].free_total,cckd->cdevhdr[sfx].free_imbed, cckd->cdevhdr[sfx].free_largest); cckd_trace (dev, "free %p nbr %d 1st %d last %d avail %d", cckd->free,cckd->freenbr,cckd->free1st, cckd->freelast,cckd->freeavail); cckd_trace (dev, "found nbr %d total %ld largest %ld",n,(long)total,(long)largest); fpos = cckd->cdevhdr[sfx].free; for (n = 0, i = cckd->free1st; i >= 0; i = cckd->free[i].next) { if (++n > cckd->freenbr) break; cckd_trace (dev, "%4d: [%4d] prev[%4d] next[%4d] pos "I64_FMTx" len %8d "I64_FMTx" pend %d", n, i, cckd->free[i].prev, cckd->free[i].next, fpos, cckd->free[i].len, fpos + cckd->free[i].len, cckd->free[i].pending); fpos = cckd->free[i].pos; } cckd_print_itrace(); } #endif } /* end function cckd_chk_space */ /*-------------------------------------------------------------------*/ /* Get file space */ /*-------------------------------------------------------------------*/ off_t cckd_get_space(DEVBLK *dev, int *size, int flags) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int i,p,n; /* Free space indexes */ int len2; /* Other lengths */ off_t fpos; /* Free space offset */ unsigned int flen; /* Free space size */ int sfx; /* Shadow file index */ int len; /* Requested length */ cckd = dev->cckd_ext; sfx = cckd->sfn; len = *size; if (flags & CCKD_L2SPACE) { flags |= CCKD_SIZE_EXACT; len = *size = CCKD_L2TAB_SIZE; } cckd_trace (dev, "get_space len %d largest %d flags 0x%2.2x", len, cckd->cdevhdr[sfx].free_largest, flags); if (len <= CKDDASD_NULLTRK_FMTMAX) return 0; if (!cckd->free) cckd_read_fsp (dev); len2 = len + CCKD_FREEBLK_SIZE; /* Get space at the end if no space is large enough */ if (len2 > (int)cckd->cdevhdr[sfx].free_largest && len != (int)cckd->cdevhdr[sfx].free_largest) { cckd_get_space_atend: fpos = (off_t)cckd->cdevhdr[sfx].size; if ((fpos + len) > cckd->maxsize) { WRMSG (HHC00304, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx), (long long) (cckd->maxsize >> 20) + 1); return -1; } cckd->cdevhdr[sfx].size += len; cckd->cdevhdr[sfx].used += len; cckd_trace (dev, "get_space atend 0x"I64_FMTx" len %d",fpos, len); return fpos; } /* Scan free space chain */ fpos = (off_t)cckd->cdevhdr[sfx].free; for (i = cckd->free1st; i >= 0; i = cckd->free[i].next) { if (cckd->free[i].pending == 0 && (len2 <= (int)cckd->free[i].len || len == (int)cckd->free[i].len) && ((flags & CCKD_L2SPACE) || fpos >= cckd->l2bounds)) break; fpos = (off_t)cckd->free[i].pos; } /* This can happen if largest comes before l2bounds */ if (i < 0) goto cckd_get_space_atend; flen = cckd->free[i].len; p = cckd->free[i].prev; n = cckd->free[i].next; /* * If `CCKD_SIZE_ANY' bit is set and the left over space is small * enough, then use the entire free space */ if ((flags & CCKD_SIZE_ANY) && flen <= cckd->freemin) *size = (int)flen; /* Remove the new space from free space */ if (*size < (int)flen) { cckd->free[i].len -= *size; if (p >= 0) cckd->free[p].pos += *size; else cckd->cdevhdr[sfx].free += *size; } else { cckd->cdevhdr[sfx].free_number--; /* Remove the free space entry from the chain */ if (p >= 0) { cckd->free[p].pos = cckd->free[i].pos; cckd->free[p].next = n; } else { cckd->cdevhdr[sfx].free = cckd->free[i].pos; cckd->free1st = n; } if (n >= 0) cckd->free[n].prev = p; else cckd->freelast = p; /* Add entry to the available queue */ cckd->free[i].next = cckd->freeavail; cckd->freeavail = i; } /* Find the largest free space if we got the largest */ if (flen >= cckd->cdevhdr[sfx].free_largest) { int i; cckd->cdevhdr[sfx].free_largest = 0; for (i = cckd->free1st; i >= 0; i = cckd->free[i].next) if (cckd->free[i].len > cckd->cdevhdr[sfx].free_largest && cckd->free[i].pending == 0) cckd->cdevhdr[sfx].free_largest = cckd->free[i].len; } /* Update free space stats */ cckd->cdevhdr[sfx].used += len; cckd->cdevhdr[sfx].free_total -= len; cckd->cdevhdr[sfx].free_imbed += *size - len; cckd_trace (dev, "get_space found 0x"I64_FMTx" len %d size %d", fpos, len, *size); return fpos; } /* end function cckd_get_space */ /*-------------------------------------------------------------------*/ /* Release file space */ /*-------------------------------------------------------------------*/ void cckd_rel_space(DEVBLK *dev, off_t pos, int len, int size) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx; /* Shadow file index */ off_t ppos, npos; /* Prev/next free offsets */ int i, p, n; /* Free space indexes */ int pending; /* Calculated pending value */ int fsize = size; /* Free space size */ if (len <= CKDDASD_NULLTRK_FMTMAX || pos == 0 || pos == 0xffffffff) return; cckd = dev->cckd_ext; sfx = cckd->sfn; cckd_trace (dev, "rel_space offset 0x"I64_FMTx" len %d size %d", pos, len, size); if (!cckd->free) cckd_read_fsp (dev); // cckd_chk_space(dev); /* Scan free space chain */ ppos = -1; npos = cckd->cdevhdr[sfx].free; for (p = -1, n = cckd->free1st; n >= 0; n = cckd->free[n].next) { if (pos < npos) break; ppos = npos; npos = cckd->free[n].pos; p = n; } /* Calculate the `pending' value */ pending = cckdblk.freepend >= 0 ? cckdblk.freepend : 1 + (1 - cckdblk.fsync); /* If possible use previous adjacent free space otherwise get an available one */ if (p >= 0 && ppos + cckd->free[p].len == pos && cckd->free[p].pending == pending) { cckd->free[p].len += size; fsize = cckd->free[p].len; } else { /* Increase the size of the free space array if necessary */ if (cckd->freeavail < 0) { cckd->freeavail = cckd->freenbr; cckd->freenbr += 1024; cckd->free = realloc ( cckd->free, cckd->freenbr * CCKD_FREEBLK_ISIZE); for (i = cckd->freeavail; i < cckd->freenbr; i++) cckd->free[i].next = i + 1; cckd->free[i-1].next = -1; cckd->freemin = CCKD_FREE_MIN_SIZE + ((cckd->freenbr >> 10) * CCKD_FREE_MIN_INCR); } /* Get an available free space entry */ i = cckd->freeavail; cckd->freeavail = cckd->free[i].next; cckd->cdevhdr[sfx].free_number++; /* Update the new entry */ cckd->free[i].prev = p; cckd->free[i].next = n; cckd->free[i].len = size; cckd->free[i].pending = pending; /* Update the previous entry */ if (p >= 0) { cckd->free[i].pos = cckd->free[p].pos; cckd->free[p].pos = pos; cckd->free[p].next = i; } else { cckd->free[i].pos = cckd->cdevhdr[sfx].free; cckd->cdevhdr[sfx].free = pos; cckd->free1st = i; } /* Update the next entry */ if (n >= 0) cckd->free[n].prev = i; else cckd->freelast = i; } /* Update the free space statistics */ cckd->cdevhdr[sfx].used -= len; cckd->cdevhdr[sfx].free_total += len; cckd->cdevhdr[sfx].free_imbed -= size - len; if (!pending && (U32)fsize > cckd->cdevhdr[sfx].free_largest) cckd->cdevhdr[sfx].free_largest = (U32)fsize; // cckd_chk_space(dev); } /* end function cckd_rel_space */ /*-------------------------------------------------------------------*/ /* Flush pending free space */ /*-------------------------------------------------------------------*/ void cckd_flush_space(DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int p,i,n; /* Free free space indexes */ int sfx; /* Shadow file index */ U32 ppos, pos; /* Free space offsets */ cckd = dev->cckd_ext; sfx = cckd->sfn; cckd_trace (dev, "flush_space nbr %d",cckd->cdevhdr[sfx].free_number); /* Make sure the free space chain is built */ if (!cckd->free) cckd_read_fsp (dev); // cckd_chk_space(dev); if (cckd->cdevhdr[sfx].free_number == 0 || cckd->cdevhdr[sfx].free == 0) { cckd->cdevhdr[sfx].free_number = cckd->cdevhdr[sfx].free = 0; cckd->free1st = cckd->freelast = cckd->freeavail = -1; } pos = cckd->cdevhdr[sfx].free; ppos = p = -1; cckd->cdevhdr[sfx].free_number = cckd->cdevhdr[sfx].free_largest = 0; for (i = cckd->free1st; i >= 0; i = cckd->free[i].next) { /* Decrement the pending count */ if (cckd->free[i].pending) --cckd->free[i].pending; /* Combine adjacent free spaces */ while (pos + cckd->free[i].len == cckd->free[i].pos) { n = cckd->free[i].next; if (cckd->free[n].pending > cckd->free[i].pending + 1 || cckd->free[n].pending < cckd->free[i].pending) break; cckd->free[i].pos = cckd->free[n].pos; cckd->free[i].len += cckd->free[n].len; cckd->free[i].next = cckd->free[n].next; cckd->free[n].next = cckd->freeavail; cckd->freeavail = n; n = cckd->free[i].next; if (n >= 0) cckd->free[n].prev = i; } ppos = pos; pos = cckd->free[i].pos; cckd->cdevhdr[sfx].free_number++; if (cckd->free[i].len > cckd->cdevhdr[sfx].free_largest && !cckd->free[i].pending) cckd->cdevhdr[sfx].free_largest = cckd->free[i].len; p = i; } cckd->freelast = p; cckd_trace (dev, "rel_flush_space nbr %d (after merge)", cckd->cdevhdr[sfx].free_number); /* If the last free space is at the end of the file then release it */ if (p >= 0 && ppos + cckd->free[p].len == cckd->cdevhdr[sfx].size && !cckd->free[p].pending) { i = p; p = cckd->free[i].prev; cckd_trace (dev, "file[%d] rel_flush_space atend 0x"I64_FMTx" len %d", sfx, ppos, cckd->free[i].len); /* Remove the entry from the chain */ if (p >= 0) { cckd->free[p].pos = 0; cckd->free[p].next = -1; } else { cckd->cdevhdr[sfx].free = 0; cckd->free1st = -1; } cckd->freelast = p; /* Add the entry to the available chain */ cckd->free[i].next = cckd->freeavail; cckd->freeavail = i; /* Update the device header */ cckd->cdevhdr[sfx].size -= cckd->free[i].len; cckd->cdevhdr[sfx].free_total -= cckd->free[i].len; cckd->cdevhdr[sfx].free_number--; if (cckd->free[i].len >= cckd->cdevhdr[sfx].free_largest) { cckd->cdevhdr[sfx].free_largest = 0; for (i = cckd->free1st; i >= 0; i = cckd->free[i].next) if (cckd->free[i].len > cckd->cdevhdr[sfx].free_largest && cckd->free[i].pending == 0) cckd->cdevhdr[sfx].free_largest = cckd->free[i].len; } /* Truncate the file */ cckd_ftruncate (dev, sfx, (off_t)cckd->cdevhdr[sfx].size); } /* Release space at end of the file */ // cckd_chk_space(dev); } /* end function cckd_flush_space */ /*-------------------------------------------------------------------*/ /* Read compressed dasd header */ /*-------------------------------------------------------------------*/ int cckd_read_chdr (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx; /* File index */ cckd = dev->cckd_ext; sfx = cckd->sfn; cckd_trace (dev, "file[%d] read_chdr", sfx); memset(&cckd->cdevhdr[sfx], 0, CCKDDASD_DEVHDR_SIZE); /* Read the device header */ if (cckd_read (dev, sfx, CKDDASD_DEVHDR_SIZE, &cckd->cdevhdr[sfx], CCKDDASD_DEVHDR_SIZE) < 0) return -1; /* Check endian format */ cckd->swapend[sfx] = 0; if ((cckd->cdevhdr[sfx].options & CCKD_BIGENDIAN) != cckd_endian()) { if (cckd->open[sfx] == CCKD_OPEN_RW) { if (cckd_swapend (dev) < 0) return -1; cckd_swapend_chdr (&cckd->cdevhdr[sfx]); } else { cckd->swapend[sfx] = 1; cckd_swapend_chdr (&cckd->cdevhdr[sfx]); } } /* Set default null format */ if (cckd->cdevhdr[sfx].nullfmt > CKDDASD_NULLTRK_FMTMAX) cckd->cdevhdr[sfx].nullfmt = 0; if (cckd->cdevhdr[sfx].nullfmt == 0 && dev->oslinux && dev->devtype == 0x3390) cckd->cdevhdr[sfx].nullfmt = CKDDASD_NULLTRK_FMT2; if (cckd->cdevhdr[sfx].nullfmt == CKDDASD_NULLTRK_FMT2) dev->oslinux = 1; return 0; } /* end function cckd_read_chdr */ /*-------------------------------------------------------------------*/ /* Write compressed dasd header */ /*-------------------------------------------------------------------*/ int cckd_write_chdr (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx; /* File index */ cckd = dev->cckd_ext; sfx = cckd->sfn; cckd_trace (dev, "file[%d] write_chdr", sfx); /* Set version.release.modlvl */ cckd->cdevhdr[sfx].vrm[0] = CCKD_VERSION; cckd->cdevhdr[sfx].vrm[1] = CCKD_RELEASE; cckd->cdevhdr[sfx].vrm[2] = CCKD_MODLVL; if (cckd_write (dev, sfx, CCKD_DEVHDR_POS, &cckd->cdevhdr[sfx], CCKD_DEVHDR_SIZE) < 0) return -1; return 0; } /* end function cckd_write_chdr */ /*-------------------------------------------------------------------*/ /* Read the level 1 table */ /*-------------------------------------------------------------------*/ int cckd_read_l1 (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx; /* File index */ int len; /* Length of level 1 table */ int i; /* Work integer */ cckd = dev->cckd_ext; sfx = cckd->sfn; cckd_trace (dev, "file[%d] read_l1 offset 0x%"I64_FMT"x", sfx, (U64)CCKD_L1TAB_POS); /* Free the old level 1 table if it exists */ cckd->l1[sfx] = cckd_free (dev, "l1", cckd->l1[sfx]); /* Allocate the level 1 table */ len = cckd->cdevhdr[sfx].numl1tab * CCKD_L1ENT_SIZE; if ((cckd->l1[sfx] = cckd_malloc (dev, "l1", len)) == NULL) return -1; if ( sfx ) memset(cckd->l1[sfx], 0xFF, len); else memset(cckd->l1[sfx], 0, len); /* Read the level 1 table */ if (cckd_read (dev, sfx, CCKD_L1TAB_POS, cckd->l1[sfx], len) < 0) return -1; /* Fix endianess */ if (cckd->swapend[sfx]) cckd_swapend_l1 (cckd->l1[sfx], cckd->cdevhdr[sfx].numl1tab); /* Determine bounds */ cckd->l2bounds = CCKD_L1TAB_POS + len; for (i = 0; i < cckd->cdevhdr[sfx].numl1tab; i++) if (cckd->l1[sfx][i] != 0 && cckd->l1[sfx][i] != 0xffffffff) cckd->l2bounds += CCKD_L2TAB_SIZE; /* Check if all l2 tables are within bounds */ cckd->l2ok = 1; for (i = 0; i < cckd->cdevhdr[sfx].numl1tab && cckd->l2ok; i++) if (cckd->l1[sfx][i] != 0 && cckd->l1[sfx][i] != 0xffffffff) if (cckd->l1[sfx][i] > cckd->l2bounds - CCKD_L2TAB_SIZE) cckd->l2ok = 0; return 0; } /* end function cckd_read_l1 */ /*-------------------------------------------------------------------*/ /* Write the level 1 table */ /*-------------------------------------------------------------------*/ int cckd_write_l1 (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx; /* File index */ int len; /* Length of level 1 table */ cckd = dev->cckd_ext; sfx = cckd->sfn; len = cckd->cdevhdr[sfx].numl1tab * CCKD_L1ENT_SIZE; cckd_trace (dev, "file[%d] write_l1 0x%"I64_FMT"x len %d", sfx, (U64)CCKD_L1TAB_POS, len); if (cckd_write (dev, sfx, CCKD_L1TAB_POS, cckd->l1[sfx], len) < 0) return -1; return 0; } /* end function cckd_write_l1 */ /*-------------------------------------------------------------------*/ /* Update a level 1 table entry */ /*-------------------------------------------------------------------*/ int cckd_write_l1ent (DEVBLK *dev, int l1x) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx; /* File index */ off_t off; /* Offset to l1 entry */ cckd = dev->cckd_ext; sfx = cckd->sfn; off = (off_t)(CCKD_L1TAB_POS + l1x * CCKD_L1ENT_SIZE); cckd_trace (dev, "file[%d] write_l1ent[%d] , 0x"I64_FMTx, sfx, l1x, off); if (cckd_write (dev, sfx, off, &cckd->l1[sfx][l1x], CCKD_L1ENT_SIZE) < 0) return -1; return 0; } /* end function cckd_write_l1ent */ /*-------------------------------------------------------------------*/ /* Initial read */ /*-------------------------------------------------------------------*/ int cckd_read_init (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx; /* File index */ CKDDASD_DEVHDR devhdr; /* Device header */ cckd = dev->cckd_ext; sfx = cckd->sfn; cckd_trace (dev, "file[%d] read_init", sfx); /* Read the device header */ if (cckd_read (dev, sfx, 0, &devhdr, CKDDASD_DEVHDR_SIZE) < 0) return -1; /* Check the device hdr */ if (sfx == 0 && memcmp (&devhdr.devid, "CKD_C370", 8) == 0) cckd->ckddasd = 1; else if (sfx == 0 && memcmp (&devhdr.devid, "FBA_C370", 8) == 0) cckd->fbadasd = 1; else if (!(sfx && memcmp (&devhdr.devid, "CKD_S370", 8) == 0 && cckd->ckddasd) && !(sfx && memcmp (&devhdr.devid, "FBA_S370", 8) == 0 && cckd->fbadasd)) { WRMSG (HHC00305, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, sfx, cckd_sf_name (dev, sfx)); return -1; } /* Read the compressed header */ if (cckd_read_chdr (dev) < 0) return -1; /* Read the level 1 table */ if (cckd_read_l1 (dev) < 0) return -1; return 0; } /* end function cckd_read_init */ /*-------------------------------------------------------------------*/ /* Read free space */ /*-------------------------------------------------------------------*/ int cckd_read_fsp (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ off_t fpos; /* Free space offset */ int sfx; /* File index */ int i; /* Index */ CCKD_FREEBLK freeblk; /* First freeblk read */ cckd = dev->cckd_ext; sfx = cckd->sfn; cckd_trace (dev, "file[%d] read_fsp number %d", sfx, cckd->cdevhdr[sfx].free_number); cckd->free = cckd_free (dev, "free", cckd->free); cckd->free1st = cckd->freelast = cckd->freeavail = -1; /* Get storage for the internal free space chain * in a multiple of 1024 entries */ cckd->freenbr = (cckd->cdevhdr[sfx].free_number + 1023) & ~0x3FF; if (cckd->freenbr) if ((cckd->free = cckd_calloc (dev, "free", cckd->freenbr, CCKD_FREEBLK_ISIZE)) == NULL) return -1; /* Build the doubly linked internal free space chain */ if (cckd->cdevhdr[sfx].free_number) { cckd->free1st = 0; /* Read the first freeblk to determine old/new format */ fpos = (off_t)cckd->cdevhdr[sfx].free; if (cckd_read (dev, sfx, fpos, &freeblk, CCKD_FREEBLK_SIZE) < 0) return -1; if (memcmp(&freeblk, "FREE_BLK", 8) == 0) { /* new format free space */ CCKD_FREEBLK *fsp; U32 ofree = cckd->cdevhdr[sfx].free; int n = cckd->cdevhdr[sfx].free_number * CCKD_FREEBLK_SIZE; if ((fsp = cckd_malloc (dev, "fsp", n)) == NULL) return -1; fpos += CCKD_FREEBLK_SIZE; if (cckd_read (dev, sfx, fpos, fsp, n) < 0) return -1; for (i = 0; i < cckd->cdevhdr[sfx].free_number; i++) { if (i == 0) cckd->cdevhdr[sfx].free = fsp[i].pos; else cckd->free[i-1].pos = fsp[i].pos; cckd->free[i].pos = 0; cckd->free[i].len = fsp[i].len; cckd->free[i].prev = i - 1; cckd->free[i].next = i + 1; } cckd->free[i-1].next = -1; cckd->freelast = i-1; fsp = cckd_free (dev, "fsp", fsp); /* truncate if new format free space was at the end */ if (ofree == cckd->cdevhdr[sfx].size) { fpos = (off_t)cckd->cdevhdr[sfx].size; cckd_ftruncate(dev, sfx, fpos); } } /* new format free space */ else { /* old format free space */ fpos = (off_t)cckd->cdevhdr[sfx].free; for (i = 0; i < cckd->cdevhdr[sfx].free_number; i++) { if (cckd_read (dev, sfx, fpos, &cckd->free[i], CCKD_FREEBLK_SIZE) < 0) return -1; cckd->free[i].prev = i - 1; cckd->free[i].next = i + 1; fpos = (off_t)cckd->free[i].pos; } cckd->free[i-1].next = -1; cckd->freelast = i-1; } /* old format free space */ } /* if (cckd->cdevhdr[sfx].free_number) */ /* Build singly linked chain of available free space entries */ if (cckd->cdevhdr[sfx].free_number < cckd->freenbr) { cckd->freeavail = cckd->cdevhdr[sfx].free_number; for (i = cckd->freeavail; i < cckd->freenbr; i++) cckd->free[i].next = i + 1; cckd->free[i-1].next = -1; } /* Set minimum free space size */ cckd->freemin = CCKD_FREE_MIN_SIZE + ((cckd->freenbr >> 10) * CCKD_FREE_MIN_INCR); return 0; } /* end function cckd_read_fsp */ /*-------------------------------------------------------------------*/ /* Write the free space */ /*-------------------------------------------------------------------*/ int cckd_write_fsp (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ off_t fpos; /* Free space offset */ U32 ppos; /* Previous free space offset*/ int sfx; /* File index */ int i, j, n; /* Work variables */ int rc; /* Return code */ CCKD_FREEBLK *fsp = NULL; /* -> new format free space */ cckd = dev->cckd_ext; sfx = cckd->sfn; if (cckd->free == NULL) return 0; cckd_trace (dev, "file[%d] write_fsp number %d", sfx, cckd->cdevhdr[sfx].free_number); /* get rid of pending free space */ for (i = 0; i < CCKD_MAX_FREEPEND; i++) cckd_flush_space(dev); /* sanity checks */ if (cckd->cdevhdr[sfx].free_number == 0 || cckd->cdevhdr[sfx].free == 0) { cckd->cdevhdr[sfx].free_number = cckd->cdevhdr[sfx].free = 0; cckd->free1st = cckd->freelast = cckd->freeavail = -1; } /* Write any free spaces */ if (cckd->cdevhdr[sfx].free) { /* size needed for new format free space */ n = (cckd->cdevhdr[sfx].free_number+1) * CCKD_FREEBLK_SIZE; /* look for existing free space to fit new format free space */ fpos = 0; for (i = cckd->free1st; i >= 0; i = cckd->free[i].next) if (n <= (int)cckd->free[i].len) break; if (i >= 0) fpos = cckd->free[i].prev < 0 ? (off_t)cckd->cdevhdr[sfx].free : (off_t)cckd->free[cckd->free[i].prev].pos; /* if no applicable space see if we can append to the file */ if (fpos == 0 && cckd->maxsize - cckd->cdevhdr[sfx].size >= n) fpos = (off_t)cckd->cdevhdr[sfx].size; if (fpos && (fsp = cckd_malloc (dev, "fsp", n)) == NULL) fpos = 0; if (fpos) { /* New format free space */ memcpy (&fsp[0], "FREE_BLK", 8); ppos = cckd->cdevhdr[sfx].free; for (i = cckd->free1st, j = 1; i >= 0; i = cckd->free[i].next) { fsp[j].pos = ppos; fsp[j++].len = cckd->free[i].len; ppos = cckd->free[i].pos; } rc = cckd_write (dev, sfx, fpos, fsp, n); fsp = cckd_free (dev, "fsp", fsp); if (rc < 0) return -1; cckd->cdevhdr[sfx].free = (U32)fpos; } /* new format free space */ else { /* Old format free space */ for (i = cckd->free1st; i >= 0; i = cckd->free[i].next) { if (cckd_write (dev, sfx, fpos, &cckd->free[i], CCKD_FREEBLK_SIZE) < 0) return -1; fpos = (off_t)cckd->free[i].pos; } } /* old format free space */ } /* if (cckd->cdevhdr[sfx].free) */ /* Free the free space array */ cckd->free = cckd_free (dev, "free", cckd->free); cckd->freenbr = 0; cckd->free1st = cckd->freelast = cckd->freeavail = -1; return 0; } /* end function cckd_write_fsp */ /*-------------------------------------------------------------------*/ /* Read a new level 2 table */ /*-------------------------------------------------------------------*/ int cckd_read_l2 (DEVBLK *dev, int sfx, int l1x) { CCKDDASD_EXT *cckd; /* -> cckd extension */ off_t off; /* L2 file offset */ int fnd; /* Found cache */ int lru; /* Oldest available cache */ CCKD_L2ENT *buf; /* -> Cache buffer */ int i; /* Loop index */ int nullfmt; /* Null track format */ cckd = dev->cckd_ext; nullfmt = cckd->cdevhdr[cckd->sfn].nullfmt; cckd_trace (dev, "file[%d] read_l2 %d active %d %d %d", sfx, l1x, cckd->sfx, cckd->l1x, cckd->l2active); /* Return if table is already active */ if (sfx == cckd->sfx && l1x == cckd->l1x) return 0; cache_lock(CACHE_L2); /* Inactivate the previous entry */ if (cckd->l2active >= 0) cache_setflag(CACHE_L2, cckd->l2active, ~L2_CACHE_ACTIVE, 0); cckd->l2 = NULL; cckd->l2active = cckd->sfx = cckd->l1x = -1; /* scan the cache array for the l2tab */ fnd = cache_lookup (CACHE_L2, L2_CACHE_SETKEY(sfx, dev->devnum, l1x), &lru); /* check for level 2 cache hit */ if (fnd >= 0) { cckd_trace (dev, "l2[%d,%d] cache[%d] hit", sfx, l1x, fnd); cache_setflag (CACHE_L2, fnd, 0, L2_CACHE_ACTIVE); cache_setage (CACHE_L2, fnd); cckdblk.stats_l2cachehits++; cache_unlock (CACHE_L2); cckd->sfx = sfx; cckd->l1x = l1x; cckd->l2 = cache_getbuf(CACHE_L2, fnd, 0); cckd->l2active = fnd; return 1; } cckd_trace (dev, "l2[%d,%d] cache[%d] miss", sfx, l1x, lru); /* Steal an entry if all are busy */ if (lru < 0) lru = cckd_steal_l2(); /* Make the entry active */ cache_setkey (CACHE_L2, lru, L2_CACHE_SETKEY(sfx, dev->devnum, l1x)); cache_setflag (CACHE_L2, lru, 0, L2_CACHE_ACTIVE); cache_setage (CACHE_L2, lru); buf = cache_getbuf(CACHE_L2, lru, CCKD_L2TAB_SIZE); cckdblk.stats_l2cachemisses++; cache_unlock (CACHE_L2); if (buf == NULL) return -1; /* Check for null table */ if (cckd->l1[sfx][l1x] == 0) { memset(buf, 0, CCKD_L2TAB_SIZE); if (nullfmt) for (i = 0; i < 256; i++) buf[i].len = buf[i].size = nullfmt; cckd_trace (dev, "l2[%d,%d] cache[%d] null fmt[%d]", sfx, l1x, lru, nullfmt); } else if (cckd->l1[sfx][l1x] == 0xffffffff) { memset(buf, 0xff, CCKD_L2TAB_SIZE); cckd_trace (dev, "l2[%d,%d] cache[%d] null 0xff", sfx, l1x, lru); } /* Read the new level 2 table */ else { off = (off_t)cckd->l1[sfx][l1x]; if (cckd_read (dev, sfx, off, buf, CCKD_L2TAB_SIZE) < 0) { cache_lock(CACHE_L2); cache_setflag(CACHE_L2, lru, 0, 0); cache_unlock(CACHE_L2); return -1; } if (cckd->swapend[sfx]) cckd_swapend_l2 (buf); cckd_trace (dev, "file[%d] cache[%d] l2[%d] read offset 0x"I32_FMTx, sfx, lru, l1x, cckd->l1[sfx][l1x]); cckd->l2reads[sfx]++; cckd->totl2reads++; cckdblk.stats_l2reads++; } cckd->sfx = sfx; cckd->l1x = l1x; cckd->l2 = buf; cckd->l2active = lru; return 0; } /* end function cckd_read_l2 */ /*-------------------------------------------------------------------*/ /* Purge all l2tab cache entries for a given device */ /*-------------------------------------------------------------------*/ void cckd_purge_l2 (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ cckd = dev->cckd_ext; cckd_trace (dev, "purge_l2%s", ""); cache_lock (CACHE_L2); cckd->l2active = cckd->sfx = cckd->l1x = -1; cckd->l2 = NULL; cache_scan (CACHE_L2, cckd_purge_l2_scan, dev); cache_unlock (CACHE_L2); } int cckd_purge_l2_scan (int *answer, int ix, int i, void *data) { U16 sfx; /* Cached suffix */ U16 devnum; /* Cached device number */ U32 l1x; /* Cached level 1 index */ DEVBLK *dev = data; /* -> device block */ UNREFERENCED(answer); L2_CACHE_GETKEY(i, sfx, devnum, l1x); if (dev == NULL || devnum == dev->devnum) { cckd_trace (dev, "purge l2cache[%d] %4.4X sfx %d ix %d purged", i, devnum, sfx, l1x); cache_release(ix, i, 0); } return 0; } /*-------------------------------------------------------------------*/ /* Steal an l2tab cache entry */ /*-------------------------------------------------------------------*/ int cckd_steal_l2 () { DEVBLK *dev; /* -> device block */ CCKDDASD_EXT *cckd; /* -> cckd extension */ int i; /* Stolen cache index */ U16 sfx; /* Cached suffix */ U16 devnum; /* Cached device number */ U32 l1x; /* Cached level 1 index */ i = cache_scan (CACHE_L2, cckd_steal_l2_scan, NULL); L2_CACHE_GETKEY(i, sfx, devnum, l1x); dev = cckd_find_device_by_devnum(devnum); cckd = dev->cckd_ext; cckd->l2active = cckd->sfx = cckd->l1x = -1; cckd->l2 = NULL; cache_release(CACHE_L2, i, 0); return i; } int cckd_steal_l2_scan (int *answer, int ix, int i, void *data) { UNREFERENCED(data); if (*answer < 0) *answer = i; else if (cache_getage(ix, i) < cache_getage(ix, *answer)) *answer = i; return 0; } /*-------------------------------------------------------------------*/ /* Write the current level 2 table */ /*-------------------------------------------------------------------*/ int cckd_write_l2 (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx,l1x; /* Lookup table indices */ off_t off, old_off; /* New/old L2 file offsets */ int size = CCKD_L2TAB_SIZE; /* L2 table size */ int fix; /* Null format type */ cckd = dev->cckd_ext; sfx = cckd->sfn; l1x = cckd->l1x; fix = cckd->cdevhdr[sfx].nullfmt; cckd->l2ok = 0; cckd_trace (dev, "file[%d] write_l2 %d", sfx, l1x); if (sfx < 0 || l1x < 0) return -1; old_off = (off_t)cckd->l1[sfx][l1x]; if (cckd->l1[sfx][l1x] == 0 || cckd->l1[sfx][l1x] == 0xffffffff) cckd->l2bounds += CCKD_L2TAB_SIZE; /* Write the L2 table if it's not empty */ if (memcmp(cckd->l2, &empty_l2[fix], CCKD_L2TAB_SIZE)) { if ((off = cckd_get_space (dev, &size, CCKD_L2SPACE)) < 0) return -1; if (cckd_write (dev, sfx, off, cckd->l2, CCKD_L2TAB_SIZE) < 0) return -1; } else { off = 0; cckd->l2bounds -= CCKD_L2TAB_SIZE; } /* Free the old L2 space */ cckd_rel_space (dev, old_off, CCKD_L2TAB_SIZE, CCKD_L2TAB_SIZE); /* Update level 1 table */ cckd->l1[sfx][l1x] = (U32)off; if (cckd_write_l1ent (dev, l1x) < 0) return -1; return 0; } /* end function cckd_write_l2 */ /*-------------------------------------------------------------------*/ /* Return a level 2 entry */ /*-------------------------------------------------------------------*/ int cckd_read_l2ent (DEVBLK *dev, CCKD_L2ENT *l2, int trk) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx,l1x,l2x; /* Lookup table indices */ cckd = dev->cckd_ext; l1x = trk >> 8; l2x = trk & 0xff; if (l2 != NULL) l2->pos = l2->len = l2->size = 0; for (sfx = cckd->sfn; sfx >= 0; sfx--) { cckd_trace (dev, "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%x", sfx, l1x, l2x, trk, cckd->l1[sfx][l1x]); /* Continue if l2 table not in this file */ if (cckd->l1[sfx][l1x] == 0xffffffff) continue; /* Read l2 table from this file */ if (cckd_read_l2 (dev, sfx, l1x) < 0) return -1; /* Exit loop if track is in this file */ if (cckd->l2[l2x].pos != 0xffffffff) break; } cckd_trace (dev, "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%x %d %d", sfx, l1x, l2x, trk, sfx >= 0 ? cckd->l2[l2x].pos : 0, sfx >= 0 ? cckd->l2[l2x].len : 0, sfx >= 0 ? cckd->l2[l2x].size : 0); if (l2 != NULL && sfx >= 0) { l2->pos = cckd->l2[l2x].pos; l2->len = cckd->l2[l2x].len; l2->size = cckd->l2[l2x].size; } return sfx; } /* end function cckd_read_l2ent */ /*-------------------------------------------------------------------*/ /* Update a level 2 entry */ /*-------------------------------------------------------------------*/ int cckd_write_l2ent (DEVBLK *dev, CCKD_L2ENT *l2, int trk) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx,l1x,l2x; /* Lookup table indices */ off_t off; /* L2 entry offset */ cckd = dev->cckd_ext; /* Error return if no available level 2 table */ if (!cckd->l2) return -1; sfx = cckd->sfn; l1x = trk >> 8; l2x = trk & 0xff; /* Copy the new entry if passed */ if (l2) memcpy (&cckd->l2[l2x], l2, CCKD_L2ENT_SIZE); cckd_trace (dev, "file[%d] l2[%d,%d] trk[%d] write_l2ent 0x%x %d %d", sfx, l1x, l2x, trk, cckd->l2[l2x].pos, cckd->l2[l2x].len, cckd->l2[l2x].size); /* If no level 2 table for this file, then write a new one */ if (cckd->l1[sfx][l1x] == 0 || cckd->l1[sfx][l1x] == 0xffffffff) return cckd_write_l2 (dev); /* Write the level 2 table entry */ off = (off_t)(cckd->l1[sfx][l1x] + l2x * CCKD_L2ENT_SIZE); if (cckd_write (dev, sfx, off, &cckd->l2[l2x], CCKD_L2ENT_SIZE) < 0) return -1; return 0; } /* end function cckd_write_l2ent */ /*-------------------------------------------------------------------*/ /* Read a track image */ /*-------------------------------------------------------------------*/ int cckd_read_trkimg (DEVBLK *dev, BYTE *buf, int trk, BYTE *unitstat) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ int sfx; /* File index */ CCKD_L2ENT l2; /* Level 2 entry */ cckd = dev->cckd_ext; cckd_trace (dev, "trk[%d] read_trkimg", trk); /* Read level 2 entry for the track */ if ((sfx = cckd_read_l2ent (dev, &l2, trk)) < 0) goto cckd_read_trkimg_error; /* Read the track image or build a null track image */ if (l2.pos != 0) { rc = cckd_read (dev, sfx, (off_t)l2.pos, buf, (size_t)l2.len); if (rc < 0) goto cckd_read_trkimg_error; cckd->reads[sfx]++; cckd->totreads++; cckdblk.stats_reads++; cckdblk.stats_readbytes += rc; if (cckd->notnull == 0 && trk > 1) cckd->notnull = 1; } else rc = cckd_null_trk (dev, buf, trk, l2.len); /* Validate the track image */ if (cckd_cchh (dev, buf, trk) < 0) goto cckd_read_trkimg_error; return rc; cckd_read_trkimg_error: if (unitstat) { ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0); *unitstat = CSW_CE | CSW_DE | CSW_UC; } return cckd_null_trk (dev, buf, trk, 0); } /* end function cckd_read_trkimg */ /*-------------------------------------------------------------------*/ /* Write a track image */ /*-------------------------------------------------------------------*/ int cckd_write_trkimg (DEVBLK *dev, BYTE *buf, int len, int trk, int flags) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ off_t off; /* File offset */ CCKD_L2ENT l2, oldl2; /* Level 2 entries */ int sfx,l1x,l2x; /* Lookup table indices */ int after = 0; /* 1=New track after old */ int size; /* Size of new track */ cckd = dev->cckd_ext; sfx = cckd->sfn; l1x = trk >> 8; l2x = trk & 0xff; cckd_trace (dev, "file[%d] trk[%d] write_trkimg len %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x", sfx, trk, len, buf, buf[0], buf[1], buf[2], buf[3], buf[4]); /* Validate the new track image */ if (cckd_cchh (dev, buf, trk) < 0) return -1; /* Get the level 2 table for the track in the active file */ if (cckd_read_l2 (dev, sfx, l1x) < 0) return -1; /* Save the level 2 entry for the track */ oldl2.pos = cckd->l2[l2x].pos; oldl2.len = cckd->l2[l2x].len; oldl2.size = cckd->l2[l2x].size; cckd_trace (dev, "file[%d] trk[%d] write_trkimg oldl2 0x%x %d %d", sfx, trk, oldl2.pos,oldl2.len,oldl2.size); /* Check if writing a null track */ len = cckd_check_null_trk(dev, buf, trk, len); if (len > CKDDASD_NULLTRK_FMTMAX) { /* Get space for the track image */ size = len; if ((off = cckd_get_space (dev, &size, flags)) < 0) return -1; l2.pos = (U32)off; l2.len = (U16)len; l2.size = (U16)size; if (oldl2.pos != 0 && oldl2.pos != 0xffffffff && oldl2.pos < l2.pos) after = 1; /* Write the track image */ if ((rc = cckd_write (dev, sfx, off, buf, len)) < 0) return -1; cckd->writes[sfx]++; cckd->totwrites++; cckdblk.stats_writes++; cckdblk.stats_writebytes += rc; } else { l2.pos = 0; l2.len = l2.size = (U16)len; } /* Update the level 2 entry */ if (cckd_write_l2ent (dev, &l2, trk) < 0) return -1; /* Release the previous space */ cckd_rel_space (dev, (off_t)oldl2.pos, (int)oldl2.len, (int)oldl2.size); /* `after' is 1 if the new offset is after the old offset */ return after; } /* end function cckd_write_trkimg */ /*-------------------------------------------------------------------*/ /* Harden the file */ /*-------------------------------------------------------------------*/ int cckd_harden(DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc=0; /* Return code */ cckd = dev->cckd_ext; if ((dev->ckdrdonly && cckd->sfn == 0) || cckd->open[cckd->sfn] != CCKD_OPEN_RW) return 0; cckd_trace (dev, "file[%d] harden", cckd->sfn); /* Write the compressed device header */ if (cckd_write_chdr (dev) < 0) rc = -1; /* Write the level 1 table */ if (cckd_write_l1 (dev) < 0) rc = -1; /* Write the free space chain */ if (cckd_write_fsp (dev) < 0) rc = -1; /* Re-write the compressed device header */ cckd->cdevhdr[cckd->sfn].options &= ~CCKD_OPENED; if (cckd_write_chdr (dev) < 0) rc = -1; if (cckdblk.fsync) fdatasync (cckd->fd[cckd->sfn]); return rc; } /* cckd_harden */ /*-------------------------------------------------------------------*/ /* Return length of an uncompressed track image */ /*-------------------------------------------------------------------*/ int cckd_trklen (DEVBLK *dev, BYTE *buf) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int size; /* Track size */ cckd = dev->cckd_ext; if (cckd->fbadasd) return CKDDASD_TRKHDR_SIZE + CFBA_BLOCK_SIZE; for (size = CKDDASD_TRKHDR_SIZE; memcmp (&buf[size], &eighthexFF, 8) != 0; ) { if (size > dev->ckdtrksz) break; /* add length of count, key, and data fields */ size += CKDDASD_RECHDR_SIZE + buf[size+5] + (buf[size+6] << 8) + buf[size+7]; } /* add length for end-of-track indicator */ size += CKDDASD_RECHDR_SIZE; /* check for missing end-of-track indicator */ if (size > dev->ckdtrksz || memcmp (&buf[size-CKDDASD_RECHDR_SIZE], &eighthexFF, 8) != 0) { WRMSG (HHC00306, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn), buf[0], buf[1], buf[2], buf[3], buf[4]); size = -1; } return size; } /*-------------------------------------------------------------------*/ /* Build a null track */ /*-------------------------------------------------------------------*/ int cckd_null_trk(DEVBLK *dev, BYTE *buf, int trk, int nullfmt) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int i; /* Loop counter */ CKDDASD_TRKHDR *trkhdr; /* -> Track header */ CKDDASD_RECHDR *rechdr; /* -> Record header */ U32 cyl; /* Cylinder number */ U32 head; /* Head number */ BYTE r; /* Record number */ BYTE *pos; /* -> Next position in buffer*/ int len; /* Length of null track */ cckd = dev->cckd_ext; if (nullfmt < 0 || nullfmt > CKDDASD_NULLTRK_FMTMAX) nullfmt = cckd->cdevhdr[cckd->sfn].nullfmt; // FIXME // Compatibility check for nullfmt bug and linux -- 18 May 2005 // Remove at some reasonable date in the future else if (nullfmt == 0 && cckd->cdevhdr[cckd->sfn].nullfmt == CKDDASD_NULLTRK_FMT2) nullfmt = CKDDASD_NULLTRK_FMT2; if (cckd->ckddasd) { /* cylinder and head calculations */ cyl = trk / dev->ckdheads; head = trk % dev->ckdheads; /* Build the track header */ trkhdr = (CKDDASD_TRKHDR*)buf; trkhdr->bin = 0; store_hw(&trkhdr->cyl, cyl); store_hw(&trkhdr->head, head); pos = buf + CKDDASD_TRKHDR_SIZE; /* Build record zero */ r = 0; rechdr = (CKDDASD_RECHDR*)pos; pos += CKDDASD_RECHDR_SIZE; store_hw(&rechdr->cyl, cyl); store_hw(&rechdr->head, head); rechdr->rec = r; rechdr->klen = 0; store_hw(&rechdr->dlen, 8); memset( pos, 0, 8 ); pos += 8; r++; /* Specific null track formatting */ if (nullfmt == CKDDASD_NULLTRK_FMT0) { rechdr = (CKDDASD_RECHDR*)pos; pos += CKDDASD_RECHDR_SIZE; store_hw(&rechdr->cyl, cyl); store_hw(&rechdr->head, head); rechdr->rec = r; rechdr->klen = 0; store_hw(&rechdr->dlen, 0); r++; } else if (nullfmt == CKDDASD_NULLTRK_FMT2) { for (i = 0; i < 12; i++) { rechdr = (CKDDASD_RECHDR*)pos; pos += CKDDASD_RECHDR_SIZE; store_hw(&rechdr->cyl, cyl); store_hw(&rechdr->head, head); rechdr->rec = r; rechdr->klen = 0; store_hw(&rechdr->dlen, 4096); r++; memset( pos, 0, 4096 ); pos += 4096; } } /* Build the end of track marker */ memcpy (pos, eighthexFF, 8); pos += 8; len = (int)(pos - buf); } else { memset( buf, 0, CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE ); store_fw(buf+1, trk); len = CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE; } cckd_trace (dev, "null_trk %s %d format %d size %d", cckd->ckddasd ? "trk" : "blkgrp", trk, nullfmt, len); return len; } /* end function cckd_null_trk */ /*-------------------------------------------------------------------*/ /* Return a number 0 .. CKDDASD_NULLTRK_FMTMAX if track is null */ /* else return the original length */ /*-------------------------------------------------------------------*/ int cckd_check_null_trk (DEVBLK *dev, BYTE *buf, int trk, int len) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ BYTE buf2[65536]; /* Null track buffer */ cckd = dev->cckd_ext; rc = len; if (len == CKDDASD_NULLTRK_SIZE0) rc = CKDDASD_NULLTRK_FMT0; else if (len == CKDDASD_NULLTRK_SIZE1) rc = CKDDASD_NULLTRK_FMT1; else if (len == CKDDASD_NULLTRK_SIZE2 && dev->oslinux && (!cckd->notnull || cckdblk.linuxnull)) { cckd_null_trk (dev, buf2, trk, 0); if (memcmp(buf, buf2, len) == 0) rc = CKDDASD_NULLTRK_FMT2; } return rc; } /*-------------------------------------------------------------------*/ /* Verify a track/block header and return track/block number */ /*-------------------------------------------------------------------*/ int cckd_cchh (DEVBLK *dev, BYTE *buf, int trk) { CCKDDASD_EXT *cckd; /* -> cckd extension */ U16 cyl; /* Cylinder */ U16 head; /* Head */ int t; /* Calculated track */ BYTE badcomp=0; /* 1=Unsupported compression */ static char *comp[] = {"none", "zlib", "bzip2"}; cckd = dev->cckd_ext; /* CKD dasd header verification */ if (cckd->ckddasd) { cyl = fetch_hw (buf + 1); head = fetch_hw (buf + 3); t = cyl * dev->ckdheads + head; if (cyl < dev->ckdcyls && head < dev->ckdheads && (trk == -1 || t == trk)) { if (buf[0] & ~cckdblk.comps) { if (buf[0] & ~CCKD_COMPRESS_MASK) { if (cckdblk.bytemsgs++ < 10) WRMSG (HHC00307, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn), t, buf[0],buf[1],buf[2],buf[3],buf[4]); buf[0] &= CCKD_COMPRESS_MASK; } } if (buf[0] & ~cckdblk.comps) badcomp = 1; else return t; } } /* FBA dasd header verification */ else { t = fetch_fw (buf + 1); if (t < dev->fbanumblk && (trk == -1 || t == trk)) { if (buf[0] & ~cckdblk.comps) { if (buf[0] & ~CCKD_COMPRESS_MASK) { WRMSG (HHC00308, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn), t, buf[0],buf[1],buf[2],buf[3],buf[4]); buf[0] &= CCKD_COMPRESS_MASK; } } if (buf[0] & ~cckdblk.comps) badcomp = 1; else return t; } } if (badcomp) { WRMSG (HHC00309, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn), cckd->ckddasd ? "trk" : "blk", cckd->ckddasd ? "trk" : "blk", t, comp[buf[0]]); } else { WRMSG (HHC00310, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn), cckd->ckddasd ? "trk" : "blk", cckd->ckddasd ? "trk" : "blk", trk, buf, buf[0], buf[1], buf[2], buf[3], buf[4]); cckd_print_itrace (); } return -1; } /* end function cckd_cchh */ /*-------------------------------------------------------------------*/ /* Validate a track image */ /*-------------------------------------------------------------------*/ int cckd_validate (DEVBLK *dev, BYTE *buf, int trk, int len) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int cyl; /* Cylinder */ int head; /* Head */ char cchh[4],cchh2[4]; /* Cyl, head big-endian */ int r; /* Record number */ int sz; /* Track size */ int vlen; /* Validation length */ int kl,dl; /* Key/Data lengths */ cckd = dev->cckd_ext; if (buf == NULL || len < 0) return -1; cckd_trace (dev, "validating %s %d len %d %2.2x%2.2x%2.2x%2.2x%2.2x " "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", cckd->ckddasd ? "trk" : "blkgrp", trk, len, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12]); /* FBA dasd check */ if (cckd->fbadasd) { if (len == CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE || len == 0) return len; cckd_trace (dev, "validation failed: bad length%s",""); return -1; } /* cylinder and head calculations */ cyl = trk / dev->ckdheads; head = trk % dev->ckdheads; cchh[0] = cyl >> 8; cchh[1] = cyl & 0xFF; cchh[2] = head >> 8; cchh[3] = head & 0xFF; /* validate record 0 */ memcpy (cchh2, &buf[5], 4); cchh2[0] &= 0x7f; /* fix for ovflow */ if (/* memcmp (cchh, cchh2, 4) != 0 || */ buf[9] != 0 || buf[10] != 0 || buf[11] != 0 || buf[12] != 8) { cckd_trace (dev, "validation failed: bad r0%s",""); return -1; } /* validate records 1 thru n */ vlen = len > 0 ? len : dev->ckdtrksz; for (r = 1, sz = 21; sz + 8 <= vlen; sz += 8 + kl + dl, r++) { if (memcmp (&buf[sz], eighthexFF, 8) == 0) break; kl = buf[sz+5]; dl = buf[sz+6] * 256 + buf[sz+7]; /* fix for track overflow bit */ memcpy (cchh2, &buf[sz], 4); cchh2[0] &= 0x7f; /* fix for funny formatted vm disks */ /* if (r == 1) memcpy (cchh, cchh2, 4); */ if (/*memcmp (cchh, cchh2, 4) != 0 ||*/ buf[sz+4] == 0 || sz + 8 + kl + dl >= vlen) { cckd_trace (dev, "validation failed: bad r%d " "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", r, buf[sz], buf[sz+1], buf[sz+2], buf[sz+3], buf[sz+4], buf[sz+5], buf[sz+6], buf[sz+7]); return -1; } } sz += 8; if ((sz != len && len > 0) || sz > vlen) { cckd_trace (dev, "validation failed: no eot%s",""); return -1; } return sz; } /* end function cckd_validate */ /*-------------------------------------------------------------------*/ /* Return shadow file name */ /*-------------------------------------------------------------------*/ char *cckd_sf_name (DEVBLK *dev, int sfx) { /* Return base file name if index is 0 */ if (sfx == 0) return dev->filename; /* Error if no shadow file name specified or number exceeded */ if (dev->dasdsfn == NULL || sfx > CCKD_MAX_SF) return NULL; /* Set the suffix character in the shadow file name */ if (sfx > 0) *dev->dasdsfx = '0' + sfx; else *dev->dasdsfx = '*'; return dev->dasdsfn; } /* end function cckd_sf_name */ /*-------------------------------------------------------------------*/ /* Initialize shadow files */ /*-------------------------------------------------------------------*/ int cckd_sf_init (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ int i; /* Index */ struct stat st; /* stat() buffer */ char pathname[MAX_PATH]; /* file path in host format */ cckd = dev->cckd_ext; /* return if no shadow files */ if (dev->dasdsfn == NULL) return 0; #if 1 /* Check for shadow file name collision */ for (i = 1; i <= CCKD_MAX_SF && dev->dasdsfn != NULL; i++) { DEVBLK *dev2; CCKDDASD_EXT *cckd2; int j; for (dev2 = cckdblk.dev1st; dev2; dev2 = cckd2->devnext) { cckd2 = dev2->cckd_ext; if (dev2 == dev) continue; for (j = 0; j <= CCKD_MAX_SF && dev2->dasdsfn != NULL; j++) { if (strcmp (cckd_sf_name(dev, i),cckd_sf_name(dev2, j)) == 0) { WRMSG (HHC00311, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, i, cckd_sf_name(dev, i), SSID_TO_LCSS(dev2->ssid), dev2->devnum, j, cckd_sf_name(dev2, j)); return -1; } } } } #endif /* open all existing shadow files */ for (cckd->sfn = 1; cckd->sfn <= CCKD_MAX_SF; cckd->sfn++) { hostpath(pathname, cckd_sf_name (dev, cckd->sfn), sizeof(pathname)); if (stat (pathname, &st) < 0) break; /* Try to open the shadow file read-write then read-only */ if (cckd_open (dev, cckd->sfn, O_RDWR|O_BINARY, 1) < 0) if (cckd_open (dev, cckd->sfn, O_RDONLY|O_BINARY, 0) < 0) break; /* Call the chkdsk function */ rc = cckd_chkdsk (dev, 0); if (rc < 0) return -1; /* Perform initial read */ rc = cckd_read_init (dev); } /* Backup to the last opened file number */ cckd->sfn--; /* If the last file was opened read-only then create a new one */ if (cckd->open[cckd->sfn] == CCKD_OPEN_RO) if (cckd_sf_new(dev) < 0) return -1; /* Re-open previous rdwr files rdonly */ for (i = 0; i < cckd->sfn; i++) { if (cckd->open[i] == CCKD_OPEN_RO) continue; if (cckd_open (dev, i, O_RDONLY|O_BINARY, 0) < 0) { WRMSG (HHC00312, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, i, cckd_sf_name(dev, i), strerror(errno)); return -1; } } return 0; } /* end function cckd_sf_init */ /*-------------------------------------------------------------------*/ /* Create a new shadow file */ /*-------------------------------------------------------------------*/ int cckd_sf_new (DEVBLK *dev) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int l1size; /* Size of level 1 table */ CKDDASD_DEVHDR devhdr; /* Device header */ cckd = dev->cckd_ext; cckd_trace (dev, "file[%d] sf_new %s", cckd->sfn+1, cckd_sf_name(dev, cckd->sfn+1) ? (char *)cckd_sf_name(dev, cckd->sfn+1) : "(none)"); /* Error if no shadow file name */ if (dev->dasdsfn == NULL) { WRMSG (HHC00313, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn+1); return -1; } /* Error if max number of shadow files exceeded */ if (cckd->sfn+1 == CCKD_MAX_SF) { WRMSG (HHC00314, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn+1, dev->dasdsfn); return -1; } /* Harden the current file */ cckd_harden (dev); /* Open the new shadow file */ if (cckd_open(dev, cckd->sfn+1, O_RDWR|O_CREAT|O_EXCL|O_BINARY, S_IRUSR | S_IWUSR | S_IRGRP) < 0) return -1; /* Read previous file's device header */ if (cckd_read (dev, cckd->sfn, 0, &devhdr, CKDDASD_DEVHDR_SIZE) < 0) goto sf_new_error; /* Make sure identifier is CKD_S370 or FBA_S370 */ devhdr.devid[4] = 'S'; /* Write new file's device header */ if (cckd_write (dev, cckd->sfn+1, 0, &devhdr, CKDDASD_DEVHDR_SIZE) < 0) goto sf_new_error; /* Build the compressed device header */ memcpy (&cckd->cdevhdr[cckd->sfn+1], &cckd->cdevhdr[cckd->sfn], CCKDDASD_DEVHDR_SIZE); l1size = cckd->cdevhdr[cckd->sfn+1].numl1tab * CCKD_L1ENT_SIZE; cckd->cdevhdr[cckd->sfn+1].size = cckd->cdevhdr[cckd->sfn+1].used = CKDDASD_DEVHDR_SIZE + CCKDDASD_DEVHDR_SIZE + l1size; cckd->cdevhdr[cckd->sfn+1].free = cckd->cdevhdr[cckd->sfn+1].free_total = cckd->cdevhdr[cckd->sfn+1].free_largest = cckd->cdevhdr[cckd->sfn+1].free_number = cckd->cdevhdr[cckd->sfn+1].free_imbed = 0; /* Init the level 1 table */ if ((cckd->l1[cckd->sfn+1] = cckd_malloc (dev, "l1", l1size)) == NULL) goto sf_new_error; memset (cckd->l1[cckd->sfn+1], 0xff, l1size); /* Make the new file active */ cckd->sfn++; /* Harden the new file */ if (cckd_harden (dev) < 0) { cckd->sfn--; goto sf_new_error; } /* Re-read the l1 to set l2bounds, l2ok */ cckd_read_l1 (dev); return 0; sf_new_error: cckd->l1[cckd->sfn+1] = cckd_free(dev, "l1", cckd->l1[cckd->sfn+1]); cckd_close (dev, cckd->sfn+1); cckd->open[cckd->sfn+1] = CCKD_OPEN_NONE; unlink (cckd_sf_name (dev, cckd->sfn+1)); /* Re-read the l1 to set l2bounds, l2ok */ cckd_read_l1 (dev); return -1; } /* end function cckd_sf_new */ /*-------------------------------------------------------------------*/ /* Add a shadow file (sf+) */ /*-------------------------------------------------------------------*/ void *cckd_sf_add (void *data) { DEVBLK *dev = data; /* -> DEVBLK */ CCKDDASD_EXT *cckd; /* -> cckd extension */ #ifdef OPTION_SYNCIO int syncio; /* Saved syncio bit */ #endif // OPTION_SYNCIO if (dev == NULL) { int n = 0; for (dev=sysblk.firstdev; dev; dev=dev->nextdev) if (dev->cckd_ext) { WRMSG (HHC00315, "I", SSID_TO_LCSS(dev->ssid), dev->devnum ); cckd_sf_add (dev); n++; } WRMSG(HHC00316, "I", n ); return NULL; } cckd = dev->cckd_ext; if (!cckd) { WRMSG (HHC00317, "E", SSID_TO_LCSS(dev->ssid), dev->devnum); return NULL; } #ifdef OPTION_SYNCIO /* Disable synchronous I/O for the device */ syncio = cckd_disable_syncio(dev); #endif // OPTION_SYNCIO /* Schedule updated track entries to be written */ obtain_lock (&cckd->cckdiolock); if (cckd->merging) { #ifdef OPTION_SYNCIO dev->syncio = syncio; #endif // OPTION_SYNCIO release_lock (&cckd->cckdiolock); WRMSG (HHC00318, "W", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn)); return NULL; } cckd->merging = 1; cckd_flush_cache (dev); while (cckd->wrpending || cckd->cckdioact) { cckd->cckdwaiters++; wait_condition (&cckd->cckdiocond, &cckd->cckdiolock); cckd->cckdwaiters--; cckd_flush_cache (dev); } cckd_purge_cache (dev); cckd_purge_l2 (dev); dev->bufcur = dev->cache = -1; release_lock (&cckd->cckdiolock); /* Obtain control of the file */ obtain_lock (&cckd->filelock); /* Harden the current file */ cckd_harden (dev); /* Create a new shadow file */ if (cckd_sf_new (dev) < 0) { WRMSG (HHC00319, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn+1, cckd_sf_name(dev, cckd->sfn+1)?cckd_sf_name(dev, cckd->sfn+1):"(null)"); goto cckd_sf_add_exit; } /* Re-open the previous file if opened read-write */ if (cckd->open[cckd->sfn-1] == CCKD_OPEN_RW) cckd_open (dev, cckd->sfn-1, O_RDONLY|O_BINARY, 0); WRMSG (HHC00320, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name (dev, cckd->sfn)); cckd_sf_add_exit: /* Re-read the l1 to set l2bounds, l2ok */ cckd_read_l1 (dev); release_lock (&cckd->filelock); obtain_lock (&cckd->cckdiolock); cckd->merging = 0; if (cckd->cckdwaiters) broadcast_condition (&cckd->cckdiocond); #ifdef OPTION_SYNCIO dev->syncio = syncio; #endif // OPTION_SYNCIO release_lock (&cckd->cckdiolock); cckd_sf_stats (dev); return NULL; } /* end function cckd_sf_add */ /*-------------------------------------------------------------------*/ /* Remove a shadow file (sf-) */ /*-------------------------------------------------------------------*/ void *cckd_sf_remove (void *data) { DEVBLK *dev = data; /* -> DEVBLK */ CCKDDASD_EXT *cckd; /* -> cckd extension */ #ifdef OPTION_SYNCIO int syncio; /* Saved syncio bit */ #endif // OPTION_SYNCIO int rc; /* Return code */ int from_sfx, to_sfx; /* From/to file index */ int fix; /* nullfmt index */ int add = 0; /* 1=Add shadow file back */ int l2updated = 0; /* 1=L2 table was updated */ int i,j; /* Loop indexes */ int merge, force; /* Flags */ off_t pos; /* File offset */ unsigned int len; /* Length to read/write */ int size; /* Image size */ int trk = -1; /* Track being read/written */ CCKD_L2ENT from_l2[256], /* Level 2 tables */ to_l2[256]; CCKD_L2ENT new_l2; /* New level 2 table entry */ BYTE buf[65536]; /* Buffer */ if (dev == NULL) { int n = 0; merge = cckdblk.sfmerge; force = cckdblk.sfforce; cckdblk.sfmerge = cckdblk.sfforce = 0; for (dev=sysblk.firstdev; dev; dev=dev->nextdev) if ((cckd = dev->cckd_ext)) { WRMSG(HHC00321, "I", SSID_TO_LCSS(dev->ssid), dev->devnum ); cckd->sfmerge = merge; cckd->sfforce = force; cckd_sf_remove (dev); n++; } WRMSG(HHC00316, "I", n ); return NULL; } cckd = dev->cckd_ext; if (!cckd) { WRMSG (HHC00317, "E", dev ? SSID_TO_LCSS(dev->ssid) : 0, dev ? dev->devnum : 0); return NULL; } /* Set flags */ merge = cckd->sfmerge || cckd->sfforce; force = cckd->sfforce; cckd->sfmerge = cckd->sfforce = 0; cckd_trace (dev, "merge starting: %s %s", merge ? "merge" : "nomerge", force ? "force" : ""); #ifdef OPTION_SYNCIO /* Disable synchronous I/O for the device */ syncio = cckd_disable_syncio(dev); #endif // OPTION_SYNCIO /* Schedule updated track entries to be written */ obtain_lock (&cckd->cckdiolock); if (cckd->merging) { #ifdef OPTION_SYNCIO dev->syncio = syncio; #endif // OPTION_SYNCIO release_lock (&cckd->cckdiolock); WRMSG (HHC00322, "W", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name(dev, cckd->sfn)); return NULL; } cckd->merging = 1; cckd_flush_cache (dev); while (cckd->wrpending || cckd->cckdioact) { cckd->cckdwaiters++; wait_condition (&cckd->cckdiocond, &cckd->cckdiolock); cckd->cckdwaiters--; cckd_flush_cache (dev); } cckd_purge_cache (dev); cckd_purge_l2 (dev); dev->bufcur = dev->cache = -1; release_lock (&cckd->cckdiolock); obtain_lock (&cckd->filelock); if (cckd->sfn == 0) { #ifdef OPTION_SYNCIO dev->syncio = syncio; #endif // OPTION_SYNCIO release_lock (&cckd->filelock); WRMSG (HHC00323, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name(dev, cckd->sfn)); cckd->merging = 0; return NULL; } from_sfx = cckd->sfn; to_sfx = cckd->sfn - 1; fix = cckd->cdevhdr[to_sfx].nullfmt; /* Harden the `from' file */ if (cckd_harden (dev) < 0) { WRMSG (HHC00324, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, from_sfx, cckd_sf_name(dev, cckd->sfx), from_sfx, "not hardened", ""); goto sf_remove_exit; } /* Attempt to re-open the `to' file read-write */ cckd_close (dev, to_sfx); if (to_sfx > 0 || !dev->ckdrdonly || force) cckd_open (dev, to_sfx, O_RDWR|O_BINARY, 1); if (cckd->fd[to_sfx] < 0) { /* `from' file can't be opened read-write */ cckd_open (dev, to_sfx, O_RDONLY|O_BINARY, 0); if (merge) { WRMSG (HHC00324, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, from_sfx, cckd_sf_name(dev, from_sfx), to_sfx, "cannot be opened read-write", to_sfx == 0 && dev->ckdrdonly && !force ? ", try 'force'" : ""); goto sf_remove_exit; } else add = 1; } else { /* `from' file opened read-write */ cckd->sfn = to_sfx; if (cckd_chkdsk (dev, 0) < 0) { cckd->sfn = from_sfx; WRMSG (HHC00324, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, to_sfx, cckd_sf_name(dev, to_sfx), to_sfx, "check failed", ""); goto sf_remove_exit; } } cckd->sfn = to_sfx; /* Perform backwards merge */ if (merge) { cckd_trace (dev, "merging to file[%d]", to_sfx); /* Make the target file the active file */ cckd->sfn = to_sfx; cckd->cdevhdr[to_sfx].options |= (CCKD_OPENED | CCKD_ORDWR); /* Loop for each level 1 table entry */ for (i = 0; i < cckd->cdevhdr[from_sfx].numl1tab; i++) { l2updated = 0; /* Continue if from L2 doesn't exist */ if (cckd->l1[from_sfx][i] == 0xffffffff || (cckd->l1[from_sfx][i] == 0 && cckd->l1[to_sfx][i] == 0)) continue; trk = i*256; /* Read `from' l2 table */ if (cckd->l1[from_sfx][i] == 0) memset( &from_l2, 0, CCKD_L2TAB_SIZE ); else { pos = (off_t)cckd->l1[from_sfx][i]; if (cckd_read(dev, from_sfx, pos, &from_l2, CCKD_L2TAB_SIZE) < 0) goto sf_merge_error; } /* Read `to' l2 table */ if (cckd->l1[to_sfx][i] == 0) memset( &to_l2, 0, CCKD_L2TAB_SIZE ); else if (cckd->l1[to_sfx][i] == 0xffffffff) memset (&to_l2, 0xff, CCKD_L2TAB_SIZE); else { pos = (off_t)cckd->l1[to_sfx][i]; if (cckd_read(dev, to_sfx, pos, &to_l2, CCKD_L2TAB_SIZE) < 0) goto sf_merge_error; } /* Loop for each level 2 table entry */ for (j = 0; j < 256; j++) { trk = i*256 + j; /* Continue if from L2 entry doesn't exist */ if (from_l2[j].pos == 0xffffffff || (from_l2[j].pos == 0 && to_l2[j].pos == 0)) continue; /* Read the `from' track/blkgrp image */ len = (int)from_l2[j].len; if (len > CKDDASD_NULLTRK_FMTMAX) { pos = (off_t)from_l2[j].pos; if (cckd_read (dev, from_sfx, pos, buf, len) < 0) goto sf_merge_error; /* Get space for the `to' track/blkgrp image */ size = len; if ((pos = cckd_get_space (dev, &size, CCKD_SIZE_EXACT)) < 0) goto sf_merge_error; new_l2.pos = (U32)pos; new_l2.len = (U16)len; new_l2.size = (U16)size; /* Write the `to' track/blkgrp image */ if (cckd_write(dev, to_sfx, pos, buf, len) < 0) goto sf_merge_error; } else { new_l2.pos = 0; new_l2.len = new_l2.size = (U16)len; } /* Release space occupied by old `to' entry */ cckd_rel_space (dev, (off_t)to_l2[j].pos, (int)to_l2[j].len, (int)to_l2[j].size); /* Update `to' l2 table entry */ l2updated = 1; to_l2[j].pos = new_l2.pos; to_l2[j].len = new_l2.len; to_l2[j].size = new_l2.size; } /* for each level 2 table entry */ /* Update the `to' level 2 table */ if (l2updated) { l2updated = 0; pos = (off_t)cckd->l1[to_sfx][i]; if (memcmp (&to_l2, &empty_l2[fix], CCKD_L2TAB_SIZE) == 0) { cckd_rel_space (dev, pos, CCKD_L2TAB_SIZE, CCKD_L2TAB_SIZE); pos = 0; } else { size = CCKD_L2TAB_SIZE; if (pos == 0 || pos == (off_t)0xffffffff) if ((pos = cckd_get_space (dev, &size, CCKD_L2SPACE)) < 0) goto sf_merge_error; if (cckd_write(dev, to_sfx, pos, &to_l2, CCKD_L2TAB_SIZE) < 0) goto sf_merge_error; } /* `to' level 2 table not null */ /* Update the level 1 table index */ cckd->l1[to_sfx][i] = (U32)pos; /* Flush free space */ cckd_flush_space (dev); } /* Update level 2 table */ } /* For each level 1 table entry */ /* Validate the merge */ cckd_harden (dev); cckd_chkdsk (dev, 0); cckd_read_init (dev); } /* if merge */ /* Remove the old file */ cckd_close (dev, from_sfx); cckd->l1[from_sfx] = cckd_free (dev, "l1", cckd->l1[from_sfx]); memset( &cckd->cdevhdr[from_sfx], 0, CCKDDASD_DEVHDR_SIZE ); rc = unlink (cckd_sf_name (dev, from_sfx)); /* adjust the stats */ cckd->reads[to_sfx] += cckd->reads[from_sfx]; cckd->writes[to_sfx] += cckd->writes[from_sfx]; cckd->l2reads[to_sfx] += cckd->l2reads[from_sfx]; cckd->reads[from_sfx] = 0; cckd->writes[from_sfx] = 0; cckd->l2reads[from_sfx] = 0; /* Add the file back if necessary */ if (add) rc = cckd_sf_new (dev) ; WRMSG (HHC00325, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, from_sfx, cckd_sf_name(dev, from_sfx), merge ? "merged" : add ? "re-added" : "removed"); sf_remove_exit: /* Re-read the l1 to set l2bounds, l2ok */ cckd_read_l1 (dev); release_lock (&cckd->filelock); obtain_lock (&cckd->cckdiolock); cckd_purge_cache (dev); cckd_purge_l2 (dev); dev->bufcur = dev->cache = -1; cckd->merging = 0; if (cckd->cckdwaiters) broadcast_condition (&cckd->cckdiocond); #ifdef OPTION_SYNCIO dev->syncio = syncio; #endif // OPTION_SYNCIO cckd_trace (dev, "merge complete%s",""); release_lock (&cckd->cckdiolock); cckd_sf_stats (dev); return NULL; sf_merge_error: if (trk < 0) WRMSG (HHC00326, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, from_sfx, cckd_sf_name(dev, from_sfx)); else WRMSG (HHC00327, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, from_sfx, cckd_sf_name(dev, from_sfx), trk); if (l2updated && cckd->l1[to_sfx][i] && cckd->l1[to_sfx][i] != 0xffffffff) { l2updated = 0; pos = (off_t)cckd->l1[to_sfx][i]; cckd_write(dev, to_sfx, pos, &to_l2, CCKD_L2TAB_SIZE); } cckd_harden(dev); cckd_chkdsk (dev, 2); cckd->sfn = from_sfx; cckd_harden(dev); cckd_chkdsk (dev, 2); goto sf_remove_exit; } /* end function cckd_sf_remove */ /*-------------------------------------------------------------------*/ /* Check and compress a shadow file (sfc) */ /*-------------------------------------------------------------------*/ void *cckd_sf_comp (void *data) { DEVBLK *dev = data; /* -> DEVBLK */ CCKDDASD_EXT *cckd; /* -> cckd extension */ #ifdef OPTION_SYNCIO int syncio; /* Saved syncio bit */ #endif // OPTION_SYNCIO int rc; /* Return code */ if (dev == NULL) { int n = 0; for (dev=sysblk.firstdev; dev; dev=dev->nextdev) if (dev->cckd_ext) { WRMSG( HHC00328, "I", SSID_TO_LCSS(dev->ssid), dev->devnum ); cckd_sf_comp (dev); n++; } WRMSG (HHC00316, "I", n ); return NULL; } cckd = dev->cckd_ext; if (!cckd) { WRMSG (HHC00317, "W", SSID_TO_LCSS(dev->ssid), dev->devnum); return NULL; } #ifdef OPTION_SYNCIO /* Disable synchronous I/O for the device */ syncio = cckd_disable_syncio(dev); #endif // OPTION_SYNCIO /* schedule updated track entries to be written */ obtain_lock (&cckd->cckdiolock); if (cckd->merging) { #ifdef OPTION_SYNCIO dev->syncio = syncio; #endif // OPTION_SYNCIO release_lock (&cckd->cckdiolock); WRMSG (HHC00329, "W", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name(dev, cckd->sfn)); return NULL; } cckd->merging = 1; cckd_flush_cache (dev); while (cckd->wrpending || cckd->cckdioact) { cckd->cckdwaiters++; wait_condition (&cckd->cckdiocond, &cckd->cckdiolock); cckd->cckdwaiters--; cckd_flush_cache (dev); } cckd_purge_cache (dev); cckd_purge_l2 (dev); dev->bufcur = dev->cache = -1; release_lock (&cckd->cckdiolock); /* obtain control of the file */ obtain_lock (&cckd->filelock); /* harden the current file */ cckd_harden (dev); /* Call the compress function */ rc = cckd_comp (dev); /* Perform initial read */ rc = cckd_read_init (dev); release_lock (&cckd->filelock); obtain_lock (&cckd->cckdiolock); cckd->merging = 0; if (cckd->cckdwaiters) broadcast_condition (&cckd->cckdiocond); #ifdef OPTION_SYNCIO dev->syncio = syncio; #endif // OPTION_SYNCIO release_lock (&cckd->cckdiolock); /* Display the shadow file statistics */ cckd_sf_stats (dev); return NULL; } /* end function cckd_sf_comp */ /*-------------------------------------------------------------------*/ /* Check a shadow file (sfk) */ /*-------------------------------------------------------------------*/ void *cckd_sf_chk (void *data) { DEVBLK *dev = data; /* -> DEVBLK */ CCKDDASD_EXT *cckd; /* -> cckd extension */ #ifdef OPTION_SYNCIO int syncio; /* Saved syncio bit */ #endif // OPTION_SYNCIO int rc; /* Return code */ int level = 2; /* Check level */ if (dev == NULL) { int n = 0; level = cckdblk.sflevel; cckdblk.sflevel = 0; for (dev=sysblk.firstdev; dev; dev=dev->nextdev) if ((cckd = dev->cckd_ext)) { WRMSG (HHC00330, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, level ); cckd->sflevel = level; cckd_sf_chk (dev); n++; } WRMSG(HHC00316, "I", n ); return NULL; } cckd = dev->cckd_ext; if (!cckd) { WRMSG (HHC00317, "W", SSID_TO_LCSS(dev->ssid), dev->devnum); return NULL; } level = cckd->sflevel; cckd->sflevel = 0; #ifdef OPTION_SYNCIO /* Disable synchronous I/O for the device */ syncio = cckd_disable_syncio(dev); #endif // OPTION_SYNCIO /* schedule updated track entries to be written */ obtain_lock (&cckd->cckdiolock); if (cckd->merging) { #ifdef OPTION_SYNCIO dev->syncio = syncio; #endif // OPTION_SYNCIO release_lock (&cckd->cckdiolock); WRMSG (HHC00331, "W", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name(dev, cckd->sfn)); return NULL; } cckd->merging = 1; cckd_flush_cache (dev); while (cckd->wrpending || cckd->cckdioact) { cckd->cckdwaiters++; wait_condition (&cckd->cckdiocond, &cckd->cckdiolock); cckd->cckdwaiters--; cckd_flush_cache (dev); } cckd_purge_cache (dev); cckd_purge_l2 (dev); dev->bufcur = dev->cache = -1; release_lock (&cckd->cckdiolock); /* obtain control of the file */ obtain_lock (&cckd->filelock); /* harden the current file */ cckd_harden (dev); /* Call the chkdsk function */ rc = cckd_chkdsk (dev, level); /* Perform initial read */ rc = cckd_read_init (dev); release_lock (&cckd->filelock); obtain_lock (&cckd->cckdiolock); cckd->merging = 0; if (cckd->cckdwaiters) broadcast_condition (&cckd->cckdiocond); #ifdef OPTION_SYNCIO dev->syncio = syncio; #endif // OPTION_SYNCIO release_lock (&cckd->cckdiolock); /* Display the shadow file statistics */ cckd_sf_stats (dev); return NULL; } /* end function cckd_sf_chk */ /*-------------------------------------------------------------------*/ /* Display shadow file statistics (sfd) */ /*-------------------------------------------------------------------*/ void *cckd_sf_stats (void *data) { DEVBLK *dev = data; /* -> DEVBLK */ CCKDDASD_EXT *cckd; /* -> cckd extension */ struct stat st; /* File information */ int i; /* Index */ int rc; /* Return code */ char *ost[] = {" ", "ro", "rd", "rw"}; U64 usize=0,ufree=0; /* Total size, free space */ int freenbr=0; /* Total number free spaces */ if (dev == NULL) { int n = 0; for (dev=sysblk.firstdev; dev; dev=dev->nextdev) if (dev->cckd_ext) { WRMSG( HHC00332, "I", SSID_TO_LCSS(dev->ssid), dev->devnum ); cckd_sf_stats (dev); n++; } WRMSG(HHC00316, "I", n ); return NULL; } cckd = dev->cckd_ext; if (!cckd) { WRMSG (HHC00317, "W", SSID_TO_LCSS(dev->ssid), dev->devnum); return NULL; } // obtain_lock (&cckd->filelock); /* Calculate totals */ rc = fstat (cckd->fd[0], &st); for (i = 0; i <= cckd->sfn; i++) { if (!i) usize = st.st_size; else usize += cckd->cdevhdr[i].size; ufree += cckd->cdevhdr[i].free_total; freenbr += cckd->cdevhdr[i].free_number; } /* header */ WRMSG (HHC00333, "I", SSID_TO_LCSS(dev->ssid), dev->devnum); if (cckd->readaheads || cckd->misses) WRMSG (HHC00334, "I", SSID_TO_LCSS(dev->ssid), dev->devnum); WRMSG (HHC00335, "I", SSID_TO_LCSS(dev->ssid), dev->devnum); /* total statistics */ WRMSG (HHC00336, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, usize, (ufree * 100) / usize, freenbr, cckd->totreads, cckd->totwrites, cckd->totl2reads, cckd->cachehits, cckd->switches); if (cckd->readaheads || cckd->misses) WRMSG (HHC00337, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->readaheads, cckd->misses); /* base file statistics */ WRMSG (HHC00338, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename); WRMSG (HHC00339, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, st.st_size, ((S64)(cckd->cdevhdr[0].free_total * 100) / st.st_size), cckd->cdevhdr[0].free_number, ost[cckd->open[0]], cckd->reads[0], cckd->writes[0], cckd->l2reads[0]); if (dev->dasdsfn != NULL && CCKD_MAX_SF > 0) WRMSG (HHC00340, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd_sf_name(dev, -1)); /* shadow file statistics */ for (i = 1; i <= cckd->sfn; i++) { WRMSG (HHC00341, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, i, (S64)cckd->cdevhdr[i].size, ((S64)(cckd->cdevhdr[i].free_total * 100) / cckd->cdevhdr[i].size), cckd->cdevhdr[i].free_number, ost[cckd->open[i]], cckd->reads[i], cckd->writes[i], cckd->l2reads[i]); } // release_lock (&cckd->filelock); return NULL; } /* end function cckd_sf_stats */ #ifdef OPTION_SYNCIO /*-------------------------------------------------------------------*/ /* Disable synchronous I/O for a device */ /*-------------------------------------------------------------------*/ int cckd_disable_syncio(DEVBLK *dev) { if (!dev->syncio) return 0; obtain_lock(&dev->lock); while (dev->syncio_active) { release_lock(&dev->lock); usleep(500); obtain_lock(&dev->lock); } dev->syncio = 0; release_lock(&dev->lock); cckd_trace (dev, "syncio disabled%s",""); return 1; } #endif // OPTION_SYNCIO /*-------------------------------------------------------------------*/ /* Lock/unlock the device chain */ /*-------------------------------------------------------------------*/ void cckd_lock_devchain(int flag) { obtain_lock(&cckdblk.devlock); while ((flag && cckdblk.devusers != 0) || (!flag && cckdblk.devusers < 0)) { cckdblk.devwaiters++; #if FALSE { struct timespec tm; struct timeval now; int timeout; gettimeofday(&now,NULL); tm.tv_sec = now.tv_sec + 2; tm.tv_nsec = now.tv_usec * 1000; timeout = timed_wait_condition(&cckdblk.devcond, &cckdblk.devlock, &tm); if (timeout) cckd_print_itrace(); } #else wait_condition(&cckdblk.devcond, &cckdblk.devlock); #endif cckdblk.devwaiters--; } if (flag) cckdblk.devusers--; else cckdblk.devusers++; release_lock(&cckdblk.devlock); } void cckd_unlock_devchain() { obtain_lock(&cckdblk.devlock); if (cckdblk.devusers < 0) cckdblk.devusers++; else cckdblk.devusers--; if (cckdblk.devusers == 0 && cckdblk.devwaiters) signal_condition(&cckdblk.devcond); release_lock(&cckdblk.devlock); } /*-------------------------------------------------------------------*/ /* Garbage Collection thread */ /*-------------------------------------------------------------------*/ void* cckd_gcol(void* arg) { int gcol; /* Identifier */ int rc; /* Return code */ DEVBLK *dev; /* -> device block */ CCKDDASD_EXT *cckd; /* -> cckd extension */ S64 size, fsiz; /* File size, free size */ struct timeval tv_now; /* Time-of-day (as timeval) */ time_t tt_now; /* Time-of-day (as time_t) */ struct timespec tm; /* Time-of-day to wait */ int gc; /* Garbage collection state */ int gctab[5]= { /* default gcol parameters */ 4096, /* critical 50% - 100% */ 2048, /* severe 25% - 50% */ 1024, /* moderate 12.5% - 25% */ 512, /* light 6.3% - 12.5% */ 256}; /* none 0% - 6.3% */ UNREFERENCED( arg ); gettimeofday (&tv_now, NULL); obtain_lock (&cckdblk.gclock); gcol = ++cckdblk.gcs; /* Return without messages if too many already started */ if (gcol > cckdblk.gcmax) { --cckdblk.gcs; release_lock (&cckdblk.gclock); return NULL; } if (!cckdblk.batch) { WRMSG (HHC00100, "I", thread_id(), get_thread_priority(0), "Garbage collector"); } while (gcol <= cckdblk.gcmax) { cckd_lock_devchain(0); /* Perform collection on each device */ for (dev = cckdblk.dev1st; dev; dev = cckd->devnext) { cckd = dev->cckd_ext; obtain_lock (&cckd->cckdiolock); /* Bypass if merging or stopping */ if (cckd->merging || cckd->stopping) { release_lock (&cckd->cckdiolock); continue; } /* Bypass if not opened read-write */ if (cckd->open[cckd->sfn] != CCKD_OPEN_RW) { release_lock (&cckd->cckdiolock); continue; } /* Free newbuf if it hasn't been used */ if (!cckd->cckdioact && !cckd->bufused && cckd->newbuf) cckd->newbuf = cckd_free (dev, "newbuf", cckd->newbuf); cckd->bufused = 0; /* If OPENED bit not on then flush if updated */ if (!(cckd->cdevhdr[cckd->sfn].options & CCKD_OPENED)) { if (cckd->updated) cckd_flush_cache (dev); release_lock (&cckd->cckdiolock); continue; } /* Determine garbage state */ size = (S64)cckd->cdevhdr[cckd->sfn].size; fsiz = (S64)cckd->cdevhdr[cckd->sfn].free_total; if (fsiz >= (size = size/2)) gc = 0; else if (fsiz >= (size = size/2)) gc = 1; else if (fsiz >= (size = size/2)) gc = 2; else if (fsiz >= (size = size/2)) gc = 3; else gc = 4; /* Adjust the state based on the number of free spaces */ if (cckd->cdevhdr[cckd->sfn].free_number > 800 && gc > 0) gc--; if (cckd->cdevhdr[cckd->sfn].free_number > 1800 && gc > 0) gc--; if (cckd->cdevhdr[cckd->sfn].free_number > 3000) gc = 0; /* Set the size */ if (cckdblk.gcparm > 0) size = gctab[gc] << cckdblk.gcparm; else if (cckdblk.gcparm < 0) size = gctab[gc] >> abs(cckdblk.gcparm); else size = gctab[gc]; if (size > cckd->cdevhdr[cckd->sfn].used >> 10) size = cckd->cdevhdr[cckd->sfn].used >> 10; if (size < 64) size = 64; release_lock (&cckd->cckdiolock); /* Call the garbage collector */ cckd_gc_percolate (dev, (unsigned int)size); /* Schedule any updated tracks to be written */ obtain_lock (&cckd->cckdiolock); cckd_flush_cache (dev); while (cckdblk.fsync && cckd->wrpending) { cckd->cckdwaiters++; wait_condition (&cckd->cckdiocond, &cckd->cckdiolock); cckd->cckdwaiters--; } release_lock (&cckd->cckdiolock); /* Sync the file */ if (cckdblk.fsync && cckd->lastsync + 10 <= tv_now.tv_sec) { obtain_lock (&cckd->filelock); rc = fdatasync (cckd->fd[cckd->sfn]); cckd->lastsync = tv_now.tv_sec; release_lock (&cckd->filelock); } /* Flush the free space */ if (cckd->cdevhdr[cckd->sfn].free_number) { obtain_lock (&cckd->filelock); cckd_flush_space (dev); release_lock (&cckd->filelock); } } /* for each cckd device */ cckd_unlock_devchain(); /* wait a bit */ // Get the time of day again for the file sync check above gettimeofday (&tv_now, NULL); tt_now = tv_now.tv_sec + ((tv_now.tv_usec + 500000)/1000000); cckd_trace (dev, "gcol wait %d seconds at %s", cckdblk.gcwait, ctime (&tt_now)); tm.tv_sec = tv_now.tv_sec + cckdblk.gcwait; tm.tv_nsec = tv_now.tv_usec * 1000; timed_wait_condition (&cckdblk.gccond, &cckdblk.gclock, &tm); } if (!cckdblk.batch) WRMSG (HHC00101, "I", thread_id(), get_thread_priority(0), "Garbage collector"); cckdblk.gcs--; if (!cckdblk.gcs) signal_condition (&cckdblk.termcond); release_lock (&cckdblk.gclock); return NULL; } /* end thread cckd_gcol */ /*-------------------------------------------------------------------*/ /* Garbage Collection -- Percolate algorithm */ /*-------------------------------------------------------------------*/ int cckd_gc_percolate(DEVBLK *dev, unsigned int size) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int rc; /* Return code */ unsigned int moved = 0; /* Space moved */ int after = 0, a; /* New space after old */ int sfx; /* File index */ int i, j, l; /* Indexes */ int flags; /* Write trkimg flags */ off_t fpos, upos; /* File offsets */ unsigned int flen, ulen, len; /* Lengths */ int trk; /* Track number */ int l1x,l2x; /* Table Indexes */ CCKD_L2ENT l2; /* Copied level 2 entry */ BYTE buf[256*1024]; /* Buffer */ cckd = dev->cckd_ext; size = size << 10; /* Debug */ if (cckdblk.itracen) { cckd_trace (dev, "gcperc size %d 1st 0x%x nbr %d largest %u", size, cckd->cdevhdr[cckd->sfn].free, cckd->cdevhdr[cckd->sfn].free_number, cckd->cdevhdr[cckd->sfn].free_largest); fpos = (off_t)cckd->cdevhdr[cckd->sfn].free; for (i = cckd->free1st; i >= 0; i = cckd->free[i].next) { cckd_trace (dev, "gcperc free[%4d]:%8.8x end %8.8x len %10d%cpend %d", i,(int)fpos,(int)(fpos+cckd->free[i].len),(int)cckd->free[i].len, fpos+(int)cckd->free[i].len == (int)cckd->free[i].pos ? '*' : ' ',cckd->free[i].pending); fpos = cckd->free[i].pos; } } if (!cckd->l2ok) cckd_gc_l2(dev, buf); /* garbage collection cycle */ while (moved < size && after < 4) { obtain_lock (&cckd->filelock); sfx = cckd->sfn; /* Exit if no more free space */ if (cckd->cdevhdr[sfx].free_total == 0) { release_lock (&cckd->filelock); return moved; } /* Make sure the free space chain is built */ if (!cckd->free) cckd_read_fsp (dev); /* Find a space to start with */ l = -1; upos = ulen = flen = 0; fpos = cckd->cdevhdr[sfx].free; /* First non-pending free space */ for (i = cckd->free1st; i >= 0; i = cckd->free[i].next) { if (!cckd->free[i].pending) { flen += cckd->free[i].len; break; } fpos = cckd->free[i].pos; } /* Continue to largest if non-zero `after' */ for ( ; i >= 0 && after; i = cckd->free[i].next) { l = i; if (!cckd->free[i].pending) flen += cckd->free[i].len; if (cckd->free[i].len == cckd->cdevhdr[sfx].free_largest) break; fpos = cckd->free[i].pos; } /* Skip following free spaces */ for ( ; i >= 0; i = cckd->free[i].next) { if (!cckd->free[i].pending) flen += cckd->free[i].len; if (fpos + cckd->free[i].len != cckd->free[i].pos) break; fpos = cckd->free[i].pos; } /* Space preceding largest if largest is at the end */ if (i < 0 && l >= 0) { if (!cckd->free[l].pending) flen -= cckd->free[i].len; for (i = cckd->free[l].prev; i >= 0; i = cckd->free[i].prev) { fpos = cckd->free[i].prev >= 0 ? cckd->free[cckd->free[i].prev].pos : cckd->cdevhdr[sfx].free; if (fpos + cckd->free[i].len < cckd->free[i].pos) break; if (!cckd->free[i].pending) flen -= cckd->free[i].len; } } /* Calculate the offset/length of the used space. * If only imbedded free space is left, then start * with the first used space that is not an l2 table. */ if (i >= 0) { upos = fpos + cckd->free[i].len; ulen = (cckd->free[i].pos ? cckd->free[i].pos : cckd->cdevhdr[sfx].size) - upos; } else if (!cckd->cdevhdr[sfx].free_number && cckd->cdevhdr[sfx].free_imbed) { upos = (off_t)(CCKD_L1TAB_POS + cckd->cdevhdr[sfx].numl1tab * CCKD_L1ENT_SIZE); while (1) { for (i = 0; i < cckd->cdevhdr[sfx].numl1tab; i++) if (cckd->l1[sfx][i] == (U32)upos) break; if (i >= cckd->cdevhdr[sfx].numl1tab) break; upos += CCKD_L2TAB_SIZE; } ulen = cckd->cdevhdr[sfx].size - upos; } /* Return if no applicable used space */ if (ulen == 0) { cckd_trace (dev, "gcperc no applicable space, moved %u", moved); release_lock (&cckd->filelock); return moved; } /* Reduce ulen size to minimize `after' relocations */ if (ulen > flen + 65536) ulen = flen + 65536; if (ulen > sizeof(buf)) ulen = sizeof(buf); cckd_trace (dev, "gcperc selected space 0x"I64_FMTx" len %d", upos, ulen); if (cckd_read (dev, sfx, upos, buf, ulen) < 0) goto cckd_gc_perc_error; /* Process each space in the buffer */ flags = cckd->cdevhdr[sfx].free_number < 100 ? CCKD_SIZE_EXACT : CCKD_SIZE_ANY; for (i = a = 0; i + CKDDASD_TRKHDR_SIZE <= (int)ulen; i += len) { /* Check for level 2 table */ for (j = 0; j < cckd->cdevhdr[sfx].numl1tab; j++) if (cckd->l1[sfx][j] == (U32)(upos + i)) break; if (j < cckd->cdevhdr[sfx].numl1tab) { /* Moving a level 2 table */ len = CCKD_L2TAB_SIZE; if (i + len > ulen) break; cckd_trace (dev, "gcperc move l2tab[%d] at pos 0x"I64_FMTx" len %d", j, upos + i, len); /* Make the level 2 table active */ if (cckd_read_l2 (dev, sfx, j) < 0) goto cckd_gc_perc_error; /* Write the level 2 table */ if (cckd_write_l2 (dev) < 0) goto cckd_gc_perc_error; } else { /* Moving a track image */ if ((trk = cckd_cchh (dev, buf + i, -1)) < 0) goto cckd_gc_perc_space_error; l1x = trk >> 8; l2x = trk & 0xff; /* Read the lookup entry for the track */ if (cckd_read_l2ent (dev, &l2, trk) < 0) goto cckd_gc_perc_error; if (l2.pos != (U32)(upos + i)) goto cckd_gc_perc_space_error; len = (int)l2.size; if (i + l2.len > (int)ulen) break; cckd_trace (dev, "gcperc move trk %d at pos 0x"I64_FMTx" len %h", trk, upos + i, l2.len); /* Relocate the track image somewhere else */ if ((rc = cckd_write_trkimg (dev, buf + i, (int)l2.len, trk, flags)) < 0) goto cckd_gc_perc_error; a += rc; } } /* for each space in the used space */ /* Set `after' to 1 if first time space was relocated after */ after += after ? a : (a > 0); moved += i; cckdblk.stats_gcolmoves++; cckdblk.stats_gcolbytes += i; release_lock (&cckd->filelock); } /* while (moved < size) */ cckd_trace (dev, "gcperc moved %d 1st 0x%x nbr %u", moved, cckd->cdevhdr[cckd->sfn].free,cckd->cdevhdr[cckd->sfn].free_number); return moved; cckd_gc_perc_space_error: WRMSG (HHC00342, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn,cckd_sf_name(dev, cckd->sfn), upos + i, buf[i], buf[i+1],buf[i+2], buf[i+3], buf[i+4]); cckd->cdevhdr[cckd->sfn].options |= CCKD_SPERRS; cckd_print_itrace(); cckd_gc_perc_error: cckd_trace (dev, "gcperc exiting due to error, moved %u", moved); release_lock (&cckd->filelock); return moved; } /* end function cckd_gc_percolate */ /*-------------------------------------------------------------------*/ /* Garbage Collection -- Reposition level 2 tables */ /* */ /* General idea is to relocate all level 2 tables as close to the */ /* beginning of the file as possible. This can help speed up, for */ /* example, chkdsk processing. */ /* */ /* If any level 2 tables reside outside of the bounds (that is, if */ /* any level 2 table could be moved closer to the beginning of the */ /* file) then first we relocate all track images within the bounds. */ /* Note that cckd_get_space will not allocate space within the */ /* the bounds for track images. Next we try to relocate all level 2 */ /* tables outside the bounds. This may take a few iterations for */ /* the freed space within the bounds to become non-pending. */ /* */ /* The bounds can change as level 2 tables are added or removed. */ /* cckd_read_l1 sets the bounds and they are adjusted by */ /* cckd_write_l2. */ /*-------------------------------------------------------------------*/ int cckd_gc_l2(DEVBLK *dev, BYTE *buf) { CCKDDASD_EXT *cckd; /* -> cckd extension */ int sfx; /* Shadow file index */ int i, j; /* Work variables */ int trk; /* Track number */ int len; /* Track length */ off_t pos, fpos; /* File offsets */ cckd = dev->cckd_ext; obtain_lock (&cckd->filelock); sfx = cckd->sfn; if (cckd->l2ok || cckd->cdevhdr[cckd->sfn].free_total == 0) goto cckd_gc_l2_exit; /* Find any level 2 table out of bounds */ for (i = 0; i < cckd->cdevhdr[sfx].numl1tab; i++) if (cckd->l1[sfx][i] != 0 && cckd->l1[sfx][i] != 0xffffffff && cckd->l2bounds - CCKD_L2TAB_SIZE < (off_t)cckd->l1[sfx][i]) break; /* Return OK if no l2 tables out of bounds */ if (i >= cckd->cdevhdr[sfx].numl1tab) goto cckd_gc_l2_exit_ok; /* Relocate all track images within the bounds */ pos = CCKD_L1TAB_POS + (cckd->cdevhdr[sfx].numl1tab * CCKD_L1ENT_SIZE); i = cckd->free1st; fpos = (off_t)cckd->cdevhdr[sfx].free; while (pos < cckd->l2bounds) { if (i >= 0 && pos == fpos) { pos += cckd->free[i].len; fpos = (off_t)cckd->free[i].pos; i = cckd->free[i].next; j = 0; } else { for (j = 0; j < cckd->cdevhdr[sfx].numl1tab; j++) if (pos == (off_t)cckd->l1[sfx][j]) { pos += CCKD_L2TAB_SIZE; break; } } if (j >= cckd->cdevhdr[sfx].numl1tab) { /* Found a track to relocate */ if (cckd_read (dev, sfx, pos, buf, CKDDASD_TRKHDR_SIZE) < 0) goto cckd_gc_l2_exit; if ((trk = cckd_cchh (dev, buf, -1)) < 0) goto cckd_gc_l2_exit; cckd_trace (dev, "gc_l2 relocate trk[%d] offset 0x%x", trk, pos); if ((len = cckd_read_trkimg (dev, buf, trk, NULL)) < 0) goto cckd_gc_l2_exit; if (cckd_write_trkimg (dev, buf, len, trk, CCKD_SIZE_EXACT) < 0) goto cckd_gc_l2_exit; /* Start over */ pos = CCKD_L1TAB_POS + (cckd->cdevhdr[sfx].numl1tab * CCKD_L1ENT_SIZE); i = cckd->free1st; fpos = (off_t)cckd->cdevhdr[sfx].free; } } do { /* Find a level 2 table to relocate */ i = cckd->free1st; fpos = (off_t)cckd->cdevhdr[sfx].free; cckd_trace (dev, "gc_l2 first free[%d] pos 0x%x len %d pending %d", i, (int)fpos, i >= 0 ? (int)cckd->free[i].len : -1, i >= 0 ? cckd->free[i].pending : -1); if (i < 0 || fpos >= cckd->l2bounds || cckd->free[i].pending) goto cckd_gc_l2_exit; if (cckd->free[i].len < CCKD_L2TAB_SIZE || (cckd->free[i].len != CCKD_L2TAB_SIZE && cckd->free[i].len < CCKD_L2TAB_SIZE + CCKD_FREEBLK_SIZE ) ) { for (i = 0; i < cckd->cdevhdr[sfx].numl1tab; i++) if (fpos + cckd->free[i].len == (off_t)cckd->l1[sfx][i]) break; } else { for (i = 0; i < cckd->cdevhdr[sfx].numl1tab; i++) if (cckd->l2bounds - CCKD_L2TAB_SIZE < (off_t)cckd->l1[sfx][i] && cckd->l1[sfx][i] != 0xffffffff) break; } if (i < cckd->cdevhdr[sfx].numl1tab) { cckd_trace (dev, "gc_l2 relocate l2[%d] pos 0x%x", i, cckd->l1[sfx][i]); if (cckd_read_l2 (dev, sfx, i) < 0) goto cckd_gc_l2_exit; if (cckd_write_l2 (dev) < 0) goto cckd_gc_l2_exit; } } while (i < cckd->cdevhdr[sfx].numl1tab); cckd_gc_l2_exit: release_lock (&cckd->filelock); return 0; cckd_gc_l2_exit_ok: cckd_trace (dev, "gc_l2 ok%s", ""); cckd->l2ok = 1; goto cckd_gc_l2_exit; } /*-------------------------------------------------------------------*/ /* Find device by devnum */ /*-------------------------------------------------------------------*/ DEVBLK *cckd_find_device_by_devnum (U16 devnum) { DEVBLK *dev; CCKDDASD_EXT *cckd; cckd_lock_devchain (0); for (dev = cckdblk.dev1st; dev; dev = cckd->devnext) { if (dev->devnum == devnum) break; cckd = dev->cckd_ext; } cckd_unlock_devchain (); return dev; } /* end function cckd_find_device_by_devnum */ /*-------------------------------------------------------------------*/ /* Uncompress a track image */ /*-------------------------------------------------------------------*/ BYTE *cckd_uncompress (DEVBLK *dev, BYTE *from, int len, int maxlen, int trk) { CCKDDASD_EXT *cckd; BYTE *to = NULL; /* Uncompressed buffer */ int newlen; /* Uncompressed length */ BYTE comp; /* Compression type */ static char *compress[] = {"none", "zlib", "bzip2"}; cckd = dev->cckd_ext; cckd_trace (dev, "uncompress comp %d len %d maxlen %d trk %d", from[0] & CCKD_COMPRESS_MASK, len, maxlen, trk); /* Extract compression type */ comp = (from[0] & CCKD_COMPRESS_MASK); /* Get a buffer to uncompress into */ if (comp != CCKD_COMPRESS_NONE && cckd->newbuf == NULL) { cckd->newbuf = cckd_malloc (dev, "newbuf", maxlen); if (cckd->newbuf == NULL) return NULL; } /* Uncompress the track image */ switch (comp) { case CCKD_COMPRESS_NONE: newlen = cckd_trklen (dev, from); to = from; break; case CCKD_COMPRESS_ZLIB: to = cckd->newbuf; newlen = cckd_uncompress_zlib (dev, to, from, len, maxlen); break; case CCKD_COMPRESS_BZIP2: to = cckd->newbuf; newlen = cckd_uncompress_bzip2 (dev, to, from, len, maxlen); break; default: newlen = -1; break; } /* Validate the uncompressed track image */ newlen = cckd_validate (dev, to, trk, newlen); /* Return if successful */ if (newlen > 0) { if (to != from) { cckd->newbuf = from; cckd->bufused = 1; } return to; } /* Get a buffer now if we haven't gotten one */ if (cckd->newbuf == NULL) { cckd->newbuf = cckd_malloc (dev, "newbuf2", maxlen); if (cckd->newbuf == NULL) return NULL; } /* Try each uncompression routine in turn */ /* uncompressed */ newlen = cckd_trklen (dev, from); newlen = cckd_validate (dev, from, trk, newlen); if (newlen > 0) return from; /* zlib compression */ to = cckd->newbuf; newlen = cckd_uncompress_zlib (dev, to, from, len, maxlen); newlen = cckd_validate (dev, to, trk, newlen); if (newlen > 0) { cckd->newbuf = from; cckd->bufused = 1; return to; } /* bzip2 compression */ to = cckd->newbuf; newlen = cckd_uncompress_bzip2 (dev, to, from, len, maxlen); newlen = cckd_validate (dev, to, trk, newlen); if (newlen > 0) { cckd->newbuf = from; cckd->bufused = 1; return to; } /* Unable to uncompress */ WRMSG (HHC00343, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name(dev, cckd->sfn), trk, from[0], from[1], from[2], from[3], from[4]); if (comp & ~cckdblk.comps) WRMSG (HHC00344, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cckd->sfn, cckd_sf_name(dev, cckd->sfn), compress[comp]); return NULL; } int cckd_uncompress_zlib (DEVBLK *dev, BYTE *to, BYTE *from, int len, int maxlen) { #if defined(HAVE_LIBZ) unsigned long newlen; int rc; UNREFERENCED(dev); memcpy (to, from, CKDDASD_TRKHDR_SIZE); newlen = maxlen - CKDDASD_TRKHDR_SIZE; rc = uncompress(&to[CKDDASD_TRKHDR_SIZE], &newlen, &from[CKDDASD_TRKHDR_SIZE], len - CKDDASD_TRKHDR_SIZE); if (rc == Z_OK) { newlen += CKDDASD_TRKHDR_SIZE; to[0] = 0; } else newlen = -1; cckd_trace (dev, "uncompress zlib newlen %d rc %d",(int)newlen,rc); return (int)newlen; #else UNREFERENCED(dev); UNREFERENCED(to); UNREFERENCED(from); UNREFERENCED(len); UNREFERENCED(maxlen); return -1; #endif } int cckd_uncompress_bzip2 (DEVBLK *dev, BYTE *to, BYTE *from, int len, int maxlen) { #if defined(CCKD_BZIP2) unsigned int newlen; int rc; UNREFERENCED(dev); memcpy (to, from, CKDDASD_TRKHDR_SIZE); newlen = maxlen - CKDDASD_TRKHDR_SIZE; rc = BZ2_bzBuffToBuffDecompress ( (void *)&to[CKDDASD_TRKHDR_SIZE], &newlen, (void *)&from[CKDDASD_TRKHDR_SIZE], len - CKDDASD_TRKHDR_SIZE, 0, 0); if (rc == BZ_OK) { newlen += CKDDASD_TRKHDR_SIZE; to[0] = 0; } else newlen = -1; cckd_trace (dev, "uncompress bz2 newlen %d rc %d",newlen,rc); return (int)newlen; #else UNREFERENCED(dev); UNREFERENCED(to); UNREFERENCED(from); UNREFERENCED(len); UNREFERENCED(maxlen); return -1; #endif } /*-------------------------------------------------------------------*/ /* Compress a track image */ /*-------------------------------------------------------------------*/ int cckd_compress (DEVBLK *dev, BYTE **to, BYTE *from, int len, int comp, int parm) { int newlen; switch (comp) { case CCKD_COMPRESS_NONE: newlen = cckd_compress_none (dev, to, from, len, parm); break; case CCKD_COMPRESS_ZLIB: newlen = cckd_compress_zlib (dev, to, from, len, parm); break; case CCKD_COMPRESS_BZIP2: newlen = cckd_compress_bzip2 (dev, to, from, len, parm); break; default: newlen = cckd_compress_bzip2 (dev, to, from, len, parm); break; } return newlen; } int cckd_compress_none (DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm) { UNREFERENCED(dev); UNREFERENCED(parm); *to = from; from[0] = CCKD_COMPRESS_NONE; return len; } int cckd_compress_zlib (DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm) { #if defined(HAVE_LIBZ) unsigned long newlen; int rc; BYTE *buf; UNREFERENCED(dev); buf = *to; from[0] = CCKD_COMPRESS_NONE; memcpy (buf, from, CKDDASD_TRKHDR_SIZE); buf[0] = CCKD_COMPRESS_ZLIB; newlen = 65535 - CKDDASD_TRKHDR_SIZE; rc = compress2 (&buf[CKDDASD_TRKHDR_SIZE], &newlen, &from[CKDDASD_TRKHDR_SIZE], len - CKDDASD_TRKHDR_SIZE, parm); newlen += CKDDASD_TRKHDR_SIZE; if (rc != Z_OK || (int)newlen >= len) { *to = from; newlen = len; } return (int)newlen; #else #if defined(CCKD_BZIP2) return cckd_compress_bzip2 (dev, to, from, len, parm); #else return cckd_compress_none (dev, to, from, len, parm); #endif #endif } int cckd_compress_bzip2 (DEVBLK *dev, BYTE **to, BYTE *from, int len, int parm) { #if defined(CCKD_BZIP2) unsigned int newlen; int rc; BYTE *buf; UNREFERENCED(dev); buf = *to; from[0] = CCKD_COMPRESS_NONE; memcpy (buf, from, CKDDASD_TRKHDR_SIZE); buf[0] = CCKD_COMPRESS_BZIP2; newlen = 65535 - CKDDASD_TRKHDR_SIZE; rc = BZ2_bzBuffToBuffCompress ( (void *)&buf[CKDDASD_TRKHDR_SIZE], &newlen, (void *)&from[CKDDASD_TRKHDR_SIZE], len - CKDDASD_TRKHDR_SIZE, parm >= 1 && parm <= 9 ? parm : 5, 0, 0); newlen += CKDDASD_TRKHDR_SIZE; if (rc != BZ_OK || newlen >= (unsigned int)len) { *to = from; newlen = len; } return newlen; #else return cckd_compress_zlib (dev, to, from, len, parm); #endif } /*-------------------------------------------------------------------*/ /* cckd command help */ /*-------------------------------------------------------------------*/ void cckd_command_help() { int i; char *help[] = { "Command parameters for cckd:" ," comp= Override compression (-1,0,1,2)" ," compparm= Override compression parm (-1 ... 9)" ," ra= Set number readahead threads ( 1 ... 9)" ," raq= Set readahead queue size ( 0 .. 16)" ," rat= Set number tracks to read ahead ( 0 .. 16)" ," wr= Set number writer threads ( 1 ... 9)" ," gcint= Set garbage collector interval (sec) ( 1 .. 60)" ," gcparm= Set garbage collector parameter (-8 ... 8)" ," freepend= Set free pending cycles (-1 ... 4)" ," trace= Set trace table size (0 ... 200000)" ," gcstart= Start garbage collector (0 or 1)" ," nostress= Disable stress writes (0 or 1)" ," fsync= Enable fsync (0 or 1)" ," linuxnull= Check for null linux tracks (0 or 1)" ," opts Display cckd options" ," stats Display cckd statistics" ," help Display help message" ,NULL }; for( i = 0; help[i] != NULL; i++ ) WRMSG(HHC00345, "I", help[i] ); } /* end function cckd_command_help */ /*-------------------------------------------------------------------*/ /* cckd command stats */ /*-------------------------------------------------------------------*/ void cckd_command_opts() { char msgbuf[128]; MSGBUF( msgbuf, "cckd opts: comp=%d,compparm=%d,ra=%d,raq=%d,rat=%d,wr=%d,gcint=%d", cckdblk.comp == 0xff ? -1 : cckdblk.comp, cckdblk.compparm, cckdblk.ramax, cckdblk.ranbr, cckdblk.readaheads, cckdblk.wrmax, cckdblk.gcwait ); WRMSG( HHC00346, "I", msgbuf ); MSGBUF( msgbuf, " gcparm=%d,nostress=%d,freepend=%d,fsync=%d,linuxnull=%d,trace=%d", cckdblk.gcparm, cckdblk.nostress, cckdblk.freepend, cckdblk.fsync, cckdblk.linuxnull, cckdblk.itracen ); WRMSG( HHC00346, "I", msgbuf ); return; } /* end function cckd_command_opts */ /*-------------------------------------------------------------------*/ /* cckd command stats */ /*-------------------------------------------------------------------*/ void cckd_command_stats() { char msgbuf[128]; WRMSG( HHC00347, "I", "cckd stats:" ); MSGBUF( msgbuf, " reads....%10" I64_FMT "d Kbytes...%10" I64_FMT "d", cckdblk.stats_reads, cckdblk.stats_readbytes >> 10 ); WRMSG( HHC00347, "I", msgbuf ); MSGBUF( msgbuf, " writes...%10" I64_FMT "d Kbytes...%10" I64_FMT "d", cckdblk.stats_writes, cckdblk.stats_writebytes >> 10 ); WRMSG( HHC00347, "I", msgbuf ); MSGBUF( msgbuf, " readaheads%9" I64_FMT "d misses...%10" I64_FMT "d", cckdblk.stats_readaheads, cckdblk.stats_readaheadmisses ); WRMSG( HHC00347, "I", msgbuf ); #ifdef OPTION_SYNCIO MSGBUF( msgbuf, " syncios..%10" I64_FMT "d misses...%10" I64_FMT "d", cckdblk.stats_syncios, cckdblk.stats_synciomisses ); WRMSG( HHC00347, "I", msgbuf ); #endif // OPTION_SYNCIO MSGBUF( msgbuf, " switches.%10" I64_FMT "d l2 reads.%10" I64_FMT "d strs wrt.%10" I64_FMT "d", cckdblk.stats_switches, cckdblk.stats_l2reads, cckdblk.stats_stresswrites ); WRMSG( HHC00347, "I", msgbuf ); MSGBUF( msgbuf, " cachehits%10" I64_FMT "d misses...%10" I64_FMT "d", cckdblk.stats_cachehits, cckdblk.stats_cachemisses ); WRMSG( HHC00347, "I", msgbuf ); MSGBUF( msgbuf, " l2 hits..%10" I64_FMT "d misses...%10" I64_FMT "d", cckdblk.stats_l2cachehits, cckdblk.stats_l2cachemisses ); WRMSG( HHC00347, "I", msgbuf ); MSGBUF( msgbuf, " waits............ i/o......%10" I64_FMT "d cache....%10" I64_FMT "d", cckdblk.stats_iowaits, cckdblk.stats_cachewaits ); WRMSG( HHC00347, "I", msgbuf ); MSGBUF( msgbuf, " garbage collector moves....%10" I64_FMT "d Kbytes...%10" I64_FMT "d", cckdblk.stats_gcolmoves, cckdblk.stats_gcolbytes >> 10 ); WRMSG( HHC00347, "I", msgbuf ); return; } /* end function cckd_command_stats */ /*-------------------------------------------------------------------*/ /* cckd command debug */ /*-------------------------------------------------------------------*/ void cckd_command_debug() { return; } /*-------------------------------------------------------------------*/ /* cckd command processor */ /*-------------------------------------------------------------------*/ DLL_EXPORT int cckd_command(char *op, int cmd) { char *kw, *p, c, buf[256]; int val, opts = 0; int rc; /* Display help for null operand */ if (op == NULL) { if (memcmp (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id)) == 0 && cmd) cckd_command_help(); return 0; } strlcpy(buf, op, sizeof(buf)); op = buf; /* Initialize the global cckd block if necessary */ if (memcmp (&cckdblk.id, "CCKDBLK ", sizeof(cckdblk.id))) cckddasd_init (0, NULL); while (op) { /* Operands are delimited by commas */ kw = op; op = strchr (op, ','); if (op) *op++ = '\0'; /* Check for keyword = value */ if ((p = strchr (kw, '='))) { *p++ = '\0'; rc = sscanf (p, "%d%c", &val, &c); } else rc = 0; /* Parse the keyword */ if ( CMD(kw,help,4 ) ) { if (!cmd) return 0; cckd_command_help(); } else if ( CMD(kw,stats,4) ) { if (!cmd) return 0; cckd_command_stats (); } else if ( CMD(kw,opts,4) ) { if (!cmd) return 0; cckd_command_opts(); } else if ( CMD(kw,debug,5) ) { if (!cmd) return 0; cckd_command_debug(); } else if (rc != 1) { WRMSG(HHC00348, "E", val, kw); return -1; } else if ( CMD(kw,comp,4) ) { if (val < -1 || (val > 0 && (val & ~cckdblk.comps))) { WRMSG(HHC00348, "E", val, kw); return -1; } else switch (val) { case -1: case CCKD_COMPRESS_NONE: case CCKD_COMPRESS_ZLIB: case CCKD_COMPRESS_BZIP2: cckdblk.comp = val < 0 ? 0xff : val; opts = 1; break; default: /* unsupported algorithm */ WRMSG(HHC00348, "E", val, kw); return -1; } } else if ( CMD(kw,compparm,8) ) { if (val < -1 || val > 9) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.compparm = val; opts = 1; } } else if ( CMD(kw,ra,2) ) { if (val < CCKD_MIN_RA || val > CCKD_MAX_RA) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.ramax = val; opts = 1; } } else if ( CMD(kw,raq,3) ) { if (val < 0 || val > CCKD_MAX_RA_SIZE) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.ranbr = val; opts = 1; } } else if ( CMD(kw,rat,3) ) { if (val < 0 || val > CCKD_MAX_RA_SIZE) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.readaheads = val; opts = 1; } } else if ( CMD(kw,wr,2) ) { if (val < CCKD_MIN_WRITER || val > CCKD_MAX_WRITER) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.wrmax = val; opts = 1; } } else if ( CMD(kw,gcint,5) ) { if (val < 1 || val > 60) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.gcwait = val; opts = 1; } } else if ( CMD(kw,gcparm,6) ) { if (val < -8 || val > 8) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.gcparm = val; opts = 1; } } else if ( CMD(kw,nostress,8) ) { if (val < 0 || val > 1) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.nostress = val; opts = 1; } } else if ( CMD(kw,freepend,8) ) { if (val < -1 || val > CCKD_MAX_FREEPEND) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.freepend = val; opts = 1; } } else if ( CMD(kw,fsync,5) ) { if (val < 0 || val > 1) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.fsync = val; opts = 1; } } else if ( CMD(kw,trace,5) ) { if (val < 0 || val > CCKD_MAX_TRACE) { WRMSG(HHC00348, "E", val, kw); return -1; } else { /* Disable tracing in case it's already active */ CCKD_TRACE *p = cckdblk.itrace; cckdblk.itrace = NULL; if (p) { SLEEP (1); cckdblk.itrace = cckdblk.itracep = cckdblk.itracex = NULL; cckdblk.itracen = 0; free (p); } /* Get a new trace table */ if (val > 0) { p = calloc (val, sizeof(CCKD_TRACE)); if (p) { cckdblk.itracen = val; cckdblk.itracex = p + val; cckdblk.itracep = p; cckdblk.itrace = p; } else { char buf[64]; MSGBUF( buf, "calloc(%d, %d)", val, (int)sizeof(CCKD_TRACE)); WRMSG (HHC00303, "E", 0, 0, buf, strerror(errno)); } } opts = 1; } } else if ( CMD(kw,linuxnull,5) ) { if (val < 0 || val > 1) { WRMSG(HHC00348, "E", val, kw); return -1; } else { cckdblk.linuxnull = val; opts = 1; } } else if ( CMD(kw,gcstart,7) ) { if (val < 0 || val > 1) { WRMSG(HHC00348, "E", val, kw); return -1; } else if (val == 1) { DEVBLK *dev; CCKDDASD_EXT *cckd; TID tid; int flag = 0; cckd_lock_devchain(0); for (dev = cckdblk.dev1st; dev; dev = cckd->devnext) { cckd = dev->cckd_ext; obtain_lock (&cckd->filelock); if (cckd->cdevhdr[cckd->sfn].free_total) { cckd->cdevhdr[cckd->sfn].options |= (CCKD_OPENED | CCKD_ORDWR); cckd_write_chdr (dev); flag = 1; } release_lock (&cckd->filelock); } cckd_unlock_devchain(); if (flag && cckdblk.gcs < cckdblk.gcmax) { rc = create_thread (&tid, JOINABLE, cckd_gcol, NULL, "cckd_gcol"); if (rc) WRMSG(HHC00102, "E", strerror(rc)); } } } else { WRMSG(HHC00349, "E", kw); if (!cmd) return -1; cckd_command_help (); op = NULL; } } if (cmd && opts) cckd_command_opts(); return 0; } /* end function cckd_command */ /*-------------------------------------------------------------------*/ /* Print internal trace */ /*-------------------------------------------------------------------*/ DLL_EXPORT void cckd_print_itrace() { CCKD_TRACE *i, *p; /* Trace table pointers */ int n; /* Trace Entries Printed */ WRMSG (HHC00399, "I"); if (!cckdblk.itrace) return; n = 0; i = cckdblk.itrace; cckdblk.itrace = NULL; SLEEP (1); p = cckdblk.itracep; if (p >= cckdblk.itracex) p = i; do { if ( strlen(*p) > 0 ) { n++; WRMSG(HHC00398, "I", (char *)p); } if (++p >= cckdblk.itracex) p = i; } while (p != cckdblk.itracep); if ( n == 0 ) WRMSG(HHC00397, "I"); memset(i, 0, cckdblk.itracen * sizeof(CCKD_TRACE)); cckdblk.itracep = i; cckdblk.itrace = i; } /* end function cckd_print_itrace */ /*-------------------------------------------------------------------*/ /* Write internal trace entry */ /*-------------------------------------------------------------------*/ void cckd_trace(DEVBLK *dev, char *msg, ...) { va_list vl; struct timeval tv; time_t t; char tbuf[64]; CCKD_TRACE *p; int l; if (dev && (dev->ccwtrace||dev->ccwstep)) { char *bfr; int sz=1024,rc; bfr=malloc(1024); va_start(vl,msg); while(1) { rc=vsnprintf(bfr,sz,msg,vl); if(rc<0) { free(bfr); bfr=NULL; break; } if(rcssid), dev->devnum, bfr); } va_end(vl); } /* ISW FIXME : The following code has a potential */ /* for heap overrun (vsprintf) */ if (cckdblk.itrace) { gettimeofday(&tv, NULL); t = tv.tv_sec; strlcpy(tbuf, ctime(&t), sizeof(tbuf)); tbuf[19] = '\0'; va_start(vl,msg); p = cckdblk.itracep++; if (p >= cckdblk.itracex) { p = cckdblk.itrace; cckdblk.itracep = p + 1; } if (p) { l = sprintf ((char *)p, "%s" "." "%6.6ld %1d:%04X> ", tbuf+11, (long)tv.tv_usec, dev ? SSID_TO_LCSS(dev->ssid) : 0, dev ? dev->devnum : 0); vsprintf ((char *)p + l, msg, vl); } } } /* end function cckd_trace */ DEVHND cckddasd_device_hndinfo = { &ckddasd_init_handler, /* Device Initialisation */ &ckddasd_execute_ccw, /* Device CCW execute */ &cckddasd_close_device, /* Device Close */ &ckddasd_query_device, /* Device Query */ NULL, /* Device Extended Query */ &cckddasd_start, /* Device Start channel pgm */ &cckddasd_end, /* Device End channel pgm */ &cckddasd_start, /* Device Resume channel pgm */ &cckddasd_end, /* Device Suspend channel pgm */ NULL, /* Device Halt channel pgm */ &cckd_read_track, /* Device Read */ &cckd_update_track, /* Device Write */ &cckd_used, /* Device Query used */ NULL, /* Device Reserve */ NULL, /* Device Release */ NULL, /* Device Attention */ NULL, /* Immediate CCW Codes */ NULL, /* Signal Adapter Input */ NULL, /* Signal Adapter Ouput */ NULL, /* Signal Adapter Sync */ NULL, /* Signal Adapter Output Mult */ NULL, /* QDIO subsys desc */ NULL, /* QDIO set subchan ind */ &ckddasd_hsuspend, /* Hercules suspend */ &ckddasd_hresume /* Hercules resume */ }; DEVHND cfbadasd_device_hndinfo = { &fbadasd_init_handler, /* Device Initialisation */ &fbadasd_execute_ccw, /* Device CCW execute */ &cckddasd_close_device, /* Device Close */ &fbadasd_query_device, /* Device Query */ NULL, /* Device Extended Query */ &cckddasd_start, /* Device Start channel pgm */ &cckddasd_end, /* Device End channel pgm */ &cckddasd_start, /* Device Resume channel pgm */ &cckddasd_end, /* Device Suspend channel pgm */ NULL, /* Device Halt channel pgm */ &cfba_read_block, /* Device Read */ &cfba_write_block, /* Device Write */ &cfba_used, /* Device Query used */ NULL, /* Device Reserve */ NULL, /* Device Release */ NULL, /* Device Attention */ NULL, /* Immediate CCW Codes */ NULL, /* Signal Adapter Input */ NULL, /* Signal Adapter Ouput */ NULL, /* Signal Adapter Sync */ NULL, /* Signal Adapter Output Mult */ NULL, /* QDIO subsys desc */ NULL, /* QDIO set subchan ind */ &fbadasd_hsuspend, /* Hercules suspend */ &ckddasd_hresume /* Hercules resume */ };