



		    scavenge_volume.pl1             11/11/89  1100.0r w 11/11/89  0806.7      498546



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1982 *
        *                                                         *
        *********************************************************** */

/* format: style3,idind30,indcomtxt */
scavenge_volume:
     proc (Pvtep, Scavenger_blockp, Scavenger_Optionsp, Sc_metersp, Code);

/*  The Volume Scavenger

   Written July 1982 by J. Bongiovanni
   Modified October 1982 by J. Bongiovanni for Filemap Checksum, fm_damaged
   Modified May 1983 by E. N. Kittlitz for free_map/in-use conflict.
   Modified August 1983 by E. N. Kittlitz for search_ast$check.
   Modified 83-12-13 BIM for fault for debugging locking.
*/


/****^  HISTORY COMMENTS:
  1) change(86-10-23,Fawcett), approve(86-10-23,MCR7517),
     audit(86-10-30,Beattie), install(86-11-03,MR12.0-1206):
     Change Error Documentation to remove the word BOS.
                                                   END HISTORY COMMENTS */


/*  Parameter  */

dcl	Pvtep			ptr;		/* -> PVTE for volume */
dcl	Scavenger_blockp		ptr;		/* -> scavenger block allocated for scavenge */
dcl	Scavenger_Optionsp		ptr;		/* -> options for this scavenge */
dcl	Sc_metersp		ptr;		/* -> metering cells */
dcl	Code			fixed bin (35);	/* Error code */

/*  Automatic  */

dcl	base_vtocx		fixed bin;
dcl	conflicts_unclaimed		fixed bin;
dcl	1 copy_options		aligned like scavenger_options;
dcl	damaged_vtoces		bit (MAX_VTOCE_PER_PACK) aligned;
dcl	device_name		char (8);
dcl	freed_vtoces		bit (MAX_VTOCE_PER_PACK) aligned;
dcl	get_vtoce_errors		fixed bin;
dcl	old_mask			fixed bin (71);
dcl	p99			pic "99";
dcl	ptp			ptr;
dcl	ptwp			ptr;
dcl	pvtx			fixed bin;
dcl	restart_sw		bit (1) aligned;
dcl	tsdw			fixed bin (71);
dcl	vastep			ptr;
dcl	vtoce_bitsp		ptr;
dcl	vtoces_damaged		fixed bin;
dcl	vtoces_damaged_by_me	fixed bin;

/*  Static  */

dcl	ALL_PARTS			bit (3) int static options (constant) init ("111"b);
dcl	GET_VTOCE_ERROR_THRESHOLD	fixed bin int static options (constant) init (30);
dcl	RLV_INITIALIZED		fixed bin int static options (constant) init (2);

/*  Based  */

dcl	1 file_map		aligned based,
	  2 fm			(0:255) fixed bin (18) uns unal;
dcl	1 Scavenger_Options		aligned like scavenger_options based (Scavenger_Optionsp);
dcl	1 vtoce_bits		aligned based (vtoce_bitsp),
	  2 pad			bit (base_vtocx) unaligned,
	  2 remaining		bit (MAX_VTOCE_PER_PACK - base_vtocx) unaligned;

/*  External  */

dcl	active_hardcore_data$pdd_uid	bit (36) aligned external;
dcl	active_hardcore_data$sl1_uid	bit (36) aligned external;
dcl	error_table_$scavenge_aborted fixed bin (35) external;
dcl	error_table_$vtoce_free	fixed bin (35) external;
dcl	pds$process_group_id	char (32) ext static;
dcl	pvt$root_lvid		bit (36) aligned external;
dcl	sst$astap			ptr external;
dcl	sst$checksum_filemap	fixed bin (35) external;
dcl	sst$cmp			ptr external;
dcl	sst$damaged_ct		fixed bin (35) external;
dcl	1 sst$level		(0:3) aligned external,
	  2 ausedp		bit (18) unaligned,
	  2 no_aste		bit (18) unaligned;
dcl	sst$pts			(0:3) fixed bin external;
dcl	sys_info$initialization_state fixed bin external;
dcl	volmap_abs_seg$		external;

/*  Entry  */

dcl	filemap_checksum_		entry (ptr, fixed bin, bit (36) aligned);
dcl	fsout_vol			entry (fixed bin, fixed bin);
dcl	lock$lock_ast		entry;
dcl	lock$unlock_ast		entry;
dcl	page$free_address_for_scavenge
				entry (fixed bin, fixed bin (18));
dcl	page$lock_volmap		entry (ptr);
dcl	page$unlock_volmap		entry (ptr);
dcl	pc$deposit_list		entry (fixed bin, fixed bin, ptr, fixed bin, ptr);
dcl	pc$cleanup		entry (ptr);
dcl	pmut$lock_ptl		entry (fixed bin (71), ptr);
dcl	pmut$swap_sdw		entry (ptr, ptr);
dcl	pmut$unlock_ptl		entry (fixed bin (71), ptr);
dcl	priv_delete_vtoce		entry (bit (36) aligned, bit (36) aligned, fixed bin, fixed bin (35));
dcl	pxss$relinquish_priority	entry;
dcl	search_ast$check		entry (bit (36) aligned, bit (36) aligned, fixed bin, fixed bin (35))
				returns (ptr);
dcl	setfaults			entry (ptr, bit (1) aligned);
dcl	syserr			entry options (variable);
dcl	syserr$binary		entry options (variable);
dcl	syserr$error_code		entry options (variable);
dcl	tc_util$check_abort		entry (fixed bin (35));
dcl	update_vtoce		entry (ptr);
dcl	vtoc_man$free_vtoce_for_scavenge
				entry (bit (36) aligned, fixed bin, fixed bin, fixed bin (35));
dcl	vtoc_man$get_vtoce		entry (bit (36) aligned, fixed bin, fixed bin, bit (3), ptr, fixed bin (35));
dcl	vtoc_man$put_vtoce		entry (bit (36) aligned, fixed bin, fixed bin, bit (3), ptr, fixed bin (35));
dcl	vtoc_man$read_ahead_vtoce	entry (bit (36) aligned, fixed bin, fixed bin, bit (3), fixed bin (35));

declare	(addr, addrel, bin, bit, clock, convert, divide, fixed, hbound, index, null, ptr, rel, rtrim, size, stacq, substr,
	unspec, mod)		builtin;



	pvtep = Pvtep;
	scavenger_blockp = Scavenger_blockp;
	sc_metersp = Sc_metersp;
	copy_options = Scavenger_Options;
	Code = 0;


	scavenger_datap = addr (scavenger_data$);
	pvt_arrayp = addr (pvt$array);
	pvtx = divide (bin (rel (pvtep)) - bin (rel (pvt_arrayp)), size (pvte), 17) + 1;
	device_name = pvte.devname || "_" || convert (p99, pvte.logical_area_number) || rtrim (pvte.sv_name);


	call pmut$swap_sdw (addr (volmap_abs_seg$), addr (pvte.volmap_seg_sdw));
	vol_mapp = ptr (addr (volmap_abs_seg$), pvte.volmap_offset);
	vtoc_mapp = ptr (vol_mapp, pvte.vtoc_map_offset);

RESTART:
	restart_sw = "0"b;

	vtoces_damaged, vtoces_damaged_by_me = 0;
	get_vtoce_errors = 0;
	conflicts_unclaimed = 0;

	unspec (freed_vtoces) = ""b;
	unspec (damaged_vtoces) = ""b;

	call lock$lock_ast;

	if copy_options.fault_under_ast
	then call FAULT ("AST Lock");

	call page$lock_volmap (pvtep);
	if copy_options.fault_under_volmap
	then call FAULT ("VOLMAP Lock");

	call COPY_VOLMAP;

	pvte.deposit_to_volmap = "1"b;
	call pmut$lock_ptl (old_mask, ptwp);
	if copy_options.fault_under_pt
	then call FAULT ("Global PTL");

	pvte.scav_check_address = "1"b;
	call COPY_STOCK;
	call WALK_SST;
	call pmut$unlock_ptl (old_mask, ptwp);
	pvte.deposit_to_volmap = "0"b;

	call page$unlock_volmap (pvtep);
	call lock$unlock_ast;

	call WALK_VOLUME;

RECOVER_OVERFLOW:
	call RESOLVE_CONFLICTS;

	if get_vtoce_errors = 0
	then call FREE_RECORDS;
	pvte.scav_check_address = "0"b;

	if restart_sw
	then do;
		unspec (scavenger_block.records) = ""b;
		unspec (scavenger_block.overflow) = ""b;
		scavenger_block.ovfl_free_ix = 1;
		goto RESTART;
	     end;

	call CHECK_VTOCE_DAMAGE;
	call FREE_VTOCES;

	if get_vtoce_errors = 0
	then do;
		pvte.vol_trouble_count = 0;
		call fsout_vol (pvtx, 0);
	     end;


	if get_vtoce_errors > 0
	then call syserr (ANNOUNCE, "scavenge_volume: ^d errors reading VTOCEs on ^a", get_vtoce_errors, device_name);

	if vtoces_damaged > 0
	then call syserr (ANNOUNCE,
		"scavenge_volume: ^d VTOCEs on ^a damaged.^[ ^d damaged during this scavenge.^;^1s^]", vtoces_damaged,
		device_name, (vtoces_damaged_by_me > 0), vtoces_damaged_by_me);

ABORT_JOIN:
	pvte.scav_check_address = "0"b;

	tsdw = 0;
	call pmut$swap_sdw (addr (volmap_abs_seg$), addr (tsdw));

	if copy_options.trap
	then call syserr (CRASH, "scavenge_volume: Debug Trap on scavenge of ^a", device_name);


	return;
%page;
/*  Internal Procedure to find all free records in the volume map
   and mark them as free in the scavenger block. It is assumed that there
   will be no conflicts. This must be called with the volume map locked.
   This prevents record addresses from migrating between the volume map
   and the record stock.
*/

COPY_VOLMAP:
     proc;

dcl	bit_mapx			fixed bin;
dcl	bx			fixed bin;
dcl	rec_add			fixed bin (18);
dcl	word_baseadd		fixed bin (18);


	do bit_mapx = 1 to vol_map.bit_map_n_words;
	     bit_map_wordp = addr (vol_map.bit_map (bit_mapx));
	     if bit_map_word.bits ^= ""b		/* Some free this word */
	     then do;
		     word_baseadd = (bit_mapx - 1) * 32;
		     do bx = 1 to 32;
			if substr (bit_map_word.bits, bx, 1) = "1"b
			then do;
				rec_add = word_baseadd + bx;
				scavenger_block.records (rec_add).state = STATE_FREE;
			     end;
		     end;
		end;
	end;

     end COPY_VOLMAP;
