1 /****^  ***********************************************************
  2         *                                                         *
  3         * Copyright, (C) Honeywell Bull Inc., 1987                *
  4         *                                                         *
  5         * Copyright, (C) Honeywell Information Systems Inc., 1982 *
  6         *                                                         *
  7         *********************************************************** */
  8 
  9 
 10 /* format: style3 */
 11 pc_deposit:
 12      proc (Pvtx, Devadd, Vtocx, Pageno);
 13 
 14 /*  Routine to deposit an address or a list of addresses to the Volume
 15     Map. It is called from ALM Page Control when the address or addresses
 16     cannot be deposited into the record stock. This can happen only if
 17     the record stock is full. Asynchronous record stock management
 18     will deposit excess addresses to the Volume Map as we update each
 19     Volume Map page.
 20 
 21     This routine is also called during a volume scavenge, when addresses
 22     are to be deposited to the volume being scavenged. Asynchronous
 23     scavenger action is done here (that is, the scavenger data block
 24     for this volume is updated - addresses being deposited are checked
 25     for conflicts and their states are updated).
 26 
 27     Written March 1982 by J. Bongiovanni
 28     Modified July 1982 by J. Bongiovanni for the scavenger
 29 */
 30 
 31 
 32 /****^  HISTORY COMMENTS:
 33   1) change(86-06-10,Hartogs), approve(86-06-10,MCR7383),
 34      audit(86-06-11,Coppola), install(86-07-17,MR12.0-1097):
 35      Calls to syserr modified to report on subvolumes.
 36                                                    END HISTORY COMMENTS */
 37 
 38 
 39 /*  Parameter  */
 40 
 41 dcl       Pvtx                fixed bin parameter;          /* PVTE index */
 42 dcl       Devadd              bit (36) aligned parameter;   /* Single address to deposit */
 43 dcl       Vtocx               fixed bin;                    /* VTOCE index (used by scavenger) */
 44 dcl       Pageno              fixed bin;                    /* Page number within segment (used by scavenger) */
 45 dcl       List_ptr            ptr parameter;                /* Pointer to list of addresses */
 46 dcl       First               fixed bin;                    /* First element to deposit in array */
 47 dcl       Last                fixed bin;                    /* Last element to deposit in array */
 48 dcl       Pageno_list_ptr     ptr;                          /* Pointer to list of page numbers */
 49 
 50 /*  Automatic  */
 51 
 52 dcl       check_scavenger     bit (1) aligned;
 53 dcl       conflict            bit (1) aligned;
 54 dcl       grabbed_vpage       fixed bin;
 55 dcl       listx               fixed bin;
 56 dcl       listx1              fixed bin;
 57 dcl       p99                 pic "99";
 58 dcl       pageno              fixed bin;
 59 dcl       pages               fixed bin;
 60 dcl       pf_begin            fixed bin (35);
 61 dcl       pf_end              fixed bin (35);
 62 dcl       Single_addressp     ptr;
 63 dcl       this_vpage          fixed bin;
 64 dcl       vcpu_begin          fixed bin (71);
 65 dcl       vcpu_end            fixed bin (71);
 66 dcl       volmap_locked       bit (1);
 67 dcl       vpage_found         bit (1);
 68 dcl       vpage_list          (256) fixed bin;
 69 dcl       vpage_no            fixed bin;
 70 dcl       vpage_ptr           ptr;
 71 dcl       vtoc_index          fixed bin;
 72 
 73 /*  Static  */
 74 
 75 dcl       NULL_SDW            fixed bin (71) int static options (constant) init (0);
 76 dcl       RECORDS_PER_WORD    fixed bin int static options (constant) init (32);
 77 
 78 /*  Based  */
 79 
 80 dcl       1 List              (Last) aligned like Single_address based (List_ptr);
 81 dcl       Pageno_list         (Last) fixed bin based (Pageno_list_ptr);
 82 dcl       1 Single_address    aligned based (Single_addressp),
 83             2 Null_flag       bit (1) unaligned,
 84             2 Address         fixed bin (17) unsigned unaligned,
 85             2 Pad             bit (18) unaligned;
 86 
 87 /*  External  */
 88 
 89 dcl       volmap_abs_seg$     external;
 90 
 91 /*  Entry  */
 92 
 93 dcl       page$grab_volmap_page_unwired
 94                               entry (ptr, fixed bin, ptr);
 95 dcl       page$lock_volmap    entry (ptr);
 96 dcl       page$unlock_volmap  entry (ptr);
 97 dcl       page$write_volmap_page_unwired
 98                               entry (ptr, fixed bin);
 99 dcl       pmut$swap_sdw       entry (ptr, ptr);
