1 /****^  ***********************************************************
  2         *                                                         *
  3         * Copyright, (C) Honeywell Bull Inc., 1987                *
  4         *                                                         *
  5         * Copyright, (C) Honeywell Information Systems Inc., 1982 *
  6         *                                                         *
  7         * Copyright (c) 1972 by Massachusetts Institute of        *
  8         * Technology and Honeywell Information Systems, Inc.      *
  9         *                                                         *
 10         *********************************************************** */
 11 
 12 
 13 /*
 14    create_vtoce (branchp, pvid, vtocx, code)
 15 
 16 
 17    FUNCTION -
 18 
 19    This procedure creates a vtoc entry for the segment whose branch is  pointed  to
 20    by  the input argument (branchp). It returns the uid of physical volume in which
 21    the vtoc entry was created (pvid) and the vtoc index (vtocx) of this vtoc entry.
 22 
 23    If the operation is successful the code returned is zero. If it fails, then  one
 24    possible returned value is error_table_$log_vol_full.      In this case, no vtoc
 25    entry was created and pvid=0, vtocx = -1.
 26 
 27    The logical volume assigned to the vtoc entry is defined as follows: if the vtoc
 28    entry is for a directory, the logical volume is the system logical volume, where
 29    all  directories  reside;  if the vtoc entry is for a non directory segment, the
 30    logical volume is specified in the header of the parent directory.
 31 
 32 
 33    IMPLEMENTATION -
 34 
 35    The parent directory is supposed to be locked before this procedure  is  called.
 36    Also the branch is supposed to be initialized.
 37 
 38    The  physical  volume where the vtoc entry is created is the first member of the
 39    logical volume which is not full. The file map is initialized with zeros.
 40 
 41    MODIFIED BY *
 42 
 43    03/10/75         Andre Bensoussan for the new storage system.
 44    08/11/75 Bernard Greenberg - most-space allocation algorithm.
 45    10/01/75 RE Mullen - optimize filemap nulling.
 46    10/21/75 by Greenberg for allocation by recs available, seg mover.
 47    02/06/76 by Greenberg for dynamic demounter.
 48    02/18/76 by Richard Bratt for LVT
 49    03/22/76 by Larry Johnson to set master_dir switch in vtoce, and to call uid_path_util for uid pathname
 50    06/76 by D.Vinograd to update volume dumper bit map when creating new vtoce.
 51    09/27/76 by RE Mullen for (cycling | pro-rata) PV selection to reduce io contention
 52    29 Jan 79 by D. Spector to allocate vtoces for deciduous segments on the RPV
 53    10/03/79 by J. A. Bush to copy terminal quota info  when copying vtoce for segment_mover
 54    01/09/80 by Mike Grady to fix try_cycle bug for inop devices and speed up fm nuller code
 55    03/22/81 by J. Bongiovanni for bug fix
 56             for volume being salvaged, avoid per-process creation on saturated units
 57    06/24/81 by J. Bongiovanni for random selection of PV within LV, weighted
 58             by fraction of space left
 59    03/06/82 by J. Bongiovanni to eliminate vtoce.infqcnt, for new PVTE, and for
 60             optimize parameter for segmove
 61    06/02/82 by J. Bongiovanni to set vtoce.perm_flags.per_bootload
 62    83-12-06 by BIM to correctly check LV access class and audit violations.
 63    84-12-05 by EJ Sharpe to use access_audit_ instead of protection_audit_
 64    85-04-01 by Keith Loepere for access_audit_check_ep_.
 65 */
 66 
 67 ^L
 68 create_vtoce : procedure (branchp, pvid, vtocx, code);
 69 
 70 
 71 dcl  branchp ptr;                                           /* Input  - ptr to the branch */
 72 dcl  pvid bit (36);                                         /* Output - uid of the phys. vol. where vtoc entry is created */
 73 dcl  vtocx fixed bin (17);                                  /* Output - index of the vtoc entry that was created */
 74 dcl  code fixed bin (35);                                   /* Output - error code */
 75 
 76 /* Arguments for segmove entry */
 77 
 78 dcl  corout_pvtx fixed bin;                                 /* Input/Output - next pvtx to be tried */
 79 dcl  a_skip_pvtx fixed bin;
 80 dcl  skip_pvtx fixed bin;                                   /* Input - original segment pvtx */
 81 dcl  a_nreq fixed bin;                                      /* Input - required number of records */
 82 dcl  a_optimize bit (1) aligned;                            /* Input - optimize allocation of PV */
 83 
 84 dcl (i, pvtx, msl) fixed bin (17);
 85 dcl  first_pvtx fixed bin (17);
 86 dcl  nreq fixed bin (17);
 87 dcl (mover, looped, looping, try_cycle, held) bit (1);
 88 dcl  1 event_flags aligned like audit_event_flags;
 89 dcl  force_rpv bit (1);
 90 dcl  optimizing bit (1);
 91 dcl  lvid bit (36);
 92 dcl  queue_length fixed bin;
 93 dcl (working_sum, random_number, sum_fract_empty) fixed bin (35, 18);
 94 dcl  n_pvs fixed bin;
 95 dcl  pv_found bit (1);
 96 dcl  pv_alloc_x fixed bin;
 97 dcl  1 pv_alloc (MAX_PV_PER_LV) aligned,
 98      2 pvtx fixed bin,
 99      2 fract_empty fixed bin (35, 18);