%page;
/*  Internal Procedure to find all free addresses in the record stock and
   mark them as free in the scavenger block. Any conflicts are marked as
   such (this can only happen if an address in the stock was found in the
   volume map, which can only happen if an address deposited recently is
   marked as free in the volume map). Both the volume map and the Page
   Table Lock must be held to ensure the stability of free addresses during
   this scan. The Page Table Lock prevents withdrawal from the stock.
   pvte.deposit_to_volmap ON disables lockless stock deposits,
   and so deposits are done under the volume map lock.
*/

COPY_STOCK:
     proc;

dcl	rec_add			fixed bin (18);
dcl	sx			fixed bin;


	record_stockp = pvte.volmap_stock_ptr;
	do sx = 1 to record_stock.n_in_stock;
	     if record_stock.stock (sx) ^= ""b		/* Record address this slot */
	     then if substr (record_stock.stock (sx), 1, 1) = "1"b
						/* Out-of-service */
		then call syserr (CRASH, "scavenge_volume: Out-of-service address in stock for ^a", device_name);
		else do;
			rec_add = bin (record_stock.stock (sx), 18) - pvte.baseadd + 1;
			if scavenger_block.records (rec_add).state ^= STATE_UNSEEN
			then scavenger_block.records (rec_add).state = STATE_CONFLICT;

			else scavenger_block.records (rec_add).state = STATE_FREE;
		     end;
	end;

     end COPY_STOCK;
%page;
/*  Internal Procedure to walk the SST. For each non-special ASTE on this
   physical volume, we construct its file map from the page table and
   core map entries. We then check the file map, as is done for each
   VTOCE at another time.  The AST Lock and the Page Table Lock must be held.
*/
WALK_SST:
     proc;


dcl	astx			fixed bin;
dcl	1 aste_file_map		aligned like file_map;
dcl	n_pages			fixed bin;
dcl	px			fixed bin;


	astep = sst$astap;

	do px = 0 to hbound (sst$level, 1);
	     n_pages = sst$pts (px);
	     do astx = 1 to bin (sst$level.no_aste (px));
		if aste.usedf & (aste.pvtx = pvtx)
		then if (aste.vtocx ^= -1) & ^aste.hc_part & ^aste.volmap_seg
		     then do;
			     call BUILD_FILEMAP_FROM_ASTE (astep, addr (aste_file_map), n_pages);
			     call CHECK_FILE_MAP ((aste.vtocx), addr (aste_file_map), n_pages, (aste.fm_damaged));
			     if aste.fm_damaged
			     then substr (damaged_vtoces, aste.vtocx + 1, 1) = "1"b;
			end;
		astep = addrel (astep, size (aste) + n_pages);
	     end;
	end;

     end WALK_SST;
%page;
/*  Internal Procedure to examine all VTOCEs on a volume. Each VTOCE is
   validated syntactically, and the file map is checked for conflicts.
   A list of VTOCEs to be freed and of damaged VTOCEs is maintained. However,
   none are freed or damaged in this pass. The goal is to minimize the
   time through this pass, due to the overhead in Page Control deposit/
   withdraw while this is going on.

   Any free VTOCE is checked against the VTOC map. If it is not there,
   it is marked to-be-freed. This is an optimization. Any VTOCE in the
   VTOC map is not lost. It doesn't matter if we mark a VTOCE which is
   not lost to-be-freed, since we'll detect this later.

*/

WALK_VOLUME:
     proc;

dcl	bitx			fixed bin;
dcl	code			fixed bin (35);
dcl	free_it			bit (1) aligned;
dcl	1 local_vtoce		aligned like vtoce;
dcl	1 vtoce_file_map		aligned like file_map;
dcl	vtocx			fixed bin;
dcl	wordx			fixed bin;


	do vtocx = 0 to pvte.n_vtoce - 1;

	     if copy_options.debug
	     then if mod (vtocx, 1024) = 0
		then call syserr (ANNOUNCE, "scavenge_volume: Processing vtocx ^o on ^a.", vtocx, device_name);

	     sc_meters.n_vtoces = sc_meters.n_vtoces + 1;
	     call vtoc_man$get_vtoce (""b, pvtx, vtocx, ALL_PARTS, addr (local_vtoce), code);
	     if code ^= 0
	     then do;
		     call syserr$error_code (SEVERITY (LOG), code, "scavenge_volume: Error reading vtocx ^o on ^a",
			vtocx, device_name);
		     get_vtoce_errors = get_vtoce_errors + 1;
		     goto NEXT_VTOCE;
		end;

	     if copy_options.no_optimize
	     then call pxss$relinquish_priority;	/* Give up the CPU */
	     else if vtocx < pvte.n_vtoce - 1
	     then call vtoc_man$read_ahead_vtoce (""b, pvtx, vtocx + 1, ALL_PARTS, code);

	     if local_vtoce.uid = ""b
	     then do;
		     wordx = divide (vtocx, 32, 17);
		     bit_map_wordp = addr (vtoc_map.bit_map (wordx));
		     bitx = mod (vtocx, 32);
		     if substr (bit_map_word.bits, bitx + 1, 1) ^= "1"b
		     then substr (freed_vtoces, vtocx + 1, 1) = "1"b;
		     goto NEXT_VTOCE;
		end;

	     call CHECK_VTOCE_FOR_FREE (addr (local_vtoce), "0"b, (""), free_it);
	     if free_it
	     then substr (freed_vtoces, vtocx + 1, 1) = "1"b;

	     call CHECK_VTOCE (vtocx, addr (local_vtoce), "0"b, ("0"b));
	     if local_vtoce.damaged | local_vtoce.fm_damaged
	     then substr (damaged_vtoces, vtocx + 1, 1) = "1"b;

	     call BUILD_FILEMAP_FROM_VTOCE (addr (local_vtoce), addr (vtoce_file_map));
	     call CHECK_FILE_MAP (vtocx, addr (vtoce_file_map), bin (local_vtoce.csl), (local_vtoce.fm_damaged));

NEXT_VTOCE:
	     if get_vtoce_errors > GET_VTOCE_ERROR_THRESHOLD
	     then do;
		     pvte.scav_check_address = "0"b;
		     Code = error_table_$scavenge_aborted;
		     goto ABORT_JOIN;
		end;
	     call CHECK_ABORT;
	end;



     end WALK_VOLUME;

%page;
/*  Internal Procedure to construct a file map from an ASTE. The AST and Page
   Table Locks must be held.
*/

BUILD_FILEMAP_FROM_ASTE:
     proc (Astep, File_Mapp, PT_Size);

dcl	Astep			ptr;
dcl	File_Mapp			ptr;
dcl	PT_Size			fixed bin;

dcl	page_tablep		ptr;
dcl	px			fixed bin;

dcl	1 Aste			aligned like aste based (Astep);
dcl	1 File_Map		aligned like file_map based (File_Mapp);

	page_tablep = addrel (Astep, size (Aste));
	unspec (File_Map) = ""b;

	do px = 0 to PT_Size - 1;
	     ptp = addrel (page_tablep, px);
	     if l68_ptw.flags.add_type = ""b
	     then File_Map.fm (px) = 0;
	     else if l68_ptw.flags.add_type = add_type.core
	     then do;
		     cmep = addr (sst$cmp -> cma (l68_core_ptw.frame));
		     if mcme.add_type = add_type.disk
		     then File_Map.fm (px) = bin (substr (mcme.record_no, 2, 17));
		     else File_Map.fm (px) = 0;
		end;
	     else if l68_ptw.flags.add_type = add_type.disk
	     then File_Map.fm (px) = bin (substr (l68_ptw.add, 2, 17));
	     else File_Map.fm (px) = 0;
	end;

     end BUILD_FILEMAP_FROM_ASTE;
%page;
/*  Internal Procedure to construct a file map from a VTOCE */

BUILD_FILEMAP_FROM_VTOCE:
     proc (Vtocep, File_Mapp);

dcl	Vtocep			ptr;
dcl	File_Mapp			ptr;

dcl	px			fixed bin;

dcl	1 File_Map		aligned like file_map based (File_Mapp);
dcl	1 Vtoce			aligned like vtoce based (Vtocep);


	do px = 0 to hbound (Vtoce.fm, 1);
	     if substr (Vtoce.fm (px), 1, 1) = "1"b
	     then File_Map.fm (px) = 0;
	     else File_Map.fm (px) = bin (Vtoce.fm (px), 18);
	end;

     end BUILD_FILEMAP_FROM_VTOCE;


%page;
/*  Internal Procedure to examine the list of VTOCEs marked as damaged
   during the first pass. Each is checked again for damage under the
   AST lock (if the segment is active it is skipped, since activate
   checks most of the fields we check). If damage is found, the
   segment is damaged for real, and messages dumped into the syserr
   log.
*/
CHECK_VTOCE_DAMAGE:
     proc;

dcl	code			fixed bin (35);
dcl	1 copy_vtoce		aligned like vtoce;
dcl	damaged_sw		bit (1) aligned;
dcl	done_damage		bit (1) aligned;
dcl	1 local_vtoce		aligned like vtoce;
dcl	prev_damaged		bit (1) aligned;
dcl	prev_fm_damaged		bit (1) aligned;
dcl	vtocx			fixed bin;


	vtoce_bitsp = addr (damaged_vtoces);

	base_vtocx = 0;
	done_damage = "0"b;

	do while (^done_damage);

	     vtocx = index (vtoce_bits.remaining, "1"b);
	     if vtocx = 0
	     then do;				/* None left */
		     done_damage = "1"b;
		     goto NEXT;
		end;

	     vtocx = vtocx + base_vtocx - 1;
	     base_vtocx = vtocx + 1;
	     call vtoc_man$get_vtoce (""b, pvtx, vtocx, ALL_PARTS, addr (local_vtoce), code);
	     if code ^= 0
	     then do;
		     call syserr$error_code (SEVERITY (LOG), "scavenge_volume: Error reading vtocx ^o on ^a", vtocx,
			device_name);
		     goto NEXT;
		end;

	     if copy_options.dump
	     then unspec (copy_vtoce) = unspec (local_vtoce);

	     call LOCK_AST_CHECK_UID ((local_vtoce.uid), vtocx, astep);
	     if astep = null ()
	     then do;				/* Only non-active segments. lock prevent activation */

		     prev_damaged = local_vtoce.damaged;
		     call CHECK_VTOCE (vtocx, addr (local_vtoce), "1"b, damaged_sw);

		     if local_vtoce.damaged
		     then vtoces_damaged = vtoces_damaged + 1;
		     if (local_vtoce.damaged & ^prev_damaged)
		     then vtoces_damaged_by_me = vtoces_damaged_by_me + 1;

		     if damaged_sw | prev_damaged
		     then call SEGDAMAGE (vtocx, addr (local_vtoce), addr (copy_vtoce), prev_damaged);

		     if local_vtoce.fm_damaged
		     then do;
			     prev_fm_damaged = "1"b;
			     local_vtoce.fm_damaged = "0"b;
			     if sst$checksum_filemap ^= 0
			     then do;
				     call filemap_checksum_ (addr (local_vtoce.fm), fixed (local_vtoce.csl),
					local_vtoce.fm_checksum);
				     local_vtoce.fm_checksum_valid = "1"b;
				end;
			     else do;
				     local_vtoce.fm_checksum_valid = "0"b;
				     local_vtoce.fm_checksum = ""b;
				end;
			     sc_meters.n_vtoces_fmd = sc_meters.n_vtoces_fmd + 1;
			end;

		     if damaged_sw | prev_fm_damaged
		     then do;
			     call vtoc_man$put_vtoce (""b, pvtx, vtocx, ALL_PARTS, addr (local_vtoce), code);
			     if code ^= 0
			     then call syserr$error_code (SEVERITY (LOG), code,
				     "scavenge_volume: Error writing vtocx ^a on ^a.", vtocx, device_name);
			end;

		end;
	     else do;				/* active */
		     if aste.fm_damaged
		     then do;
			     sc_meters.n_vtoces_fmd = sc_meters.n_vtoces_fmd + 1;
			     aste.fm_damaged = "0"b;
			end;
		end;

	     call lock$unlock_ast;