100 dcl       syserr              entry options (variable);
101 dcl       usage_values        entry (fixed bin (35), fixed bin (71));
102 
103 /*  Condition  */
104 
105 dcl       cleanup             condition;
106 dcl       page_fault_error    condition;
107 
108 /*  Builtin  */
109 
110 dcl       addr                builtin;
111 dcl       convert             builtin;
112 dcl       divide              builtin;
113 dcl       mod                 builtin;
114 dcl       null                builtin;
115 dcl       ptr                 builtin;
116 dcl       stacq               builtin;
117 dcl       unspec              builtin;
118 %page;
119 /*  Deposit a single record address into the Volume Map  */
120 
121           call usage_values (pf_begin, vcpu_begin);
122           pages = 1;
123 
124           pvtep = addr (addr (pvt$array) -> pvt_array (Pvtx));
125           record_stockp = pvte.volmap_stock_ptr;
126           Single_addressp = addr (Devadd);
127           volmap_locked = "0"b;
128           grabbed_vpage = -1;
129 
130           call CHECK_FOR_SCAVENGE;
131 
132           vtoc_index = Vtocx;
133           pageno = Pageno;
134 
135           call FIND_VOLMAP_PAGE (Single_addressp, vpage_no, pageno, conflict);
136           if conflict
137           then goto RETURN;
138 
139           on cleanup call UNLOCK_RESET;
140 
141           call LOCK_SETUP;
142 
143           on page_fault_error
144                begin;
145                     call IO_ERROR;
146                     goto RETURN;
147                end;
148 
149           call page$grab_volmap_page_unwired (pvtep, vpage_no - 1, vpage_ptr);
150           grabbed_vpage = vpage_no - 1;
151 
152           call DEPOSIT_TO_PAGE ((Single_address.Address), vpage_no, vpage_ptr);
153 
154           call page$write_volmap_page_unwired (pvtep, vpage_no - 1);
155           grabbed_vpage = -1;
156 
157 RETURN:
158           revert page_fault_error;
159 
160           call UNLOCK_RESET;
161 
162           call METER;
163 
164           return;
165 %page;
166 /*  Deposit a list of addresses into the Volume Map. Go through the list
167     some number of times. For each candidate found, compute the Volume
168     Map page number, grab the page, and deposit all addresses in the list
169     which belong to that page.
170 */
171 
172 pc_deposit$deposit_list:
173      entry (Pvtx, List_ptr, First, Last, Vtocx, Pageno_list_ptr);
174 
175           call usage_values (pf_begin, vcpu_begin);
176           pages = Last - First + 1;
177 
178           pvtep = addr (addr (pvt$array) -> pvt_array (Pvtx));
179           record_stockp = pvte.volmap_stock_ptr;
180           volmap_locked = "0"b;
181           grabbed_vpage = -1;
182 
183           call CHECK_FOR_SCAVENGE;
184 
185           vtoc_index = Vtocx;
186 
187           do listx = First to Last;
188                if Pageno_list_ptr = null ()
189                then pageno = -1;
190                else pageno = Pageno_list (listx);
191                call FIND_VOLMAP_PAGE (addr (List (listx)), vpage_list (listx), pageno, conflict);
192                if conflict
193                then vpage_list (listx) = -1;
194           end;
195 
196           on cleanup call UNLOCK_RESET;
197 
198           call LOCK_SETUP;
199 
200           on page_fault_error
201                begin;
202 dcl       pagex               fixed bin;
203 
204                     if grabbed_vpage >= 0
205                     then do;
206                               do pagex = First to Last;
207                                    if vpage_list (pagex) = grabbed_vpage + 1
208                                    then vpage_list (pagex) = -1;
209                               end;
210                               call IO_ERROR;
211                          end;
212                     goto VPAGE_RETRY;
213                end;
214 
215 VPAGE_RETRY:
216           vpage_found = "0"b;
217           do listx = First to Last;
218                if vpage_list (listx) > 0
219                then do;
220                          vpage_found = "1"b;
221                          this_vpage = vpage_list (listx);
222                          call page$grab_volmap_page_unwired (pvtep, this_vpage - 1, vpage_ptr);
223                          grabbed_vpage = this_vpage - 1;
224                          do listx1 = listx to Last;
225                               if vpage_list (listx1) = this_vpage
226                               then do;
227                                         call DEPOSIT_TO_PAGE ((List (listx1).Address), this_vpage, vpage_ptr);
228                                         vpage_list (listx1) = -1;
229                                    end;
230                          end;
231                          call page$write_volmap_page_unwired (pvtep, grabbed_vpage);
232                          grabbed_vpage = -1;
233                     end;
234           end;
235 
236           if vpage_found
237           then goto VPAGE_RETRY;
238 
239           revert page_fault_error;
240 
241           call UNLOCK_RESET;
242 
243           call METER;
244 
245           return;
246 %page;
247 /* Internal Procedure to find the Volume Map page associated with a given
248    address */
249 
250 FIND_VOLMAP_PAGE:
251      proc (Devaddp, Volmap_pageno, Page_no, Conflict);
252 
253 dcl       Devaddp             ptr parameter;
254 dcl       Volmap_pageno       fixed bin parameter;
255 dcl       Page_no             fixed bin parameter;
256 dcl       Conflict            bit (1) aligned parameter;
257 
258 dcl       vpagex              fixed bin;
259 dcl       vpage_found         bit (1);
260 dcl       address             fixed bin;
261 
262 dcl       1 Devaddr           aligned like Single_address based (Devaddp);
263 
264           vpage_found = "0"b;
265           address = Devaddr.Address;
266           Conflict = "0"b;
267 
268           if address < pvte.baseadd | address >= pvte.baseadd + pvte.totrec
269           then call syserr (CRASH, "pc_deposit: Address ^o out of paging region on ^a_^a^[^a^;^s^].", address, pvte.devname,
270                     convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name);
271 
272           do vpagex = record_stock.n_volmap_pages to 1 by -1 while (^vpage_found);
273                if address >= record_stock.volmap_page (vpagex).baseadd
274                then do;
275                          vpage_found = "1"b;
276                          Volmap_pageno = vpagex;
277                     end;
278           end;
279 
280           if ^vpage_found
281           then call syserr (CRASH, "pc_deposit: Invalid address ^o on ^a_^a^[^a^;^s^].", address, pvte.devname,
282                     convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name);
283 
284           if check_scavenger
285           then Conflict = CHECK_ADDRESS_FOR_SCAVENGER (address, Page_no);
286 
287           return;
288 
289 
290      end FIND_VOLMAP_PAGE;
291 %page;
292 /* Internal Procedure to deposit a single record address to the Volume Map */
293 
294 DEPOSIT_TO_PAGE:
295      proc (Record_address, Vpage_no, Vpage_ptr);
296 
297 dcl       Record_address      fixed bin;
298 dcl       Vpage_no            fixed bin;
299 dcl       Vpage_ptr           ptr;
300 
301 dcl       bit_no              fixed bin;
302 dcl       word_no             fixed bin;
303 
304 dcl       1 Vm_page           aligned based (Vpage_ptr),
305             2 Word            (0:1023) aligned,
306               3 Pad1          bit (1) unaligned,
307               3 Bit           (0:31) bit (1) unaligned,
308               3 Pad2          bit (3) unaligned;
309 
310           word_no = divide (Record_address - record_stock.volmap_page (Vpage_no).baseadd, RECORDS_PER_WORD, 17);
311           if word_no < 0 | word_no > 1023 | (Vpage_no = 1 & word_no < 64)
312           then call syserr (CRASH, "pc_deposit: Invalid address ^o on ^a_^a^[^a^;^s^].", Record_address, pvte.devname,
313                     convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name);
314 
315           bit_no = mod (Record_address - record_stock.volmap_page (Vpage_no).baseadd, RECORDS_PER_WORD);
316 
317           if Vm_page.Word (word_no).Bit (bit_no)
318           then do;
319                     call syserr (ANNOUNCE, "pc_deposit: Deposit in-use address ^o on ^a_^a^[^a^;^s^].", Record_address,
320                          pvte.devname, convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name);
321                     pvte.vol_trouble_count = pvte.vol_trouble_count + 1;
322                                                             /* Add to inconsistency count */
323                end;
324           else do;
325                     Vm_page.Word (word_no).Bit (bit_no) = "1"b;
326                     pvte.nleft = pvte.nleft + 1;
327                     record_stock.volmap_page (Vpage_no).n_free = record_stock.volmap_page (Vpage_no).n_free + 1;
328                end;
329 
330           return;
331 
332      end DEPOSIT_TO_PAGE;
333 %page;
334 /*  Internal Procedure to handle I/O Error on the Volume Map  */
335 
336 IO_ERROR:
337      proc;
338 
339           pvte.vol_trouble_count = pvte.vol_trouble_count + 1;
340           if grabbed_vpage >= 0
341           then if record_stock.volmap_page (grabbed_vpage + 1).n_free > 0
342                then do;
343                          record_stock.volmap_page (grabbed_vpage + 1).n_free = 0;
344                          call syserr (BEEP,
345                               "pc_deposit: Unrecoverable I/O error on Volmap page ^d of ^a_^a^[^a^;^s^]. Addresses lost.",
346                               grabbed_vpage, pvte.devname, convert (p99, pvte.logical_area_number), pvte.is_sv, pvte.sv_name);
347                     end;
348 
349      end IO_ERROR;
350 %page;
351 /*  Internal procedure to see whether there's a scavenge goin' on for this
352     physical volume and set a flag accordingly.
353 */
354 
355 CHECK_FOR_SCAVENGE:
356      proc;
357 
358           check_scavenger = "0"b;
359 
360           scavenger_blockp = null ();
361           scavenger_datap = addr (scavenger_data$);
362 
363           if pvte.scavenger_block_rel ^= ""b
364           then if pvte.scav_check_address
365                then do;
366                          check_scavenger = "1"b;
367                          scavenger_blockp = ptr (scavenger_datap, pvte.scavenger_block_rel);
368                     end;
369 
370      end CHECK_FOR_SCAVENGE;
371 %page;
372 /*  Internal Procedure to check an address against the scavenger block for
373     this physical volume. The state is updated and conflicts marked
374     appropriately. Indication of conflict is returned to the caller,
375     so that the address is not deposited.
376 */
377 
378 CHECK_ADDRESS_FOR_SCAVENGER:
379      proc (Record_address, Page_no) returns (bit (1) aligned);
380 
381 dcl       Record_address      fixed bin parameter;
382 dcl       Page_no             fixed bin parameter;
383 
384 dcl       1 A_record_block    aligned like record_block;
385 dcl       Ap                  ptr;
386 dcl       conflict            bit (1) aligned;
387 dcl       locked              bit (1) aligned;
388 dcl       1 Q_record_block    aligned like record_block;
389 dcl       Qp                  ptr;
390 dcl       Wp                  ptr;
391 
392 dcl       A                   bit (36) aligned based (Ap);
393 dcl       Q                   bit (36) aligned based (Qp);
394 dcl       W                   bit (36) aligned based (Wp);
395 
396 
397           record_blockp = addr (scavenger_block.records (Record_address - pvte.baseadd + 1));
398 
399           locked = "0"b;
400           Ap = addr (A_record_block);
401           Qp = addr (Q_record_block);
402           Wp = record_blockp;
403           do while (^locked);
404                unspec (Q_record_block) = unspec (record_block);
405                unspec (A_record_block) = unspec (Q_record_block);
406                if ^A_record_block.lock
407                then do;
408                          A_record_block.lock = "1"b;
409                          locked = stacq (W, A, Q);
410                     end;
411           end;
412 
413           if record_block.state = STATE_UNSEEN
414           then record_block.state = STATE_FREE;
415           else if record_block.state = STATE_FREE
416           then record_block.state = STATE_CONFLICT;
417           else if record_block.state = STATE_IN_USE
418           then do;
419                     if Page_no >= 0 & vtoc_index >= 0 & record_block.vtocx = vtoc_index & record_block.pageno = Page_no
420                     then do;
421                               record_block.vtocx = 0;
422                               record_block.pageno = 0;
423                               record_block.state = STATE_FREE;
424                          end;
425                     else record_block.state = STATE_CONFLICT;
426                end;
427 
428           if record_block.state = STATE_CONFLICT
429           then conflict = "1"b;
430           else conflict = "0"b;
431 
432           record_block.lock = "0"b;
433 
434           return (conflict);
435 
436 
437      end CHECK_ADDRESS_FOR_SCAVENGER;
438 
439 
440 %page;
441 /*  Internal Procedure to lock the Volume Map lock, setup volmap_abs_seg  */
442 
443 LOCK_SETUP:
444      proc;
445 
446           call page$lock_volmap (pvtep);
447           volmap_locked = "1"b;
448           call pmut$swap_sdw (addr (volmap_abs_seg$), addr (pvte.volmap_seg_sdw));
449 
450      end LOCK_SETUP;
451 %page;
452 /*  Internal Procedure to Cleanup  */
453 
454 UNLOCK_RESET:
455      proc;
456 
457           if grabbed_vpage ^= -1
458           then call page$write_volmap_page_unwired (pvtep, grabbed_vpage);
459           grabbed_vpage = -1;
460 
461           call pmut$swap_sdw (addr (volmap_abs_seg$), addr (NULL_SDW));
462 
463           if volmap_locked
464           then call page$unlock_volmap (pvtep);
465           volmap_locked = "0"b;
466 
467      end UNLOCK_RESET;
468 %page;
469 /*  Internal Subroutine to meter CPU time, number of calls, and number of pages
470     whose addresses were deposited  */
471 
472 METER:
473      proc;
474 
475           stock_segp = addr (stock_seg$);
476           stock_seg.meters.pc_deposit_calls = stock_seg.meters.pc_deposit_calls + 1;
477           stock_seg.meters.pc_deposit_pages = stock_seg.meters.pc_deposit_pages + pages;
478           call usage_values (pf_end, vcpu_end);
479           stock_seg.meters.pc_deposit_time = stock_seg.meters.pc_deposit_time + vcpu_end - vcpu_begin;
480 
481      end METER;
482 
483 %page;
484 %include pvte;
485 %page;
486 %include scavenger_data;
487 %page;
488 %include stock_seg;
489 %page;
490 %include syserr_constants;
491 %page;
492 /* BEGIN MESSAGE DOCUMENTATION
493 
494    Message:
495    pc_deposit: Address XXXXXX out of paging region on dskX_NN{s}.
496 
497    S:     $crash
498 
499    T:     $run
500 
501    M:     An attempt was made to return disk address XXXXXX on device dskX_NN{s}
502    to the free pool. The address is not in the paging region.
503 
504    A:     $recover
505    It may be necessary to run the physical volume salvager on the device.
506 
507    Message:
508    pc_deposit: Invalid address XXXXXX on dskX_NN{s}.
509 
510    S:     $crash
511 
512    T:     $run
513 
514    M:     In attempting to deposit address XXXXXX on device dskX_NN{s}, an invalid
515    volume map offset was computed.
516 
517    A:     $recover
518    It may be necessary to run the physical volume salvager on the device.
519 
520    Message:
521    pc_deposit: Deposit in-use address XXXXXX on dskX_NN{s}.
522 
523    S:     $beep
524 
525    T:     $run
526 
527    M:     An attempt was made to return address XXXXXX on device dskX_NN{s} to
528    the free record pool, but the address was already marked as free. This
529    indicates damage to control structures on the device. This damage can
530    be corrected by a physical volume salvage.
531 
532    A:     $inform
533 
534    Message:
535    pc_deposit: Unrecoverable I/O error on Volmap page M of dskX_NN{s}. Addresses lost.
536 
537    S:     $beep
538 
539    T:     $run
540 
541    M:     There was an unrecoverable I/O error on a page of the Volume Map,
542    which describes free records of the volume. All free records described
543    by that page have been lost.
544 
545    A:     It may be possible to recover the lost addresses by a volume
546    salvage. If there is a hard device error, the volume salvage will
547    fail. In this case, it will be necessary to recover the volume onto
548    a good pack.
549 
550    END MESSAGE DOCUMENTATION */
551 
552      end pc_deposit;