100 
101 dcl  vtoc_buffer (96) fixed bin (71);
102 dcl 1 local_vtoce like vtoce aligned based (addr (vtoc_buffer));
103 dcl  based_class_range (2) bit (72) aligned based;
104 
105 dcl  access_operations_$fs_obj_create bit (36) aligned ext;
106 dcl  sys_info$initialization_state fixed bin ext;
107 dcl  sst$cycle_pv_allocation fixed bin (35) external;
108 dcl  pvt$root_lvid bit (36) aligned external;
109 dcl  error_table_$log_vol_full ext fixed bin (35);
110 dcl  error_table_$pvid_not_found ext fixed bin (35);
111 dcl  error_table_$ai_restricted ext fixed bin (35);
112 dcl  sys_info$default_max_length ext fixed bin (19);
113 dcl  sys_info$default_dir_max_length ext fixed bin (19);
114 dcl  active_hardcore_data$sl1_uid bit (36) aligned external;
115 
116 dcl  access_audit_check_ep_$self entry (bit (36) aligned, bit (36) aligned, ptr) returns (bit (1));
117 dcl  access_audit_$log_entry_ptr entry options (variable);
118 dcl  display_access_class_$range entry ((2) bit(72) aligned) returns(char(32) aligned);
119 dcl  vtoc_man$alloc_and_put_vtoce entry (bit (36) aligned, fixed bin (17), ptr, fixed bin (35)) returns (fixed bin);
120 dcl  logical_volume_manager$lvtep entry (bit (36) aligned, ptr, fixed bin (35));
121 dcl  clock_ entry returns (fixed bin (52));
122 dcl  level$get entry () returns (fixed bin);
123 dcl  read_allowed_ entry (bit(72) aligned, bit(72) aligned) returns(bit(1) aligned);
124 dcl  write_allowed_ entry (bit(72) aligned, bit(72) aligned) returns(bit(1) aligned);
125 dcl  get_pvtx$hold_pvtx entry (bit (36) aligned, fixed bin, fixed bin (35));
126 dcl  get_pvtx$release_pvtx entry (bit (36) aligned, fixed bin);
127 dcl  dbm_man$set_incr entry (fixed bin, fixed bin, fixed bin (35));
128 dcl  uid_path_util$get entry (ptr, dim (0:15) bit (36) aligned, fixed bin (35));
129 dcl  disk_control$queue_length_given_pvtx entry entry (fixed bin, fixed bin);
130 
131 dcl  (addr, bit, clock, divide, fixed, high9, mod, multiply, null, ptr, rel, string, unspec) builtin;
132 
133 
134 dcl  fm_nullifier char (256*2) aligned based (fmn_ptr);
135 dcl  fmn_ptr ptr;
136 dcl  uid_path (0:15) bit (36) aligned;
137 
138 dcl  MAXQ_FOR_PDIR_CYCLE fixed bin int static options (constant) init (7);      /* number disk queue entries for saturation*/
139 dcl  MAX_PV_PER_LV fixed bin int static options (constant) init (32); /* maximum number of physical volumes on an LV*/
140 dcl  MODULUS fixed bin int static options (constant) init (1024);     /* for generating random number from clock*/
141 
142 
143 ^L
144 /* PREPARE A LOCAL COPY OF THE VTOCE USING THE BRANCH INFORMATION */
145 
146           mover = "0"b;                                     /* entry switch */
147           skip_pvtx = 0;
148           nreq = 0;
149           optimizing = "1"b;
150 join:     code = 0;
151           vtocep = addr (local_vtoce);
152           ep = branchp;
153           dp = ptr (ep, 0);
154 
155           if entry.dirsw then msl = divide (sys_info$default_dir_max_length, 1024, 17, 0);
156           else msl = divide (sys_info$default_max_length, 1024, 17, 0);
157 
158           unspec (local_vtoce) = "0"b;
159           local_vtoce.uid = entry.uid;
160           local_vtoce.msl = bit (fixed (msl, 9));
161           local_vtoce.dirsw = entry.dirsw;
162           local_vtoce.primary_name = addr (entry.primary_name) -> names.name;
163           local_vtoce.time_created = bit (clock_ (), 52);
164           local_vtoce.dtu = local_vtoce.time_created;
165           local_vtoce.dtm = local_vtoce.dtu;
166           local_vtoce.par_pvid = dir.pvid;
167           local_vtoce.par_vtocx = dir.vtocx;
168           local_vtoce.per_process = entry.per_process_sw;
169           local_vtoce.branch_rp = rel (ep);
170           local_vtoce.access_class = entry.access_class;
171           local_vtoce.master_dir = entry.master_dir;
172           if dp -> dir.uid = active_hardcore_data$sl1_uid   /* parent is current >sl1 */
173                then local_vtoce.perm_flags.per_bootload = "1"b;
174           if mover then                                     /* if called from segment_mover */
175                if tq_infop ^= null then                     /* and we need to copy term quota attributes */
176                     do i = 0 to 1;                          /* do it like this for efficiency */
177                     local_vtoce.trp (i) = tq_info.trp (i);  /* copy pertinent data */
178                     local_vtoce.trp_time (i) = tq_info.tup (i);
179                     local_vtoce.received (i) = tq_info.received (i);
180                end;
181 
182 /* Fill the filemap with appropriate null addresses.  This is done using a */
183 /* based overlay in order to generate efficient code.  We are simulating */
184 /* do i = 0 to 255; local_vtoce.fm(i) = create_vtoce_null_addr; end; */
185 /* which is about 2500% slower. fm (0) MUST BE DOUBLE WORD ALIGNED */
186 
187           fmn_ptr = addr (local_vtoce.fm (0));              /* get ptr to base of filemap */
188           fm_nullifier = high9(256*2);                      /* set whole string - 256 bit (18)'s to */
189                                                             /* all one bits, done with single mlr */
190 
191 /* GET THE UID_PATH OF THE PARENT FROM THE KST AND STORE IT IN THE VTOCE - THE UID_PATH IS AN ARRAY OF 16 ENTRIES
192    NUMBERED FROM 0 TO 15 - ANY DIRECTORY WHICH IS IN THE PATH OF THE PARENT AND WHOSE TREE DEPTH IS i HAS ITS UID RECORDED
193    IN UID_PATH(i)  - ANY ELEMENT OF THE UID_PATH THAT DOES NOT HOLD A UID HAS THE VALUE ZERO. */
194 
195 
196           force_rpv = (dir.tree_depth = 0)                  /* set for level 1 creations to go on rpv */
197                | dir.force_rpv                              /* Better be on RLV ! */
198                | sys_info$initialization_state < 3;         /* make_sdw carefully placed deciduous
199                                                                segments on the RPV. If we are creating segments
200                                                                during collection 2, make sure the vtoce is allocated
201                                                                on the RPV */
202 
203           call uid_path_util$get (dp, uid_path, code);      /* get uid path of parent */
204           if code ^= 0 then return;
205           local_vtoce.uid_path = uid_path;
206 
207 
208 /* If per process and not constrained attempt to cycle through PV's */
209 
210           try_cycle = ^mover & ^force_rpv
211                & (dir.per_process_sw | sst$cycle_pv_allocation ^= 0);
212 
213 /* DETERMINE IN WHICH LOGICAL VOLUME THE VTOCE IS TO BE CREATED. */
214 
215           if entry.dirsw = "0"b then lvid = dir.sons_lvid;
216           else lvid = pvt$root_lvid;
217 
218 
219 restart:  call logical_volume_manager$lvtep ((lvid), lvtep, code);
220           if code ^= 0 then return;
221           if ^(read_allowed_ (entry.access_class, lvte.access_class.min) &
222                write_allowed_ (entry.access_class, lvte.access_class.max))
223           then do;
224                pvid = "0"b;
225                vtocx = -1;
226                code = error_table_$ai_restricted;
227                string(event_flags) = ""b;
228                if access_audit_check_ep_$self (string (event_flags), access_operations_$fs_obj_create, ep) then
229                     call access_audit_$log_entry_ptr ("create_vtoce", level$get(), string(event_flags),
230                     access_operations_$fs_obj_create, ep, code, null(), 0,
231                     "entry class range outside LV (^a LVID ^w)",
232                     display_access_class_$range (addr(lvte.access_class)->based_class_range), lvte.lvid);
233                return;
234           end;
235 
236 /* ALLOCATE A VTOCE ON SOME PV WITH A FREE VTOCE.  ALGORITHM:
237    Satisfy segment_mover or force_rpv constraints if any.
238    Else if per_process then try to cycle among the PV's. (always spread heavy I/O segments)
239       Skip any PV whose disk queue is larger than MAXQ_FOR_PDIR_CYCLE, as this
240       indicates local saturation in this cycle.
241    Else place randomly (biased by fraction of space left on each PV).
242 
243    This algorithm attempts to gracefully handle the cases where some
244    PV's are either empty (newly added?) or larger than others.
245    It used to happen that if one PV was 2x larger, it would fill
246    halfway before anything was placed on the others.
247    It used to be that if one PV was by far the emptiest, then
248    it would obtain all per-process segments thus creating an
249    I/O bottleneck. --REM */
250 
251 
252           pvt_arrayp = addr (pvt$array);
253 retry:
254           held = "0"b;
255           if try_cycle then do;                             /* attempt to use cycle_pvtx */
256                try_cycle = "0"b;                            /* don't do this more than once per creation */
257 
258                looped, looping = "0"b;                      /* not yet passed head of list */
259                pvtx = lvte.cycle_pvtx;                      /* See if cycle has reasonable value */
260                if pvtx = 0 then looped = "1"b;              /* not good, reset and note */
261                else do;                                     /* maybe good */
262                     pvtep = addr (pvt_array (pvtx));        /* examine in detail */
263                     if pvte.lvid ^= lvid then looped = "1"b; /* not good, reset and note */
264                end;
265                if looped then pvtx, lvte.cycle_pvtx = lvte.pvtex; /* do the reset of cycle */
266                                                             /* Now pvtx and cycle_pvtx as good as can be, LV_wise */
267 
268                do while (^looping);                         /* dont loop forever if cant cycle */
269                     pvtep = addr (pvt_array (pvtx));
270                     call disk_control$queue_length_given_pvtx (pvtx, queue_length);
271                     if ^pvte.vacating
272                     & ^pvte.device_inoperative              /* bad idea if down */
273                     & pvte.n_free_vtoce > 0
274                     & pvte.nleft > 32 then do;              /* cycle not to cause immediate segmoves */
275                          if dir.per_process_sw
276                               & queue_length>MAXQ_FOR_PDIR_CYCLE /* drive looks saturated           */
277                               then do;                      /* meter these                                    */
278                               if pvte.skip_queue_count=262143    /* dont want overflow of meter               */
279                                    then pvte.skip_queue_count = 0;
280                               else pvte.skip_queue_count = pvte.skip_queue_count + 1;
281                          end;
282                          else do;
283                               lvte.cycle_pvtx = pvte.brother_pvtx; /* leave cycle at next, may be Zero */
284                               pvid = pvte.pvid;
285                               go to got;
286                          end;
287                     end;
288                     pvtx = pvte.brother_pvtx;     /* chase to next */
289                     if pvtx = 0 then do;                    /* must wrap around */
290                          if looped then looping = "1"b; /* wrap around only once */
291                          else do;
292                               looped = "1"b;                /* note this first time */
293                               pvtx = lvte.pvtex;
294                          end;
295                     end;
296                end;
297           end;                                              /* end of try_cycle code */
298 
299           n_pvs = 0;
300           sum_fract_empty = 0;
301           pvtx = -1;
302 
303           if mover
304           then if corout_pvtx = 0
305                then first_pvtx = lvte.pvtex;                /* initialize */
306                else do;
307                     first_pvtx = pvt_array (corout_pvtx).brother_pvtx; /* pick up where we left off */
308                     corout_pvtx = 0;                        /* reinitialize coroutine hack if scan is restarted */
309                end;
310           else first_pvtx = lvte.pvtex;
311 
312           do i = first_pvtx repeat (pvte.brother_pvtx) while (i ^= 0);
313                pvtep = addr (pvt_array (i));
314                if pvte.lvid ^= lvid then go to restart;     /* LVT must have changed during scan */
315                if (^force_rpv | pvte.rpv)                   /* Want to use rpv? */
316                then if pvte.n_free_vtoce > 0 & ^pvte.vacating /* Must be space for 1 new seg */
317                     then if (^mover | (pvte.nleft > nreq) & (i ^= skip_pvtx)) /* If moving, need nreq, not orig PV */
318                          & ^pvte.device_inoperative         /* bad idea if down */
319                          then do;
320                               if ^optimizing then do;       /* not trying to optimize                         */
321                                    pvtx = i;
322                                    goto got;
323                               end;
324                               else do;
325                                    n_pvs = n_pvs + 1;
326                                    pv_alloc (n_pvs).pvtx = i;
327                                    pv_alloc (n_pvs).fract_empty
328                                         = divide (pvte.nleft, pvte.totrec, 35, 18);
329                                    sum_fract_empty = sum_fract_empty
330                                         + pv_alloc (n_pvs).fract_empty;
331                               end;
332                          end;
333           end;
334 
335 /* Select a physical volume randomly biased by the fraction of space
336    left on each physical volume.  The random number used is a modulus
337    of the current clock.  This algorithm has the effect (for a reasonable
338    number of segment creations) of cycling among physical volumes which
339    are balanced in space used.  As a physical volume's space becomes
340    exhausted (relative to other physical volumes in the same logical
341    volume), it becomes progressively less favored for segment creation.                                       */
342 
343           if n_pvs > 0 then do;
344                random_number = divide (multiply (mod (clock (), MODULUS), sum_fract_empty, 35, 18),
345                     MODULUS, 35, 18);                       /* between 0 and sum_fract_empty                  */
346                working_sum = 0;
347                pv_found = "0"b;
348                do pv_alloc_x = 1 repeat pv_alloc_x + 1
349                     while (^pv_found & pv_alloc_x < n_pvs);
350                     working_sum = working_sum + pv_alloc (pv_alloc_x).fract_empty;
351                     if working_sum >= random_number then do;
352                          pv_found = "1"b;
353                          pvtx = pv_alloc (pv_alloc_x).pvtx;
354                     end;
355                end;
356                if ^pv_found then pvtx = pv_alloc (n_pvs).pvtx;
357           end;
358 
359 
360 
361           if pvtx = -1 then
362                do ;
363 no_room:       vtocx = -1;
364                pvid = "0"b;
365                code = error_table_$log_vol_full;
366                return;
367           end;
368 
369 got:      pvtep = addr (pvt_array (pvtx));
370           if pvte.lvid ^= lvid then go to retry;
371           pvid = pvte.pvid;
372 
373 
374           call get_pvtx$hold_pvtx ((pvid), pvtx, code);
375           if code ^= 0 then goto not_there;
376           held = "1"b;
377 
378           vtocx = vtoc_man$alloc_and_put_vtoce ((pvid), pvtx, addr (local_vtoce), code);
379           if code ^= 0 then do;
380 not_there:
381                vtocx = -1;
382                if held then call get_pvtx$release_pvtx ((pvid), pvtx);
383                pvid = "0"b;
384                return;
385           end;
386           if vtocx = -1 then do;                            /* lost in window. There must be a better volume,
387                                                                or LV is full, and we will find this out. */
388                if held then call get_pvtx$release_pvtx ((pvid), pvtx);
389                goto retry;
390           end;
391           call dbm_man$set_incr (pvtx, vtocx, code);
392 
393           call get_pvtx$release_pvtx ((pvid), pvtx);
394 
395           if mover then corout_pvtx = pvtx;                 /* Start there next */
396           return;
397 
398 /* ^L */
399 
400 /* create_vtoce$createv_for_segmove
401 
402    This entry is used to try to find a home for a segment which
403    cannot allocate for a pendant page fault. segment_mover calls this
404    entry maintaining the variable corout_pvtx for us. This enables us to
405    scan the PVT. a_nreq is a minimum record requirement on a potential
406    trial volume */
407 
408 
409 createv_for_segmove: entry (branchp, pvid, vtocx, code,     /* as regular */
410                corout_pvtx,                                 /* control state/pvtx answer */
411                a_skip_pvtx,                                 /* original pvtx, do not use */
412                a_nreq,                                      /* number or records needed */
413                tq_infop,                                    /* ptr to tq_info structure (null if no term quota) */
414                a_optimize);                                 /* ON => optimize allocation of PV */
415 
416 
417           skip_pvtx = a_skip_pvtx;
418           nreq = a_nreq;                                    /* copy args */
419           mover = "1"b;                                     /* entry switch */
420           optimizing = a_optimize;
421           go to join;
422 %page; %include backup_static_variables;
423 %page; %include dir_entry;
424 %page; %include dir_header;
425 %page; %include dir_name;
426 %page; %include lvt;
427 %page; %include null_addresses;
428 %page; %include pvte;
429 %page; %include tq_info;
430 %page; %include vtoce;
431 %page; %include access_audit_eventflags;
432 %page;
433 
434 /* BEGIN MESSAGE DOCUMENTATION
435 
436    Message:
437    AUDIT (create_vtoce): DENIED creation of file system object ADDED_INFO entry class range outside LV (CLASS_RANGE lvid LVID)
438 
439    S:     $access_audit
440 
441    T:     $run
442 
443    M:     The specified user attempted to create a segment whose access class
444           is outside the range accepted by the logical volume.
445 
446    A:     $inform_ssa
447 
448    END MESSAGE DOCUMENTATION */
449 
450      end create_vtoce;