NEXT:
	end;




     end CHECK_VTOCE_DAMAGE;
%page;
/*  Internal Procedure to free any records which are unseen. Any unseen
   records are known not to be claimed by any VTOCE and not to be in
   the Volume Map. It is safe to free these without further examination
   or adieu.
*/

FREE_RECORDS:
     proc;

dcl	1 deposit_list		(256) aligned,
	  2 record		fixed bin (18) uns unal,
	  2 pad			bit (18) unal;
dcl	dx			fixed bin;
dcl	records_freed		fixed bin;
dcl	rx			fixed bin;

	unspec (deposit_list) = ""b;
	records_freed = 0;
	dx = 0;

	do rx = 1 to scavenger_block.n_records;

	     record_blockp = addr (scavenger_block.records (rx));
	     if record_block.state = STATE_UNSEEN
	     then do;
		     dx = dx + 1;
		     records_freed = records_freed + 1;
		     deposit_list (dx).record = rx + pvte.baseadd - 1;
		     if dx >= hbound (deposit_list, 1)
		     then do;
			     call pc$deposit_list (pvtx, dx, addr (deposit_list), -1, null ());
			     dx = 0;
			     call CHECK_ABORT;
			end;
		end;

	end;

	if dx > 0
	then call pc$deposit_list (pvtx, dx, addr (deposit_list), -1, null ());

	records_freed = records_freed - conflicts_unclaimed;
						/* Adjust for those really lost */
	sc_meters.n_lost_records = records_freed;
	if records_freed > 0
	then call syserr (SEVERITY (LOG), "scavenge_volume: Freed ^d records on ^a", records_freed, device_name);

     end FREE_RECORDS;
%page;
/*  Internal Procedure to look for record addresses which have potential
   conflicts. A routine to resolve the conflict is called for each such.
   This driver routine is split out for logic clarity.
*/

RESOLVE_CONFLICTS:
     proc;

dcl	rx			fixed bin;


	do rx = 1 to scavenger_block.n_records;
	     record_blockp = addr (scavenger_block.records (rx));
	     if record_block.state = STATE_CONFLICT
	     then do;
		     call RESOLVE_THIS_CONFLICT ((rx - 1 + pvte.baseadd), record_blockp);
		     call CHECK_ABORT;
		end;
	end;

     end RESOLVE_CONFLICTS;
%page;
/*  Internal Procedure to resolve conflicts for a given record address.
   This routine is the real hair of the scavenger, the complexity due
   to the many races, both here and in the first pass (which flagged
   the potential conflicts).

   The logic is as follows:

   1. Make sure that the address is not marked as free in the
   stock or Volume Map (using a special page control entry for us).

   2. Walk the thread of potential conflicts. For each entry, note
   whether the conflict in fact exists. Of those segments still
   claiming the address, select at most one to get it, according
   to the following rules:

   If only one segment claims the address, it gets it.

   If only one directory claims the address, it gets it. The
   directory salvager is presumed capable of straightening
   any mess out.

   If only one segment which has no record of file map damage
   claims the address, it gets it.

   Otherwise nobody gets it. Note that our covenant with page control
   deposit/withdraw prevents any segment from getting this address other
   than the ones we know about.

   3. Walk the thread of potential conflicts again. For each entry which
   doesn't get the address, attempt to bust the address loose.
   A setfaults is used to disconnect the segment from active processes.
   If the segment is entry-held, we log a message and give up. The
   address is nulled, and the segment is damaged.
   This is all done under the AST lock.

   4. Under the AST lock, check whether the lucky segment which gets
   the address still has it. If not, deposit it after setting the
   state "unseen" (so that the deposit takes).

   It is assumed that conflicts are relatively rare, and that efficiency
   can be sacrificed for a modicum of simplicity.
*/

RESOLVE_THIS_CONFLICT:
     proc (Record_Address, Record_Blockp);

dcl	Record_Address		fixed bin (18);
dcl	Record_Blockp		ptr;

dcl	checksum			bit (36) aligned;
dcl	claim_count		fixed bin;
dcl	claims_address		bit (1) aligned;
dcl	code			fixed bin (35);
dcl	conflict_blockp		ptr;
dcl	1 copy_vtoce		aligned like vtoce;
dcl	csl			fixed bin;
dcl	dir_count			fixed bin;
dcl	done_thread		bit (1) aligned;
dcl	1 local_file_map		aligned like file_map;
dcl	1 local_vtoce		aligned like vtoce;
dcl	not_fmd_count		fixed bin;
dcl	owner_pageno		fixed bin;
dcl	owner_vtocx		fixed bin;
dcl	pageno			fixed bin;
dcl	prev_damaged		bit (1) aligned;
dcl	real_conflict		bit (1) aligned;
dcl	records			fixed bin;
dcl	vtocx			fixed bin;

dcl	1 conflict_block		aligned like record_block based (conflict_blockp);
dcl	1 Record_Block		aligned like record_block based (Record_Blockp);


/* Free the address. Check whether each conflict is real, and select an
   owner for the address. */

	sc_meters.n_conflicts = sc_meters.n_conflicts + 1;
	real_conflict = "0"b;

	conflict_blockp = Record_Blockp;

	call page$free_address_for_scavenge (pvtx, Record_Address);

	dir_count, claim_count, not_fmd_count = 0;
	owner_vtocx = -1;
	done_thread = "0"b;
	do while (^done_thread);

	     vtocx = conflict_block.vtocx;
	     pageno = conflict_block.pageno;

	     call GET_VTOCE_CHECK_ADDRESS (vtocx, addr (local_vtoce), pageno, Record_Address, astep, claims_address,
		code);
	     if code ^= 0
	     then do;
ERROR_PUNT:
		     call syserr$error_code (SEVERITY (LOG), code,
			"scavenge_volume: Unable to resolve conflict for address ^o on ^a. Error at vtocx ^o.",
			Record_Address, device_name, vtocx);
		     get_vtoce_errors = get_vtoce_errors + 1;
		     return;
		end;
	     if claims_address
	     then do;
		     call lock$unlock_ast;
		     claim_count = claim_count + 1;
		     if local_vtoce.dirsw
		     then dir_count = dir_count + 1;
		     if ^local_vtoce.fm_damaged & local_vtoce.fm_checksum_valid
		     then do;
			     call filemap_checksum_ (addr (local_vtoce.fm), fixed (local_vtoce.csl, 9), checksum);
			     if local_vtoce.fm_checksum ^= checksum
			     then local_vtoce.fm_damaged = "1"b;
			end;
		     if ^local_vtoce.fm_damaged
		     then not_fmd_count = not_fmd_count + 1;
		     if (claim_count = 1) | (local_vtoce.dirsw & (dir_count = 1))
			| (^local_vtoce.fm_damaged & (not_fmd_count = 1) & (dir_count = 0))
		     then do;
			     owner_vtocx = vtocx;
			     owner_pageno = pageno;
			end;
		end;
	     if conflict_block.ovflx = 0
	     then done_thread = "1"b;
	     else conflict_blockp = addr (scavenger_block.overflow (conflict_block.ovflx));

	end;


/*  If there are no claimants, mark the address as unseen. This will
   cause it to be freed later.
*/

	if claim_count = 0
	then do;
FREE_RETURN:
		call LOCK_RECORD (record_blockp);
		Record_Block.state = STATE_UNSEEN;
		record_block.lock = "0"b;
		conflicts_unclaimed = conflicts_unclaimed + 1;
		return;
	     end;

%page;
/*  Walk the list of conflicts, and attempt to bust the address loose
   from all except the owner. Note that there may be no owner.  */

	done_thread = "0"b;
	conflict_blockp = Record_Blockp;

	if (claim_count > 1) & (dir_count ^= 1) & (not_fmd_count ^= 1)
	then owner_vtocx = -1;

	do while (^done_thread);

	     vtocx = conflict_block.vtocx;
	     pageno = conflict_block.pageno;
	     if ((vtocx ^= owner_vtocx) | (pageno ^= owner_pageno))
	     then do;

		     call GET_VTOCE_CHECK_ADDRESS (vtocx, addr (local_vtoce), pageno, Record_Address, astep,
			claims_address, code);
		     if code ^= 0
		     then goto ERROR_PUNT;
		     if ^claims_address
		     then goto NEXT;
		     real_conflict = "1"b;

		     if copy_options.dump
		     then unspec (copy_vtoce) = unspec (local_vtoce);

		     if astep ^= null ()
		     then do;
			     if aste.ehs | aste.hc_sdw | aste.hc | aste.hc_part
			     then do;
