/*{ ** Name: DIwrite - Writes page(s) of a file to disk. ** ** Description: ** The DIwrite routine is used to write pages of a direct access ** file. This routine should be flexible enough to write multiple ** contiguous pages. The number of pages to write is indicated ** as an input parameter, This value is updated to indicate the ** actual number of pages written. A synchronous write is preferred ** but not required. ** ** The buffer address from which the data is to be written is examined ** to see if it is in shared memory. If so, we then instruct the slave ** to write the page(s) directly from the target buffer. Otherwise, we ** copy the page(s) from the buffer into the server segment, and then ** instruct the slave to write the page(s) from the server segment. ** ** Inputs: ** f Pointer to the DI file ** context needed to do I/O. ** n Pointer to value indicating number of pages to ** write. ** page Value indicating page(s) to write. ** buf Pointer to page(s) to write. ** ** Outputs: ** f Updates the file control block. ** n Pointer to value indicating number of pages ** written. ** err_code Pointer to a variable used ** to return operating system ** errors. ** Returns: ** OK ** DI_BADFILE Bad file context. ** DI_BADWRITE Error writing. ** DI_BADPARAM Parameter(s) in error. ** DI_ENDFILE Write past end of file. ** DI_BADLRU_RELEASE Error releasing open file. ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 26-mar-87 (mmm) ** Created new for 6.0. ** 06-feb-89 (mikem) ** Return CL_ERR_DESC from DIlru_open(). ** 23-mar-89 (mikem) ** update io_system_eof when necessary (bug 4854). ** 10-jul-89 (rogerk & mikem) ** When asked to write a page that is past our cached EOF marker, call ** DIsense to check the actual EOF before signalling an error. The ** page may have been allocated by a different server and our copy of ** the EOF has just not been updated yet. ** ** This case can also come up in a single server case now, as we ** continue to cache pages in the buffer manager even when the table ** is closed. This can result in a DIwrite() being performed on a ** newly opened file without ever doing a DIread() (where eof info ** was previously obtained). ** 10-jul-89 (mikem) ** Return DI_EXCEED_LIMIT if out of disk space, rather than ** BAD_WRITE. Also add some debugging code to make it easier to ** test that the server handles out of disk space correctly (by ** returning out of disk space out of DIwrite based on a gloal set ** by DIalloc every N times called). ** 23-Jan-90 (anton) ** Call DI_sense instead of DIsense to prevent multiple DIlru_opens ** and use of two CSevcbs. ** 2-Feb-90 (anton) ** Don't always copy CL_ERR_DESC ** 6-Feb-90 (jkb) ** Change write to IIdio_write which combines the write and lseek ** commands and makes direct io available for Sequent ** 5-aug-1991 (bryanp) ** Added support for I/O directly from server shared memory, ** bypassing the copy through the server segment if possible. ** 03-mar-1992 (jnash) ** Fix LG slave problem noted when Sun mmap() support ** introduced, change slave logic to send to the slave the ** segment id "key" rather than "segid" (segid value not ** the same in the slave). ** 30-October-1992 (rmuth) ** Prototype and make sure we have opened the file before ** we close it. ** 30-nov-1992 (rmuth) ** - Include <cldio.h> ** - DIlru error checking, this was a major restructuring of the ** code. No Change in functionality. ** 10-dec-1993 (rmuth) ** If fail the past io_allocated_eof test then make sure that we ** unset the errno value in CL_ERR_DESC set by SETCLERR. This was ** causing confusion as we were logging random errno's to the ** errlog.log ** 31-jan-94 (mikem) ** sir #57671 ** The transfer size of slave I/O is now stored in ** Cs_srv_block.cs_size_io_buf, rather than a constant ** DI_FILE_BUF_SIZE. ** 18-apr-1994 (jnash) ** fsync project. Call DIforce() on systems where fsync() exists ** but O_SYNC does not (hopefully never). ** 20-jun-1995 (amo ICL) ** Added call on DI_async_write for async io ** 20-Apr-1998 (merja01) ** Move "#" to column 1 to correct compile errors on axp_osf. ** 01-oct-1998 (somsa01) ** Return DI_NODISKSPACE when we are out of disk space. ** 29-Oct-1998 (schte01) ** Move "#" to column 1 to correct compile errors on axp_osf. ** 14-Oct-2005 (jenjo02) ** Chris's file descriptor properties now cached in io_fprop ** (file properties) and established on the first open, ** not every open. */ STATUS DIwrite( DI_IO *f, i4 *n, i4 page, char *buf, CL_ERR_DESC *err_code) { STATUS big_status = OK, small_status = OK, r_status; i4 num_of_pages; i4 last_page_to_write; CL_ERR_DESC lerr_code; DI_OP diop; /* default returns */ CL_CLEAR_ERR( err_code ); num_of_pages = *n; *n = 0; if (num_of_pages <= 0) return (DI_BADPARAM); last_page_to_write = page + num_of_pages - 1; diop.di_flags = 0; if (f->io_type != DI_IO_ASCII_ID) return(DI_BADFILE); if (f->io_mode != DI_IO_WRITE) return(DI_BADWRITE); /* Count another write */ f->io_stat.write++; /* ** get open file descriptor for the file */ if (big_status = DIlru_open(f, FALSE, &diop, err_code)) return(big_status); /* ** now check for write within bounds of the file */ if (last_page_to_write > f->io_alloc_eof) { i4 real_eof; /* ** DI_sense updates f->io_alloc_eof with the protection ** of io_sem (OS_THREADS), so there's no need to ** duplicate that update here. */ big_status = DI_sense(f, &diop, &real_eof, err_code); if (big_status == OK) { if (last_page_to_write > f->io_alloc_eof) { small_status = DI_ENDFILE; SETCLERR(err_code, 0, ER_write); /* ** The above sets errno as errno will be left over from ** a previous call zero it out to avoid confusion. */ err_code->errnum = 0; } } } if (big_status == OK && small_status == OK) { #ifdef xOUT_OF_DISK_SPACE_TEST if ((f->io_open_flags & DI_O_NODISKSPACE_DEBUG) && (last_page_to_write > f->io_logical_eof) && (last_page_to_write <= f->io_alloc_eof)) { f->io_open_flags &= ~DI_O_NODISKSPACE_DEBUG; small_status = DI_NODISKSPACE; SETCLERR(err_code, 0, ER_write); err_code->errnum = ENOSPC; TRdisplay( "DIwrite(): Returning false DI_NODISKSPACE, page %d\n", page); } else #endif /* xOUT_OF_DISK_SPACE_TEST */ { if (Di_slave) { big_status = DI_slave_write( f, &diop, buf, page, num_of_pages, err_code ); } else # if defined(OS_THREADS_USED) || defined(xCL_ASYNC_IO) if (Di_async_io) { big_status = DI_async_write( f, &diop, buf, page, num_of_pages, err_code ); } else # endif /* OS_THREADS_USED || xCL_ASYNC_IO */ { big_status = DI_inproc_write( f, &diop, buf, page, num_of_pages, err_code ); } if (big_status == OK && small_status == OK) # if defined(xCL_010_FSYNC_EXISTS) && !defined(O_SYNC) { /* ** Due to lru activity, this code assumes that a force on any ** file descriptor forces pages for all open files. If not ** the case, fsync() logic must be installed in the slave. */ big_status = DIforce( f, err_code ); } if (big_status == OK && small_status == OK) # endif *n = num_of_pages; } } r_status = DIlru_release(&diop, &lerr_code); if (big_status) return( big_status ); else if (small_status) return( small_status ); return(r_status); }
/* ** Writes or erases a transaction log and displays a completion thermometer. ** ** 'context' must not be NULL, and defaults for element 0 & 1 of context ** must have been set. */ i4 write_transaction_log(bool create, PM_CONTEXT *context, char *log_dir[], char *log_file[], void (*message)(char *), void (*init_graph)(bool, i4), i4 graph_size, void (*update_graph)()) { # define LG_PAGE_SIZE 2048L # define LG_NUMBER_OF_PAGES 32L /* Number of pages to write at a time */ DI_IO dio[LG_MAX_FILE]; char buf[LG_PAGE_SIZE * LG_NUMBER_OF_PAGES]; char nodename[GL_MAXNAME]; i4 i, j, marker, count, num_logs; i4 num_pages, page, part_size, remainder, loop; CL_ERR_DESC err; char *string; LOCATION loc[LG_MAX_FILE]; char locbuf[MAX_LOC + 1]; char *path[LG_MAX_FILE]; i4 LOinfo_flag; LOINFORMATION loc_info; i8 size; CS_SCB scb; bool size_in_kbytes = FALSE; MEfill( sizeof( scb ), 0, ( PTR ) &scb ); ++graph_size; /* Prepare transaction for log LOCATION(s) */ for (num_logs = 0; num_logs < LG_MAX_FILE; num_logs++) { if (log_dir[num_logs] == 0) break; STcopy( log_dir[num_logs], locbuf ); LOfroms( PATH, locbuf, &loc[num_logs] ); LOfstfile( log_file[num_logs], &loc[num_logs] ); LOtos( &loc[num_logs], &path[num_logs] ); } /* get size (in bytes) of log file to be created or erased */ if( create ) { STATUS status; char *value; bool havevalue = FALSE; status = PMmGet( context, ERx( "$.$.rcp.file.kbytes" ), &value ); if ( status == OK ) { havevalue = TRUE; size_in_kbytes = TRUE; } if ( havevalue == FALSE ) { status = PMmGet( context, ERx( "$.$.rcp.file.size" ), &value ); if ( status == OK ) havevalue = TRUE; } if ( havevalue == FALSE ) { if ( message != NULL ) { char msg[ BIG_ENOUGH ]; STprintf( msg, ERx( "%s %s" ), PMmExpandRequest( context, ERx( "$.$.rcp.file.kbytes" ) ), "not found." ); message( msg ); } return( 0 ); } CVal8( value, &size ); } else { LOinfo_flag = LO_I_SIZE; size = 0; for (i = 0; i < num_logs; i++) { if ( LOinfo( &loc[i], &LOinfo_flag, &loc_info ) != OK ) { if ( message != NULL ) { char msg[ BIG_ENOUGH ]; STprintf( msg, "Unable to get size of transaction log:\n\n\t%s", path[i] ); (*message)( msg ); } return( 0 ); } else size += loc_info.li_size; if ((LOinfo_flag & LO_I_SIZE) == 0) break; } if ( (LOinfo_flag & LO_I_SIZE) == 0 || size == 0L) { STATUS status; char *value; bool havevalue = FALSE; status = PMmGet( context, ERx( "$.$.rcp.file.kbytes" ), &value ); if ( status == OK ) { havevalue = TRUE; size_in_kbytes = TRUE; } if ( havevalue == FALSE ) { status = PMmGet( context, ERx( "$.$.rcp.file.size" ), &value ); if ( status == OK ) havevalue = TRUE; } if ( havevalue == FALSE ) { if ( message != NULL ) { char msg[ BIG_ENOUGH ]; STprintf( msg, ERx( "%s %s." ), PMmExpandRequest( context, ERx( "$.$.rcp.file.kbytes" ) ), "not found." ); message( msg ); } return( 0 ); } CVal8( value, &size ); } } for (i = 0; i < num_logs; i++) { if ( create && LOexist( &loc[i] ) == OK ) { char msg[ BIG_ENOUGH ]; if ( message != NULL ) { STprintf( msg, "%s already exists.", path[i] ); (*message)( msg ); STprintf( msg, "To create a new transaction log, you must first delete all partitions of the old one." ); (*message)( msg ); } return( 0 ); } } if ( CSinitiate( (i4 *) NULL, (char ***) NULL, (CS_CB *) NULL ) != OK ) { if ( message != NULL ) message( "Unable to connect to shared memory" ); return( 0 ); } CSset_sid( &scb ); /* create DI file */ for (i = 0; i < num_logs; i++) { if ( create && DIcreate( &dio[i], log_dir[i], (u_i4)STlength(log_dir[i]), log_file[i], (u_i4)STlength(log_file[i]), (i4)LG_PAGE_SIZE, &err) != OK ) { char msg[ BIG_ENOUGH ]; if ( message != NULL ) { STprintf( msg, "Unable to create transaction log:\n\n\t%s", path[i] ); (*message)( msg ); } return( 0 ); } /* open DI file */ if ( DIopen( &dio[i], log_dir[i], (u_i4)STlength(log_dir[i]), log_file[i], (u_i4)STlength(log_file[i]), (i4)LG_PAGE_SIZE, DI_IO_WRITE, 0, &err) != OK ) { if ( message != NULL ) { char msg[ BIG_ENOUGH ]; STprintf( msg, "Unable to open transaction log:\n\n\t%s", path[i] ); (*message)( msg ); } return( 0 ); } } if ( size_in_kbytes == FALSE ) size = (size + 1023) / 1024; (*init_graph)( create, (i4) size ); num_pages = size / (LG_PAGE_SIZE/1024); part_size = num_pages / num_logs; /* Readjust num_pages to be a multiple of num_logs */ num_pages = part_size * num_logs; loop = part_size / LG_NUMBER_OF_PAGES; remainder = part_size % LG_NUMBER_OF_PAGES; marker = loop / graph_size; /* Fill buffer with zeroes */ MEfill( sizeof( buf ), 0 , buf); for (i = 0; i < num_logs; i++) { if ( create && DIalloc( &dio[i], part_size, &page, &err ) != OK ) { DIdelete( &dio[i], log_dir[i], (u_i4)STlength( log_dir[i] ), log_file[i], (u_i4)STlength( log_file[i] ), &err ); if ( message != NULL ) (*message)( "Unable to allocate space in transaction log file." ); return( 0 ); } } count = 0; for( j = 0; j < loop; j++ ) { i4 n = LG_NUMBER_OF_PAGES; i4 page_no = j * n; for (i = 0; i < num_logs; i++) { if ( DIwrite( &dio[i], &n, page_no, buf, &err ) != OK ) { DIdelete( &dio[i], log_dir[i], (u_i4)STlength(log_dir[i]), log_file[i], (u_i4)STlength(log_file[i]), &err ); if ( message != NULL ) (*message)( "Unable to continue writing transaction log." ); return( 0 ); } } if ( j >= marker && j % marker == 0 && (f4) ((f4) j / (f4) loop) >= ((f4) count + 1) / graph_size ) { ++count; (*update_graph)(); SIflush( stdout ); } } if (remainder) { i4 page_no = loop * LG_NUMBER_OF_PAGES; for (i = 0; i < num_logs; i++) { if ( DIwrite( &dio[i], &remainder, page_no, buf, &err ) != OK ) { DIdelete( &dio[i], log_dir[i], (u_i4)STlength(log_dir[i]), log_file[i], (u_i4)STlength(log_file[i]), &err ); if ( message != NULL ) (*message)( "Unable to continue writing transaction log." ); return( 0 ); } } } for (i = 0; i < num_logs; i++) { if ( DIforce( &dio[i], &err ) != OK ) { DIdelete( &dio[i], log_dir[i], (u_i4)STlength(log_dir[i]), log_file[i], (u_i4)STlength(log_file[i]), &err ); if ( message != NULL ) (*message)( "Unable to force changes to transaction log." ); return( 0 ); } if ( create && DIflush( &dio[i], &err ) != OK ) { DIdelete( &dio[i], log_dir[i], (u_i4)STlength(log_dir[i]), log_file[i], (u_i4)STlength(log_file[i]), &err ); if ( message != NULL ) (*message)( "Unable to flush transaction log to disk." ); return( 0 ); } if( DIclose( &dio[i], &err ) != OK) { DIdelete( &dio[i], log_dir[i], (u_i4)STlength(log_dir[i]), log_file[i], (u_i4)STlength(log_file[i]), &err ); if ( message != NULL ) (*message)( "Unable to finish writing transaction log." ); return( 0 ); } } return( (i4) size ); }