PUNT_ASTE:
				     call lock$unlock_ast;
				     call syserr (SEVERITY (LOG),
					"scavenge_volume: Unable to resolve conflict for address ^o on ^a. astep=^p"
					, Record_Address, device_name, astep);
				     return;
				end;
			     call setfaults (astep, "0"b);
			     call pc$cleanup (astep);
			     ptp = addrel (astep, size (aste) + pageno);
			     if l68_ptw.add_type ^= add_type.disk
			     then goto PUNT_ASTE;
			     l68_ptw.add_type = ""b;
			     l68_ptw.add = pv_scav_null_addr;
			     prev_damaged = aste.damaged;
			     aste.damaged = "1"b;
			     aste.fmchanged = "1"b;

			     call BUILD_FILEMAP_FROM_ASTE (astep, addr (local_file_map), sst$pts (bin (aste.ptsi)));
			     call COMPUTE_RECORDS_CSL (addr (local_file_map), records, csl);
			     aste.records = bit (bin (records, 9), 9);
			     aste.csl = bit (bin (csl, 9), 9);

			     call update_vtoce (astep);
						/* Reflect into VTOCE */

			end;
		     else do;
			     call vtoc_man$get_vtoce (""b, pvtx, vtocx, ALL_PARTS, addr (local_vtoce), code);
			     if code ^= 0
			     then do;
				     call lock$unlock_ast;
				     goto ERROR_PUNT;
				end;
			     prev_damaged = local_vtoce.damaged;
			     local_vtoce.fm (pageno) = pv_scav_null_addr;
			     call BUILD_FILEMAP_FROM_VTOCE (addr (local_vtoce), addr (local_file_map));
			     call COMPUTE_RECORDS_CSL (addr (local_file_map), records, csl);
			     local_vtoce.csl = bit (bin (csl, 9), 9);
			     local_vtoce.records = bit (bin (records, 9), 9);
			     if sst$checksum_filemap = 0
			     then do;
				     local_vtoce.fm_checksum_valid = "0"b;
				     local_vtoce.fm_checksum = ""b;
				end;
			     else do;
				     local_vtoce.fm_checksum_valid = "1"b;
				     call filemap_checksum_ (addr (local_vtoce.fm), csl, local_vtoce.fm_checksum);
				end;

			     local_vtoce.damaged = "1"b;
			     call vtoc_man$put_vtoce (""b, pvtx, vtocx, ALL_PARTS, addr (local_vtoce), code);
			     if code ^= 0
			     then do;
				     call lock$unlock_ast;
				     goto ERROR_PUNT;
				end;
			end;
		     call lock$unlock_ast;
		     call syserr (SEVERITY (LOG),
			"scavenge_volume: vtoce ^a at ^o (^a). ref to pageno ^o at addr ^o deleted",
			local_vtoce.primary_name, vtocx, device_name, pageno, Record_Address);
		     if ^prev_damaged
		     then do;
			     call SEGDAMAGE (vtocx, addr (local_vtoce), addr (copy_vtoce), prev_damaged);
			     vtoces_damaged = vtoces_damaged + 1;
			     vtoces_damaged_by_me = vtoces_damaged_by_me + 1;
			end;
		end;

NEXT:
	     if conflict_block.ovflx = 0
	     then done_thread = "1"b;
	     else conflict_blockp = addr (scavenger_block.overflow (conflict_block.ovflx));
	end;

	if real_conflict
	then sc_meters.n_real_conflicts = sc_meters.n_real_conflicts + 1;
%page;
/*  Now check whether the owner (if any) still claims the address. If not,
   free it.  */

	if owner_vtocx = -1
	then do;
		call LOCK_RECORD (record_blockp);
		record_block.state = STATE_UNSEEN;
		record_block.lock = "0"b;
		return;
	     end;

	call GET_VTOCE_CHECK_ADDRESS (owner_vtocx, addr (local_vtoce), owner_pageno, Record_Address, astep,
	     claims_address, code);
	if code ^= 0
	then return;


	if ^claims_address
	then goto FREE_RETURN;

	call lock$unlock_ast;

     end RESOLVE_THIS_CONFLICT;

%page;
/*  Internal Procedure to recompute the number of records and current
   segment length from a file map.
*/

COMPUTE_RECORDS_CSL:
     proc (File_Mapp, Records, Csl);

dcl	File_Mapp			ptr;
dcl	Records			fixed bin;
dcl	Csl			fixed bin;

dcl	px			fixed bin;

dcl	1 File_Map		aligned like file_map based (File_Mapp);

	Records, Csl = 0;
	do px = 0 to hbound (File_Map.fm, 1);
	     if File_Map.fm (px) ^= 0
	     then do;
		     Csl = px + 1;
		     Records = Records + 1;
		end;
	end;

     end COMPUTE_RECORDS_CSL;
%page;
/*  Internal Procedure to read a VTOCE, check whether the segment is active,
   and whether the segment claims a specified address for a certain page.
   The AST is locked, and left locked if the address is claimed.
*/
GET_VTOCE_CHECK_ADDRESS:
     proc (Vtocx, Vtocep, Pageno, Record_Address, Astep, Claims, Code);

dcl	Vtocx			fixed bin;
dcl	Vtocep			ptr;
dcl	Pageno			fixed bin;
dcl	Record_Address		fixed bin (18);
dcl	Astep			ptr;
dcl	Claims			bit (1) aligned;
dcl	Code			fixed bin (35);

dcl	old_mask			fixed bin (71);
dcl	ptwp			ptr;

dcl	1 Vtoce			aligned like vtoce based (Vtocep);


	Code = 0;
	Astep = null ();
	Claims = "0"b;

	call vtoc_man$get_vtoce (""b, pvtx, Vtocx, ALL_PARTS, Vtocep, Code);
	if Code ^= 0
	then return;

	call LOCK_AST_CHECK_UID ((Vtoce.uid), Vtocx, Astep);
	if Astep = null ()
	then do;
		if substr (Vtoce.fm (Pageno), 1, 1) = "0"b
		then if bin (Vtoce.fm (Pageno), 18) = Record_Address
		     then Claims = "1"b;
	     end;
	else do;
		if Pageno < sst$pts (fixed (Astep -> aste.ptsi, 3))
						/* silly scoping */
		then do;
			ptp = addrel (Astep, size (aste) + Pageno);
			call pmut$lock_ptl (old_mask, ptwp);
			if l68_ptw.flags.add_type = add_type.core
			then do;
				cmep = addr (sst$cmp -> cma (l68_core_ptw.frame));
				if mcme.add_type = add_type.disk
				then if bin (substr (mcme.record_no, 2, 17)) = Record_Address
				     then Claims = "1"b;
			     end;
			else if l68_ptw.flags.add_type = add_type.disk
			then if bin (substr (l68_ptw.add, 2, 17)) = Record_Address
			     then Claims = "1"b;
			call pmut$unlock_ptl (old_mask, ptwp);
		     end;
	     end;

	if ^Claims
	then call lock$unlock_ast;


     end GET_VTOCE_CHECK_ADDRESS;


%page;
/*  Internal Procedure to examine the list of VTOCEs to be freed which
   were found in the first pass. Each is examined again.
   If not active and still to-be-freed, it is freed. Potentially lost
   VTOCEs are freed using a special entry to vtoc_man to avoid the
   race with an in-progress allocation. Other VTOCEs are freed by
   priv_delete_vtoce, which  protects against races and active segments.
*/
FREE_VTOCES:
     proc;

dcl	code			fixed bin (35);
dcl	done_free			bit (1) aligned;
dcl	free_it			bit (1) aligned;
dcl	1 local_vtoce		aligned like vtoce;
dcl	message			char (32);
dcl	vtoces_freed		fixed bin;
dcl	vtocx			fixed bin;


	vtoces_freed = 0;
	vtoce_bitsp = addr (freed_vtoces);

	base_vtocx = 0;
	done_free = "0"b;

	do while (^done_free);

	     vtocx = index (vtoce_bits.remaining, "1"b);
	     if vtocx = 0
	     then do;				/* None left */
		     done_free = "1"b;
		     goto NEXT;
		end;

	     vtocx = vtocx + base_vtocx - 1;
	     base_vtocx = vtocx + 1;
	     call vtoc_man$get_vtoce (""b, pvtx, vtocx, ALL_PARTS, addr (local_vtoce), code);
	     if code ^= 0
	     then do;
		     call syserr$error_code (SEVERITY (LOG), code, "scavenge_volume: Error reading vtocx ^a on ^a.",
			vtocx, device_name);
		     get_vtoce_errors = get_vtoce_errors + 1;
		     goto NEXT;
		end;


	     call CHECK_VTOCE_FOR_FREE (addr (local_vtoce), "1"b, message, free_it);
	     if free_it
	     then do;

		     if local_vtoce.uid = ""b
		     then call vtoc_man$free_vtoce_for_scavenge (""b, pvtx, vtocx, code);

		     else call priv_delete_vtoce ((local_vtoce.uid), pvte.pvid, vtocx, code);

		     if code = 0
		     then do;
			     vtoces_freed = vtoces_freed + 1;
			     if copy_options.debug
			     then call syserr (SEVERITY (JUST_LOG),
				     "scavenge_volume: Freeing ^a VTOCE ^[^a ^;^1s^]at ^o (^a)", message,
				     (local_vtoce.uid ^= ""b), local_vtoce.primary_name, vtocx, device_name);
			end;
		     else if code ^= error_table_$vtoce_free
		     then call syserr$error_code (SEVERITY (LOG), code,
			     "scavenge_volume: Error freeing ^a vtocx ^o on ^a", message, vtocx, device_name);


		end;

NEXT:
	     call CHECK_ABORT;
	end;

	sc_meters.n_vtoces_freed = vtoces_freed;
	if vtoces_freed > 0
	then call syserr (SEVERITY (LOG), "scavenge_volume: Freed ^d VTOCEs on ^a", vtoces_freed, device_name);

     end FREE_VTOCES;

%page;
/*  Internal Procedure to lock the AST Lock and find the ASTE (if any)
   corresponding to a supplied UID.
*/
LOCK_AST_CHECK_UID:
     proc (Uid, Vtocx, Astep);

dcl	Uid			bit (36) aligned;
dcl	Vtocx			fixed bin;
dcl	Astep			ptr;


	call lock$lock_ast;
	if Uid = ""b
	then Astep = null ();
	else Astep = search_ast$check (Uid, pvte.pvid, Vtocx, (0));
						/* use global pvid (!), ignore double-uid error */

     end LOCK_AST_CHECK_UID;



%page;
/*  Internal Procedure to validate a VTOCE. Invalid fields are corrected,
   to the extent possible. If the corrections are "for real" (second
   pass), they are reported into the syserr log.
*/

CHECK_VTOCE:
     proc (Vtocx, Vtocep, Loud, Damaged);

dcl	Vtocx			fixed bin;
dcl	Vtocep			ptr;
dcl	Loud			bit (1) aligned;
dcl	Damaged			bit (1) aligned;

dcl	checksum			bit (36) aligned;
dcl	csl			fixed bin;
dcl	cur_fstime		bit (36) aligned;
dcl	low_add			fixed bin (18);
dcl	high_add			fixed bin (18);
dcl	px			fixed bin;
dcl	rec_add			fixed bin (18);
dcl	records			fixed bin;
dcl	trp_bad			bit (1) aligned;
dcl	tx			fixed bin;

dcl	1 Vtoce			aligned like vtoce based (Vtocep);


	Damaged = "0"b;

	low_add = pvte.baseadd;
	high_add = pvte.baseadd + pvte.totrec - 1;
	csl, records = 0;

	cur_fstime = substr (bit (bin (clock (), 71), 71), 20, 36);

	do px = 0 to hbound (Vtoce.fm, 1);
	     if substr (Vtoce.fm (px), 1, 1) = "0"b
	     then do;
		     rec_add = bin (Vtoce.fm (px), 18);
		     if rec_add < low_add | rec_add > high_add
		     then do;
			     Damaged = "1"b;
			     Vtoce.damaged = "1"b;
			     Vtoce.fm (px) = pv_scav_null_addr;
			     if Loud & copy_options.debug
			     then call syserr (SEVERITY (JUST_LOG),
				     "scavenge_volume: vtoce ^a at ^o (^a) disk addr ^o bad", Vtoce.primary_name,
				     Vtocx, pvte.devname, device_name, rec_add);
			end;
		     else do;
			     csl = px + 1;
			     records = records + 1;
			end;
		end;
	end;

	if bin (Vtoce.records) ^= records
	then do;
		if Loud
		then call syserr (SEVERITY (JUST_LOG),
			"scavnege_volume: vtoce ^a at ^o (^a). rec used changed from ^o to ^o", Vtoce.primary_name,
			Vtocx, device_name, bin (Vtoce.records), records);
		Damaged = "1"b;
		Vtoce.damaged = "1"b;
		Vtoce.records = bit (bin (records, 9), 9);
	     end;

	if bin (Vtoce.csl) ^= csl
	then do;
		if Loud
		then call syserr (SEVERITY (JUST_LOG),
			"scavenge_volume: vtoce ^a at ^o (^a). cur len changed from ^o to ^o", Vtoce.primary_name,
			Vtocx, device_name, bin (Vtoce.csl), csl);
		Damaged = "1"b;
		Vtoce.damaged = "1"b;
		Vtoce.csl = bit (bin (csl, 9), 9);
	     end;

	if (bin (Vtoce.msl) > 256) | (bin (Vtoce.msl) < 0) | (bin (Vtoce.msl) < csl)
	then do;
		if Loud
		then call syserr (SEVERITY (JUST_LOG),
			"scavenge_volume: vtoce ^a at ^o (^a). max len changed from ^o to 400", Vtoce.primary_name,
			Vtocx, device_name, bin (Vtoce.msl));
		Damaged = "1"b;
		Vtoce.damaged = "1"b;
		Vtoce.msl = bit (bin (256, 9), 9);
	     end;

	trp_bad = "0"b;
	do tx = 0 to 1;
	     if Vtoce.trp (tx) < 0 | bin (Vtoce.trp (tx), 36) > bin (cur_fstime, 36)
	     then trp_bad = "1"b;
	end;
	if trp_bad
	then do;
		do tx = 0 to 1;
		     Vtoce.trp (tx) = 0;
		     Vtoce.trp_time (tx) = cur_fstime;
		end;
		if Loud
		then call syserr (SEVERITY (JUST_LOG),
			"scavenge_volume: vtoce ^a at ^o (^a). time-record-product reset to zero",
			Vtoce.primary_name, Vtocx, device_name);
	     end;

	if Vtoce.dirsw & pvte.lvid ^= pvt$root_lvid
	then do;
		if Loud
		then call syserr (SEVERITY (JUST_LOG), "scavenge_volume: dirsw turned off for ^a at ^o (^a)",
			Vtoce.primary_name, Vtocx, device_name);
		Damaged = "1"b;
		Vtoce.damaged = "1"b;
		Vtoce.dirsw = "0"b;
	     end;

	if Vtoce.fm_checksum_valid
	then do;
		call filemap_checksum_ (addr (Vtoce.fm), fixed (Vtoce.csl, 9), checksum);
		if Vtoce.fm_checksum ^= checksum
		then do;
			Vtoce.fm_damaged = "1"b;
			if Loud & copy_options.debug
			then call syserr (SEVERITY (JUST_LOG),
				"scavenge_volume: Invalid File Map Checksum for ^a at ^o (^a)",
				Vtoce.primary_name, Vtocx, device_name);
		     end;
	     end;

     end CHECK_VTOCE;



%page;
/*  Internal Procedure which is the brains of the scavenger. It examines
   a file map and updates the scavenger data base for this volume. It
   is operating asynchronously on this data base along with page
   control deposit/withdraw.
*/

CHECK_FILE_MAP:
     proc (Vtocx, File_Mapp, N_Pages, Fm_Damaged);

dcl	Vtocx			fixed bin;
dcl	File_Mapp			ptr;
dcl	N_Pages			fixed bin;
dcl	Fm_Damaged		bit (1) aligned;

dcl	high_add			fixed bin (18);
dcl	low_add			fixed bin (18);
dcl	px			fixed bin;
dcl	rec_add			fixed bin (18);

dcl	1 File_Map		aligned like file_map based (File_Mapp);

	low_add = pvte.baseadd;
	high_add = pvte.baseadd + pvte.totrec - 1;
	do px = 0 to N_Pages - 1;
	     if File_Map.fm (px) ^= 0
	     then if (File_Map.fm (px) >= low_add) & (File_Map.fm (px) <= high_add)
		then do;
			rec_add = File_Map.fm (px) - pvte.baseadd + 1;
			sc_meters.n_records = sc_meters.n_records + 1;
			record_blockp = addr (scavenger_block.records (rec_add));

/*  Lock the record block */

			call LOCK_RECORD (record_blockp);


/*  Update scavenger data for this record. If the File Map is potentially
   damaged (invalid checksum at some point), all record addresses must
   be marked as conflicts, since they are not deposited. This way, truncations
   happening at the same time as the scavenge will not result in lost
   addresses.  */

			if Fm_Damaged
			then do;
				call THREAD_IN_CONFLICT (record_blockp, Vtocx, px);
				sc_meters.n_fmd_conflicts = sc_meters.n_fmd_conflicts + 1;
			     end;
			else if record_block.state = STATE_UNSEEN
			then do;
				record_block.state = STATE_IN_USE;
				record_block.vtocx = Vtocx;
				record_block.pageno = px;
			     end;
			else if record_block.state = STATE_IN_USE
			then do;
				if record_block.vtocx ^= Vtocx | record_block.pageno ^= px
				then call THREAD_IN_CONFLICT (record_blockp, Vtocx, px);
			     end;
			else if record_block.state = STATE_FREE | record_block.state = STATE_CONFLICT
			then call THREAD_IN_CONFLICT (record_blockp, Vtocx, px);


			record_block.lock = "0"b;
		     end;
	end;
     end CHECK_FILE_MAP;
%page;
/*  Internal Procedure to check whether a VTOCE should be freed, or perhaps
   already is free.
*/

CHECK_VTOCE_FOR_FREE:
     proc (Vtocep, Meter_It, Reason, Free_It);

dcl	Vtocep			ptr;
dcl	Reason			char (*);
dcl	Meter_It			bit (1) aligned;
dcl	Free_It			bit (1) aligned;

dcl	1 Vtoce			aligned like vtoce based (Vtocep);


	Free_It = "0"b;

	if Vtoce.uid = ""b
	then do;
		Free_It = "1"b;
		Reason = "lost";
	     end;
	else if ^Vtoce.dirsw
	then do;
		if Vtoce.per_process & (Vtoce.uid_path (1) ^= active_hardcore_data$pdd_uid)
		then do;
			Free_It = "1"b;
			Reason = "per-process";
			if Meter_It
			then sc_meters.n_vtoces_per_proc = sc_meters.n_vtoces_per_proc + 1;
		     end;
		else if Vtoce.deciduous & (Vtoce.uid_path (1) ^= active_hardcore_data$sl1_uid)
		then do;
			Free_It = "1"b;
			Reason = "deciduous";
			if Meter_It
			then sc_meters.n_vtoces_per_boot = sc_meters.n_vtoces_per_boot + 1;
		     end;
		else if Vtoce.perm_flags.per_bootload & (Vtoce.uid_path (1) ^= active_hardcore_data$sl1_uid)
		then do;
			Free_It = "1"b;
			Reason = "per-bootload";
			if Meter_It
			then sc_meters.n_vtoces_per_boot = sc_meters.n_vtoces_per_boot + 1;
		     end;
	     end;

     end CHECK_VTOCE_FOR_FREE;
%page;
/*  Internal Procedure to report segment damage to the syserr log  */

SEGDAMAGE:
     proc (Vtocx, Vtocep, Dump_Vtocep, Prev_Damaged);

dcl	Vtocx			fixed bin;
dcl	Vtocep			ptr;
dcl	Dump_Vtocep		ptr;
dcl	Prev_Damaged		bit (1) aligned;

dcl	1 Vtoce			aligned like vtoce based (Vtocep);


	segdamage.pvid = pvte.pvid;
	segdamage.lvid = pvte.lvid;
	segdamage.uid = Vtoce.uid;
	segdamage.vtocx = Vtocx;
	segdamage.pno = -1;
	segdamage.uid_path = Vtoce.uid_path;
	call syserr$binary (SEVERITY (LOG), addr (segdamage), SB_vtoc_salv_dam, SBL_vtoc_salv_dam,
	     "scavenge_volume: ^[damaged switch found on for^;setting damaged switch on^] ^a at ^o (^a).", Prev_Damaged,
	     Vtoce.primary_name, Vtocx, device_name);

	if ^Prev_Damaged
	then do;
		sst$damaged_ct = sst$damaged_ct + 1;
		sc_meters.n_vtoces_damaged = sc_meters.n_vtoces_damaged + 1;
		if copy_options.dump
		then call syserr$binary (SEVERITY (JUST_LOG), Dump_Vtocep, SB_vtoce, SBL_vtoce,
			"scavenge_volume: Damaged vtoce ^o (^a).", Vtocx, device_name);
	     end;


%include segdamage_msg;

     end SEGDAMAGE;
%page;
/*  Internal procedure to lock a record address  */

LOCK_RECORD:
     proc (Record_Blockp);

dcl	Record_Blockp		ptr;

dcl	1 A_record_block		aligned like record_block;
dcl	Ap			ptr;
dcl	locked			bit (1) aligned;
dcl	1 Q_record_block		aligned like record_block;
dcl	Qp			ptr;
dcl	Wp			ptr;

dcl	A			bit (36) aligned based (Ap);
dcl	Q			bit (36) aligned based (Qp);
dcl	1 Record_Block		aligned like record_block based (Record_Blockp);
dcl	W			bit (36) aligned based (Wp);


	locked = "0"b;
	Ap = addr (A_record_block);
	Qp = addr (Q_record_block);
	Wp = Record_Blockp;
	do while (^locked);
	     unspec (Q_record_block) = unspec (Record_Block);
	     unspec (A_record_block) = unspec (Q_record_block);
	     if ^A_record_block.lock
	     then do;
		     A_record_block.lock = "1"b;
		     locked = stacq (W, A, Q);
		end;
	end;

     end LOCK_RECORD;
%page;
/*  Internal Procedure to check whether the invoker requested an abort. */

CHECK_ABORT:
     proc;

	call tc_util$check_abort (Code);
	if Code ^= 0
	then goto ABORT_JOIN;

     end CHECK_ABORT;
%page;
/*  Internal Procedure to translate syserr severity codes, based on
   attendant circumstances. Only severity codes of 4 or higher are
   affected, as follows:

   debug option in effect - all messages are printed

   scavenge during initialization - 4 becomes 0, 5 becomes 4

   otherwise 5 becomes 4.
*/

SEVERITY:
     proc (Severity) returns (fixed bin);

dcl	Severity			fixed bin;

	if Severity <= BEEP
	then return (Severity);

	if copy_options.debug
	then return (ANNOUNCE);

	if sys_info$initialization_state < RLV_INITIALIZED
	then do;
		if Severity = LOG
		then return (ANNOUNCE);
		else return (LOG);
	     end;

	return (LOG);


     end SEVERITY;


%page;
/*  Internal Procedure to add an entry to the thread of conflicts for this
   record address.
*/

THREAD_IN_CONFLICT:
     proc (Record_Blockp, Vtocx, Page_No);

dcl	Record_Blockp		ptr;
dcl	Vtocx			fixed bin;
dcl	Page_No			fixed bin;

dcl	A			bit (36) aligned;
dcl	free_ix			fixed bin;
dcl	got_free			bit (1) aligned;
dcl	next_free			fixed bin;
dcl	ovfl_blockp		ptr;
dcl	Q			bit (36) aligned;
dcl	Wp			ptr;

dcl	1 ovfl_block		aligned like record_block based (ovfl_blockp);
dcl	1 Record_Block		aligned like record_block based (Record_Blockp);
dcl	W			bit (36) aligned based (Wp);

	if (Record_Block.state = STATE_UNSEEN) | (Record_Block.state = STATE_FREE)
	then do;
		Record_Block.vtocx = Vtocx;
		Record_Block.pageno = Page_No;
	     end;
	else if (Record_Block.vtocx ^= Vtocx) | (Record_Block.pageno ^= Page_No)
	then do;

		got_free = "0"b;
		do while (^got_free);
		     free_ix = scavenger_block.ovfl_free_ix;
		     if free_ix <= 0 | free_ix > scavenger_block.n_ovfl
		     then do;			/* Fix damage so far and restart */
			     call syserr (ANNOUNCE, "scavenge_volume: Overflow on scavenge of ^a. Restarting.",
				device_name);
			     restart_sw = "1"b;
			     Record_Block.lock = "0"b;
			     goto RECOVER_OVERFLOW;
			end;
		     next_free = free_ix + 1;
		     Q = bit (bin (free_ix, 36), 36);
		     A = bit (bin (next_free, 36), 36);
		     Wp = addr (scavenger_block.ovfl_free_ix);
		     got_free = stacq (W, A, Q);
		end;

		ovfl_blockp = addr (scavenger_block.overflow (free_ix));

		ovfl_block.vtocx = Vtocx;
		ovfl_block.pageno = Page_No;
		ovfl_block.state = STATE_CONFLICT;
		ovfl_block.ovflx = Record_Block.ovflx;
		Record_Block.ovflx = free_ix;
	     end;

	Record_Block.state = STATE_CONFLICT;


     end THREAD_IN_CONFLICT;

FAULT:
     procedure (Lock_name);

declare	Lock_name			char (*);

declare	faulting_ptr		pointer;
declare	1 faulting_ptr_under_construction
				aligned like its_unsigned;
declare	foo			bit (36) aligned;	/* any old thing */
declare	based_foo			bit (36) aligned based;

	unspec (faulting_ptr_under_construction) = ""b;
	faulting_ptr_under_construction.its_mod = "47"b3; /* DF 2 */
	unspec (faulting_ptr) = unspec (faulting_ptr_under_construction);

	call syserr (BEEP, "scavenge_volume: Faulting under the ^a for debugging for ^a.", Lock_name,
	     pds$process_group_id);

	foo = faulting_ptr -> based_foo;
	return;
     end FAULT;

/* format: off */
%page;  %include add_type;
%page;  %include aste;
%page;  %include cmp;
%page;  %include disk_pack;
%page;  %include its;
%page;  %include null_addresses;
%page;  %include "ptw.l68";
%page;  %include pvte;
%page;  %include scavenger_data;
%page;  %include stock_seg;
%page;  %include syserr_binary_def;
%page;  %include syserr_constants;
%page;  %include vol_map;
%page;  %include vtoc_map;
%page;  %include vtoce;
%page;
/* BEGIN MESSAGE DOCUMENTATION

   Message:
   scavenge_volume: NNN errors reading VTOCEs on dskX_NN{S}

   S:	$info

   T: 	During a physical volume scavenge of dskX_NN{S}

   M:	Errors were encountered reading VTOCEs. A message describing
   each error was recorded into the syserr log. The VTOCEs with errors
   are skipped, and lost records are not recovered.

   A:	When the volume has been repaired, rerun the scavenger to 
   recover the lost records.

   Message:
   scavenge_volume: NNN VTOCEs on dskX_NN{S} damaged. MMM damaged during this scavenge.

   S:	$info

   T:	At the completion of a volume scavenge of dskX_NN{S}

   M:	The scavenger found a total of NNN damaged segments on the volume.
   Of these, MMM were damaged by the scavenger due to inconsistencies in the
   VTOCEs for these segments. A message for each was recorded into the
   syserr log.

   A:	Examine the syserr log to find the damaged segments and recover them.

   Message:
   scavenge_volume: Debug Trap on scavenge of dskX_NN{S}

   S:	$crash

   T:	At the completion of a volume scavenge of dskX_NN{S}

   M:	This is a debugging trap which results when scavenge_vol
   is used with the -trap control argument.

   A:	Type GO to resume Multics operation.

   Message:
   scavenge_volume: Out-of-service address in stock for dskX_NN{S}

   S:	$crash

   T:	During a scavenge of dskX_NN{S}

   M:	While examining the record stock, and out-of-service address was
   encountered.  Due to the volume map locking protocols used, this should
   never happen. It indicates hardware or software malfunction.

   A:	$recover

   Message:	
   scavenge_volume: Processing vtocx NNNNN on dskX_NN{S}

   S:	$info

   T:	During a volume scavenge of dskX_NN{S}

   M:	This message is printed every 2000 (octal) VTOCEs during the
   scan of the VTOC by the scavenger if scavenge_vol was invoked with
   the -debug option.

   A:	$ignore

   Message:
   scavenge_volume: Error reading vtocx NNNN on dskX_NN{S}. ERRORMESSAGE

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	An error was encountered attempting to read the VTOCE indicated.
   The VTOCE is skipped, and lost records are not recovered. The count
   of volume inconsistencies is not reset to zero.

   A:	After the problems with the volume have been repaired, run
   the scavenger again to recover lost records.

   Message:
   scavenge_volume: Error writing vtocx NNNNN on dskX_NN{S}. ERRORMESSAGE

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	The error indicated was encountered writing a VTOCE.

   A:	$ignore

   Message:
   scavenge_volume: Freed NNNN records on dskX_NN{S}

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	NNNN records were not claimed by any VTOCE and were not listed as
   free in the volume map. These records have been freed.

   A:	$ignore

   Message:
   scavenge_volume: Unable to resolve conflict for address RRR on dskX_NN{S}. Error at vtocx NNNN. ERRORMESSAGE.

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	During the VTOC walk, the scavenger found several VTOCEs claiming the
   save address RRR. It was unable to resolve the conflict due to a VTOCE read
   error.

   A:	After the volume has been repaired, rerun the scavenger.

   Message:
   scavenge_volume: Unable to resolve conflict for address RRR on dskX_NN{S}. astep=PTR

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	During the scan of the VTOC, the scavenger found several VTOCEs which
   claimed the same record address RRR. It could not resolve the conflict,
   since the segment with the ASTE indicated was active and could not be
   deactivated.

   A:	Rerun the scavenger at a later time. It may be necessary to
   wait until the next bootload.

   Message:
   scavenge_volume: vtoce NAME at VTOCX (dskX_NN{S}). ref to pageno PPP at addr RRR deleted

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	Address RRR was claimed by more than one VTOCE. The page indicated
   has been changed to a null page, and the segment has been damaged.

   A:	Recover the segment.

   Message:
   scavenge_volume: freeing TYPE VTOCE NAME at VTOCX (dskX_NN{S})

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	The message appears for each VTOCE freed if scavenge_vol was
   invoked with the -debug control argument.

   A:	$ignore

   Message:
   scavenge_volume: Error freeing TYPE vtocx VTOCX on dskX_NN{S}. ERRORMESSAGE

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}. 

   M:	The VTOCE indicated could not be freed due to the error
   indicated.

   A:	$ignore

   Message:
   scavenge_volume: Freed NNN VTOCEs on dskX_NN{S}

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	NNN VTOCEs were added to the free pool. These VTOCEs are VTOCEs
   found free but not marked as free in the VTOC map, per-process VTOCEs from
   a prior bootload, and per-bootload VTOCEs from a prior bootload.

   Message: 
   scavenge_volume: vtoce NAME at VTOCX (dskX_NN{S}) disk addr RRR bad

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	Record address RRR in the VTOCE indicated is outside of the paging 
   region for the volume. The segment has been damaged.

   A:	Recover the segment.

   Message:
   scavenge_volume: vtoce NAME at VTOCX (dskX_NN{S}). rec used changed from X to Y

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	The records used field in the VTOCE has been corrected.  The segment
   has been damaged.

   A:	Recover the segment.

   Message:
   scavenge_volume: vtoce NAME at VTOCX (dskX_NN{S}). cur len changed from X to Y

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	The current segment length field in the VTOCE has been corrected.
   The segment has been damaged.

   A:	Recover the segment.

   Message:
   scavenge_volume: vtoce NAME at VTOCX (dskX_NN{S}). max len changed from X to Y

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	The max length field in the VTOCE has been corrected. The segment has
   been damaged.

   A:	Recover the segment.

   Message:
   scavenge_volume: vtoce NAME at VTOCX (dskX_NN{S}). time-record-product reset to zero

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	An invalid value was found in the time-record-product field of
   the VTOCE indicated. This field has been reset to zero.

   A:	$ignore

   Message:
   scavenge_volume: dirsw turned off for NAME at VTOCX (dskX_NN{S})

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	The directory switch in the VTOCE was found on for a non-directory
   segment. It has been turned off. The segment has been damaged.

   A:	Recover the segment.

   Message:
   scavenge_volume: Invalid File Map Checksum for NAME at VTOCX (dskX_NN{S}).

   S:        $log

   T:	During a physical volume scavenge of dskX_NN{S}.

   M:	The filemap checksum was incorrect, indicating damage to the
   VTOCE. The checksum is corrected. Other damage, if found, is reported.

   A:        $ignore

   Message:
   scavenge_volume: damaged switch found on for NAME at VTOCX (dskX_NN{S})

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	The damaged switch in the VTOCEwas set prior to the scavenge.

   A:	Recover the segment.

   Message:
   scavenge_volume: setting damaged switch on NAME at VTOCX (dskX_NN{S}).

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	An inconsistency has been detected in the VTOCE and the damaged
   switch has been set. A message describing the inconsistency has been
   recorded in the syserr log. The binary information associated with this
   message includes the segment UID-path.

   A:	Recover the segment.

   Message:
   scavenge_volume: Damaged vtoce VTOCX (dskX_NN{S}).

   S:	$log

   T:	During a physical volume scavenge of dskX_NN{S}

   M:	The VTOCE image before correction is recorded into
   the syserr log if scavenge_vol was invoked with the -dump control
   argument.

   A:	$ignore

   Message:
   scavenge_volume: Overflow on scavenge of dskX_NN{S}. Restarting.

   S:	$info

   T:	During a physical volume scavenge of dskX_NN{S}.

   M:	An internal table which tracks reused record addresses overflowed.
   All conflicts detected so far are resolved, and the scavenge is restarted
   from the beginning.

   A:	$ignore

   Message:
   scavenge_volume: Faulting under the LOCK for debugging for USER

   S:	$beep

   T:	During a physical volume salvage.

   M:	A fault tag three fault is taken to exercise fault recovery.

   A:	$ignore

END MESSAGE DOCUMENTATION */

     end scavenge_volume;




		    scavenger.pl1                   11/11/89  1100.0r w 11/11/89  0807.4      149706



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1982 *
        *                                                         *
        *********************************************************** */



/* format: style3 */
scavenger:
     proc (Pvtx, Pvid, Scavenger_Optionsp, Sc_metersp, Code);

/*  This is the driver program for the scavenger. It validates the PVTE
    and sets up all data structures needed for scavenging. The scavenge
    itself is done elsewhere.

    scavenger$shutdown is called by normal shutdown to reset any
    scavenge in progress. It must be called after traffic control
    is shutdown to avoid races.

    Written July 1982 by J. Bongiovanni
    Modified October 1982 by J. Bongiovanni for fm_damaged and associated meters
    Modified 83-12-13 BIM to call verify_lock on faults.
    Modified 85-02-21, EJ Sharpe: use syserr_fault_msg.incl.pl1, correct logging of history registers.
*/

/****^  HISTORY COMMENTS:
  1) change(86-05-13,GJohnson), approve(86-05-13,MCR7387),
     audit(86-05-13,Martinson), install(86-05-14,MR12.0-1056):
     Correct error message documentation.
  2) change(86-06-02,Fawcett), approve(86-04-11,MCR7383),
     audit(86-06-18,Beattie), install(86-07-17,MR12.0-1097):
     Add support for subvolumes.
                                                   END HISTORY COMMENTS */

/*  Parameter  */

dcl	Pvtx		fixed bin;		/* PVTE index */
dcl	Pvid		bit (36) aligned;		/* Physical Volume ID */
dcl	Scavenger_Optionsp	ptr;			/* -> scavenger_options */
dcl	Sc_metersp	ptr;			/* -> returned meters, or null */
dcl	Code		fixed bin (35);		/* Error code */

/*  Automatic  */

dcl	begin_clock	fixed bin (71);
dcl	begin_pf		fixed bin;
dcl	begin_vcpu	fixed bin (71);
dcl	code		fixed bin (35);
dcl	end_pf		fixed bin;
dcl	end_vcpu		fixed bin (71);
dcl	i_am_wired	bit (1) aligned;
dcl	1 local_sc_meters	aligned like sc_meters;
dcl	1 local_scavenger_options
			aligned like scavenger_options;
dcl	p99		pic "99";
dcl	old_mask		fixed bin (71);
dcl	process_tablex	fixed bin;
dcl	ptwp		ptr;
dcl	scavenger_data_astep
			ptr;
dcl	started		bit (1) aligned;

/*  Static  */

dcl	N_OVFL		fixed bin int static options (constant) init (1023);
						/* Limited by field size */

/*  Based  */

dcl	1 Sc_meters	aligned like sc_meters based (Sc_metersp);
dcl	1 Scavenger_Options aligned like scavenger_options based (Scavenger_Optionsp);

/*  External  */

dcl	error_table_$pvid_not_found
			fixed bin (35) external;
dcl	error_table_$unexpected_condition
			fixed bin (35) external;
dcl	error_table_$scavenge_process_limit
			fixed bin (35) external;
dcl	error_table_$pv_no_scavenge
			fixed bin (35) external;
dcl	pds$process_group_id
			char (32) aligned external;
dcl	pds$processid	bit (36) aligned external;
dcl	sst$astl		bit (36) aligned external;

/*  Entry  */

dcl	condition_	entry (char (*), entry);
dcl	get_ptrs_$given_segno
			entry (fixed bin) returns (ptr);
dcl	lock$lock_ast	entry;
dcl	lock$lock_fast	entry (ptr);
dcl	lock$unlock_ast	entry;
dcl	lock$unlock_fast	entry (ptr);
dcl	pc_wired$wire_wait	entry (ptr, fixed bin, fixed bin);
dcl	pc_wired$unwire	entry (ptr, fixed bin, fixed bin);
dcl	pmut$lock_ptl	entry (fixed bin (71), ptr);
dcl	pmut$unlock_ptl	entry (fixed bin (71), ptr);
dcl	scavenge_volume	entry (ptr, ptr, ptr, ptr, fixed bin (35));
dcl	syserr		entry options (variable);
dcl	syserr$binary	entry options (variable);
dcl	syserr$error_code	entry options (variable);
dcl	usage_values	entry (fixed bin, fixed bin (71));
dcl	verify_lock$condition_nolog
			entry (character (*), pointer);
dcl	wire_proc$wire_me	entry;
dcl	wire_proc$unwire_me entry;

/*  Builtin  */

dcl	addr		builtin;
dcl	addrel		builtin;
dcl	baseno		builtin;
dcl	bin		builtin;
dcl	clock		builtin;
dcl	convert		builtin;
dcl	divide		builtin;
dcl	float		builtin;
dcl	null		builtin;
dcl	rel		builtin;
dcl	size		builtin;
dcl	unspec		builtin;
%page;
	started = "0"b;
	i_am_wired = "0"b;

	unspec (local_sc_meters) = ""b;
	unspec (local_scavenger_options) = unspec (Scavenger_Options);

	call SETUP_LOCK (Code);
	if Code ^= 0
	then return;

	call SETUP_PROCESS_TABLE (process_tablex, code);
	if code ^= 0
	then goto CLEANUP_RETURN;

	call SETUP_BLOCK (process_tablex);

	pvte.scavenger_block_rel = rel (scavenger_blockp);
	call lock$unlock_fast (addr (scavenger_data.lock));

	call usage_values (begin_pf, begin_vcpu);
	begin_clock = clock ();

	call condition_ ("any_other", PRINT_ERROR);

	call syserr (ANNOUNCE, "scavenger: Begin scavenge of ^a_^a^[^a^;^1s^] by ^a", pvte.devname,
	     convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name, pds$process_group_id);
	started = "1"b;
	call wire_proc$wire_me;
	i_am_wired = "1"b;
	call scavenge_volume (pvtep, scavenger_blockp, addr (local_scavenger_options), addr (local_sc_meters), code);

LOCK_CLEANUP_RETURN:
	call lock$lock_fast (addr (scavenger_data.lock));

CLEANUP_RETURN:
	if i_am_wired
	then call wire_proc$unwire_me;

	if started & (code = 0)
	then do;
		call usage_values (end_pf, end_vcpu);
		local_sc_meters.n_scavenge = 1;
		local_sc_meters.pf = end_pf - begin_pf;
		local_sc_meters.vcpu = end_vcpu - begin_vcpu;
		local_sc_meters.clock_time = clock () - begin_clock;
		if local_scavenger_options.print_meters
		then call PRINT_METERS;
		scavenger_data.meters = scavenger_data.meters + local_sc_meters;
	     end;

	call lock$lock_ast;				/* Protect against asynchronous SC/PC */
	call pmut$lock_ptl (old_mask, ptwp);
	pvte.scavenger_block_rel = ""b;
	pvte.scav_check_address = "0"b;
	pvte.deposit_to_volmap = "0"b;
	call pmut$unlock_ptl (old_mask, ptwp);
	call lock$unlock_ast;

	if scavenger_blockp ^= null ()
	then call RETURN_BLOCK (process_tablex);
	if sc_process_tablep ^= null ()
	then call RETURN_PROCESS_TABLE (process_tablex);

	call lock$unlock_fast (addr (scavenger_data.lock));

	if started
	then do;
		if code = 0
		then call syserr (ANNOUNCE, "scavenger: Scavenge of ^a_^a^[^a^;^1s^] by ^a completed.", pvte.devname,
			convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name, pds$process_group_id);
		else call syserr$error_code (ANNOUNCE, code,
			"scavenger: Scavenge of ^a_^a^[^a^;^1s^] by ^a completed with error.", pvte.devname,
			convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name, pds$process_group_id);
	     end;

	if Sc_metersp ^= null ()
	then unspec (Sc_meters) = unspec (local_sc_meters);

	Code = code;
	return;
%page;
/*  Entry to shutdown scavenges in progress  */

shutdown:
     entry;

	scavenger_datap = addr (scavenger_data$);
	sc_process_tablep = scavenger_data.process_table_ptr;

	do process_tablex = 1 to sc_process_table.max_n_processes;
	     if sc_process_table.process (process_tablex).processid ^= ""b
						/* Live entry */
	     then do;
		     pvtep = sc_process_table.process (process_tablex).pvtep;
		     pvte.deposit_to_volmap = "0"b;
		     pvte.scav_check_address = "0"b;
		     pvte.scavenger_block_rel = ""b;
		     call syserr (ANNOUNCE, "scavenger: Scavenge of ^a_^a^[^a^] stopped.", pvte.devname,
			convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name);
		end;
	end;

	return;


%page;
/*  Internal Procedure to lock the scavenger data base and set up pointers */

SETUP_LOCK:
     proc (Code);

dcl	Code		fixed bin (35);

	scavenger_datap = addr (scavenger_data$);
	sc_process_tablep = null ();
	scavenger_blockp = null ();
	pvt_arrayp = addr (pvt$array);
	pvtep = addr (pvt_array (Pvtx));
	scavenger_data_astep = get_ptrs_$given_segno (bin (baseno (scavenger_datap)));
	Code = 0;

	if pvte.pvid ^= Pvid
	then do;
		Code = error_table_$pvid_not_found;
		return;
	     end;
	if ^pvte.used | ^pvte.storage_system | (pvte.scavenger_block_rel ^= ""b)
	then do;
		Code = error_table_$pv_no_scavenge;
		return;
	     end;

	call lock$lock_fast (addr (scavenger_data.lock));

     end SETUP_LOCK;
%page;
/*  Internal Procedure to get a process table entry and fill it in */

SETUP_PROCESS_TABLE:
     proc (Process_Index, Code);

dcl	Process_Index	fixed bin;
dcl	Code		fixed bin (35);

dcl	n_header_pages	fixed bin;
dcl	procx		fixed bin;

	Process_Index = -1;
	Code = 0;

	sc_process_tablep = scavenger_data.process_table_ptr;
	if sc_process_table.n_processes >= sc_process_table.max_n_processes
	then do;
RETURN_ERROR:
		sc_process_tablep = null ();
		Code = error_table_$scavenge_process_limit;
		return;
	     end;

	do procx = 1 repeat procx + 1 while (procx <= sc_process_table.max_n_processes);
	     if sc_process_table.process (procx).processid = ""b
	     then goto FOUND_EMPTY;
	end;
	goto RETURN_ERROR;

FOUND_EMPTY:
	Process_Index = procx;
	sc_process_table.process (procx).processid = pds$processid;
	sc_process_table.process (procx).pvtep = pvtep;

	sc_process_table.n_processes = sc_process_table.n_processes + 1;
	if sc_process_table.n_processes = 1
	then do;
		n_header_pages = divide (size (scavenger_data) + 1023, 1024, 17);
		call pc_wired$wire_wait (scavenger_data_astep, 0, n_header_pages);
	     end;


     end SETUP_PROCESS_TABLE;
%page;
/*  Internal Procedure to Allocate and Setup the Scavenge Block  */

SETUP_BLOCK:
     proc (Process_Index);

dcl	Process_Index	fixed bin;

dcl	first_page	fixed bin;
dcl	n_pages		fixed bin;


	scavenger_n_records = pvte.totrec;
	scavenger_n_ovfl = N_OVFL;

	first_page = sc_process_table.process (Process_Index).first_block_page;
	n_pages = divide (size (scavenger_block) + 1023, 1024, 17);

	scavenger_blockp = sc_process_table.process (process_tablex).blockp;

	scavenger_block.n_records = scavenger_n_records;
	scavenger_block.n_ovfl = scavenger_n_ovfl;
	scavenger_block.ovfl_free_ix = 1;

	unspec (scavenger_block.records) = ""b;
	unspec (scavenger_block.overflow) = ""b;


	call pc_wired$wire_wait (scavenger_data_astep, first_page, n_pages);

	sc_process_table.process (Process_Index).n_block_pages = n_pages;

     end SETUP_BLOCK;
%page;
/*  Internal Procedure to Revert a Scavenge Block  */

RETURN_BLOCK:
     proc (Process_Index);

dcl	Process_Index	fixed bin;

dcl	first_page	fixed bin;
dcl	n_pages		fixed bin;


	if sc_process_table.process (Process_Index).processid ^= pds$processid
	then call syserr (CRASH, "scavenger: Invalid block reset");

	first_page = sc_process_table.process (Process_Index).first_block_page;
	n_pages = sc_process_table.process (Process_Index).n_block_pages;
	call pc_wired$unwire (scavenger_data_astep, first_page, n_pages);



     end RETURN_BLOCK;
%page;
/*  Internal Procedure to release a process table entry  */

RETURN_PROCESS_TABLE:
     proc (Process_Index);

dcl	Process_Index	fixed bin;
dcl	n_header_pages	fixed bin;

	if sc_process_table.process (Process_Index).processid ^= pds$processid
	then call syserr (CRASH, "scavenger: Process table entry not owned by this process.");

	sc_process_table.process (Process_Index).processid = ""b;
	sc_process_table.process (Process_Index).pvtep = null ();
	sc_process_table.process (Process_Index).n_block_pages = 0;

	sc_process_table.n_processes = sc_process_table.n_processes - 1;
	if sc_process_table.n_processes = 0
	then do;
		n_header_pages = divide (size (scavenger_data) + 1023, 1024, 17);
		call pc_wired$unwire (scavenger_data_astep, 0, n_header_pages);
	     end;


     end RETURN_PROCESS_TABLE;
%page;
/*  Internal Procedure to print an error message and cleanup. Called
    when any conditon is signalled through this frame */

PRINT_ERROR:
     proc (Mcptr, Condition, Coptr, Infoptr, Continue) options (non_quick);

dcl	Mcptr		ptr;
dcl	Condition		char (*);
dcl	Coptr		ptr;
dcl	Infoptr		ptr;
dcl	Continue		bit (1) aligned;

dcl	1 auto_fault_msg	aligned like fault_msg;	/* Machine conds and hist regs for logging */
dcl	ssptr		ptr;			/* pointer to signaller stack frame */


	if Mcptr ^= null ()
	then do;
		ssptr = addrel (Mcptr, -8);		/* signaller_stack.pad is 8 words long! */

/* Construct contiguous machine conditions and history registers */
		unspec (auto_fault_msg.mach_cond) = unspec (ssptr -> signaller_stack.mach_cond);
		auto_fault_msg.hist_reg = ssptr -> signaller_stack.history_registers;

		call syserr$binary (scavenger_data.error_severity, Mcptr, SB_hw_fault, SBL_hw_fault,
		     "scavenger: ^a condition signalled during scavenge of ^a_^a^[^a^;^1s^] by ^a", Condition,
		     pvte.devname, convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name,
		     pds$process_group_id);
	     end;
	else do;
		call syserr (scavenger_data.error_severity,
		     "scavenger: ^a condition signalled during scavenge of ^a_^a^[^a^;^1s^] by ^a", Condition,
		     pvte.devname, convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name,
		     pds$process_group_id);
	     end;

	if sst$astl = pds$processid
	then call lock$unlock_ast;			/* Read-only, safe */
	call verify_lock$condition_nolog (Condition, Mcptr);
						/* Fix other locks */

	code = error_table_$unexpected_condition;

	goto LOCK_CLEANUP_RETURN;

     end PRINT_ERROR;
%page;
/*  Internal Procedure to print metering data into the syserr log  */

PRINT_METERS:
     proc;

dcl	clock_sec		float;
dcl	vcpu_sec		float;

	vcpu_sec = float (local_sc_meters.vcpu) / 1.0e6;
	clock_sec = float (local_sc_meters.clock_time) / 1.0e6;

	call syserr (LOG,
	     "scavenger: Meters from scavenge of ^a_^a^[^a^;^1s^]. Clock=^6.1f vcpu=^6.1f pf=^d^/VTOCES: Total=^d Damaged=^d Per-Proc=^d Per-Boot=^d FMDamaged=^d Freed=^d^/Records: Total=^d Pot conflict=^d FMD Conflict = ^d Conflict=^d Lost=^d",
	     pvte.devname, convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name, clock_sec, vcpu_sec,
	     local_sc_meters.pf, local_sc_meters.n_vtoces, local_sc_meters.n_vtoces_damaged,
	     local_sc_meters.n_vtoces_per_proc, local_sc_meters.n_vtoces_per_boot, local_sc_meters.n_vtoces_fmd,
	     local_sc_meters.n_vtoces_freed, local_sc_meters.n_records, local_sc_meters.n_conflicts,
	     local_sc_meters.n_fmd_conflicts, local_sc_meters.n_real_conflicts, local_sc_meters.n_lost_records);

     end PRINT_METERS;

%page;
%include pvte;
%page;
%include scavenger_data;
%page;
%include syserr_binary_def;
%page;
%include syserr_constants;
%page;
%include syserr_fault_msg;
%page;
%include mc;
%page;
%include signaller_stack;
%page;
/* BEGIN MESSAGE DOCUMENTATION


Message:
scavenger: Begin scavenge of dskX_NN{s} by PERSON.PROJECT.TAG

S:        $info

T:        When a physical volume is being scavenged

M:        This is an informational message at the beginning of a volume
scavenge.

A:        $ignore


Message:
scavenger: Scavenge of dskX_NN{s} by PERSON.PROJECT.TAG completed. 

S:        $info

T:        When a physical volume is being scavenged.

M:        This is an informational message to indicate successful completion
of a scavenge.

A:        $ignore


Message:
scavenger: Scavenge of dskX_NN{s} by PERSON.PROJECT.TAG completed with error. ERRORMESSAGE.

S:        $info

T:	When a physical volume is being scavenged.

M:        Scavenging could not be completed because of the error indicated.

A:        $inform


Message:
scavenger: Invalid block reset.

S:        $crash

T:        When a physical volume is being scavenged.

M:        A process attempted to clean up a scavenger block which was not
assigned to it. This indicates a software malfunction.

A:        $recover


Message:
scavenger: Process table entry not owned by this process.

S:        $crash

T:        When a physical volume is being scavenged.

M:        The scavenger attempted to release a process table entry which did not
belong to this process. This indicates a software malfunction.

A:        $recover


Message:
scavenger: XXXXXX condition signalled during scavenge of dskX_NN{s} by PERSON.PROJECT.TAG

S:        $info

T:        When a physical volume is being scavenged.

M:        An unexpected XXXXXX condition was signalled during a volume
scavenge, causing abnormal termination of the scavenge.

A:        $inform


Message:
scavenger: Meters from scavenge of dskX_NN{s}. METERINGDATA.

S:        $log

T:        When a physical volume is being scavenged.

M:        Various peformance measurements of the scavenge are recorded if
the appropriate scavenger control flag is set. 

A:        $ignore

Message:
scavenger: Scavenge of dskX_NN{s} stopped.

S:	$info

T:	During system shutdown.

M:	The system is being shutdown, and a scavenge of dskX_NN{s} is
in progress. The scavenge is terminated and likely had no effect. 

A:        Rerun the scavenge.


END MESSAGE DOCUMENTATION */

     end scavenger;





		    bull_copyright_notice.txt       08/30/05  1008.4r   08/30/05  1007.3    00020025

                                          -----------------------------------------------------------


Historical Background

This edition of the Multics software materials and documentation is provided and donated
to Massachusetts Institute of Technology by Group Bull including Bull HN Information Systems Inc. 
as a contribution to computer science knowledge.  
This donation is made also to give evidence of the common contributions of Massachusetts Institute of Technology,
Bell Laboratories, General Electric, Honeywell Information Systems Inc., Honeywell Bull Inc., Groupe Bull
and Bull HN Information Systems Inc. to the development of this operating system. 
Multics development was initiated by Massachusetts Institute of Technology Project MAC (1963-1970),
renamed the MIT Laboratory for Computer Science and Artificial Intelligence in the mid 1970s, under the leadership
of Professor Fernando Jose Corbato.Users consider that Multics provided the best software architecture for 
managing computer hardware properly and for executing programs. Many subsequent operating systems
incorporated Multics principles.
Multics was distributed in 1975 to 2000 by Group Bull in Europe , and in the U.S. by Bull HN Information Systems Inc., 
as successor in interest by change in name only to Honeywell Bull Inc. and Honeywell Information Systems Inc. .

                                          -----------------------------------------------------------

Permission to use, copy, modify, and distribute these programs and their documentation for any purpose and without
fee is hereby granted,provided that the below copyright notice and historical background appear in all copies
and that both the copyright notice and historical background and this permission notice appear in supporting
documentation, and that the names of MIT, HIS, Bull or Bull HN not be used in advertising or publicity pertaining
to distribution of the programs without specific prior written permission.
    Copyright 1972 by Massachusetts Institute of Technology and Honeywell Information Systems Inc.
    Copyright 2006 by Bull HN Information Systems Inc.
    Copyright 2006 by Bull SAS
    All Rights Reserved
