?? RIGHT := 110 ??
MODULE mmm$pfti_manager;
?? RIGHT := 110 ??

{
{  PURPOSE: Memory_Manager
{     This module contains the monitor routines that are used to
{     locate all page frames bedlonging to a specified range of an SVA.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc mmt$active_segment_table
*copyc mmt$page_frame_index
*copyc mmt$page_frame_queue_id
*copyc mmt$page_queue_set
*copyc mmt$page_selection_criteria
*copyc mmt$pfti_array
?? POP ??
*copyc mtp$error_stop
*copyc jmv$ijl_p
*copyc mmv$pft_p
*copyc mmv$pt_length
*copyc mmv$pt_p
*copyc osv$page_size
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??

  VAR
    mmv$pfti_array_p: [XDCL, #GATE] ^mmt$pfti_array;

?? TITLE := '[XDCL] mmp$initialize_find_next_pfti', EJECT ??
{----------------------------------------------------------------------------------------------------------
{
{ The following procedures are used for locating pages belonging to a specified SVA to SVA+LENGTH range.
{ The procedures use an array (MMV$PFTI_ARRAY) allocated at deadstart time to contain the list of PFTIs.
{       mmp$initialize_find_next_pfti   - fills the array with the list of PFTIs and returns the first one.
{       mmp$find_next_pfti              - returns the next PFTI from the array. (0 = end of array)
{       mmp$delete_last_pfti_from_array - deletes the most recently returned PFTI from the array. If the
{                                         PFTI array is rescanned the entry will not be returned again;
{       mmp$reset_find_next_pfti        - resets the array index so that subsequent calls to
{                                         mmp$find_next_pfti will rescan the array and return all PFTIs.
{
{ Depending on initialization options the PFTIs selected by theses routines are be restricted as follows:
{       psc_nominal_queue   - only PFTIs of pages that are 'valid' will be returned (eg. working_set, wired,
{                             fixed, shared, io_error)
{       psc_all_except_avail- PFTI's of all pages in memory EXCEPT for pages in the AVAIL queue are returned.
{                             PROCEDURES THAT CALL THESES ROUTINES MUST BE PREPARED TO HANDLE NOT FINDING
{                             PAGES THAT ARE IN THE AVAIL QUEUE.
{       psc_all             - the PFTI's of all pages within the range that are in memory are returned.  To
{                             ensure that all pages of the segment are found, THE CALLER MUST SPECIFY THAT
{                             THE ENTIRE SEGMENT BE SEARCHED.
{----------------------------------------------------------------------------------------------------------

  PROCEDURE [XDCL] mmp$initialize_find_next_pfti
    (    xsva: ost$system_virtual_address;
         length: ost$segment_length;
         end_point_option: (include_partial_pages, exclude_partial_pages);
         page_selection_criteria: mmt$page_selection_criteria;
         aste_p: ^mmt$active_segment_table_entry;
     VAR xpfti: mmt$page_frame_index);

    VAR
      found: boolean,
      hcount: 1 .. 32,
      high_offset: integer,
      ipti: integer,
      low_offset: integer,
      offset: integer,
      page_count: integer,
      pages_in_memory: integer,
      pfti: mmt$page_frame_index,
      pfti_index: integer,
      selection_page_queues: mmt$page_queue_set,
      sva: ost$system_virtual_address;

    mmv$pfti_array_p^.pftis [0] := 0;
    pfti_index := 0;
    pages_in_memory := aste_p^.pages_in_memory;


{ Set up the parameters used to control the search. This includes the starting offset.
{ Determine the set of page queues a page can be linked to based on the page_selection_criteria.

    IF (pages_in_memory > 0) AND (length > 0) THEN
      sva := xsva;
      offset := sva.offset;
      IF end_point_option = include_partial_pages THEN
        page_count := (offset + length - 1) DIV osv$page_size - (offset DIV osv$page_size) + 1;
      ELSE
        page_count := (offset + length) DIV osv$page_size;
        offset := ((offset + osv$page_size - 1) DIV osv$page_size) * osv$page_size;
        page_count := page_count - offset DIV osv$page_size;
      IFEND;
      low_offset := offset - offset MOD osv$page_size;
      high_offset := low_offset + page_count * osv$page_size - 1;

      selection_page_queues := $mmt$page_queue_set [];
      IF page_selection_criteria = psc_all THEN
        selection_page_queues := -$mmt$page_queue_set [];
      ELSEIF page_selection_criteria = psc_all_except_avail THEN
        selection_page_queues := -$mmt$page_queue_set [mmc$pq_avail];
      ELSE  {page_selection_criteria = psc_nominal_queue}
        IF aste_p^.queue_id = mmc$pq_job_working_set THEN
          selection_page_queues := $mmt$page_queue_set [mmc$pq_shared_io_error, mmc$pq_job_io_error,
                mmc$pq_job_working_set]; { Unwritable permanent file pages go to the shared_io_error q. }
        ELSEIF ((aste_p^.queue_id >= mmc$pq_shared_first) AND (aste_p^.queue_id <= mmc$pq_shared_last)) THEN
          selection_page_queues := -$mmt$page_queue_set [mmc$pq_free, mmc$pq_avail, mmc$pq_avail_modified,
                mmc$pq_wired, mmc$pq_job_fixed, mmc$pq_job_working_set, mmc$pq_job_io_error];
        ELSEIF aste_p^.queue_id = mmc$pq_wired THEN
          selection_page_queues := $mmt$page_queue_set [mmc$pq_wired];
        ELSEIF aste_p^.queue_id = mmc$pq_job_fixed THEN
          selection_page_queues := $mmt$page_queue_set [mmc$pq_job_fixed];
        ELSE
          mtp$error_stop ('FIND NEXT PFTI -- BAD QUEUE ID');
        IFEND;
      IFEND;

{ Search for pages.

      IF (low_offset = 0) AND (length > 7ffffe00(16)) THEN
        pfti := aste_p^.pft_link.fwd;
        IF (page_selection_criteria = psc_all) THEN

{ The request is for all the pages of a segment that are in memory, so use the search algorithm that uses
{ the segment/page frame link to find all pages of the segment.

          WHILE pfti <> 0 DO
            mmv$pfti_array_p^.pftis [pfti_index] := pfti;
            pfti_index := pfti_index + 1;
            pfti := mmv$pft_p^ [pfti].segment_link.fwd;
          WHILEND;
        ELSE

{ The request length is for the entire segment, but not all queues should be searched.  Use the
{ the segment/page frame link to find all pages of the segment and check if each page fits the
{ page_selection_criteria.

          WHILE (pfti <> 0) DO
            IF mmv$pft_p^ [pfti].queue_id IN selection_page_queues THEN
              mmv$pfti_array_p^.pftis [pfti_index] := pfti;
              pfti_index := pfti_index + 1;
            IFEND;
            pfti := mmv$pft_p^ [pfti].segment_link.fwd;
          WHILEND;
        IFEND;

      ELSEIF (page_count < pages_in_memory DIV 4) THEN

{ If the request is for a small percentage of the pages in memory, use the search algorith that uses
{ the #HASH instruction to locate pages. This algorithm is NOT efficient for searches that look for
{ a large number of pages, because it does a HASH on each page in the range.  If the page is found,
{ it is put in the pfti array only if the page satisfies the page selection criteria.

        WHILE page_count > 0 DO
          sva.offset := offset;
          #HASH_SVA (sva, ipti, hcount, found);
          page_count := page_count - 1;
          offset := offset + osv$page_size; {Must be integer to prevent end case at end of segment (2**31)}
          IF found THEN
            pfti := (mmv$pt_p^ [ipti].rma * 512) DIV osv$page_size;
            IF mmv$pft_p^ [pfti].queue_id IN selection_page_queues THEN
              mmv$pfti_array_p^.pftis [pfti_index] := pfti;
              pfti_index := pfti_index + 1;
            IFEND;
            pages_in_memory := pages_in_memory - 1;
            IF pages_in_memory = 0 THEN
              page_count := 0;
            IFEND;
          IFEND;
        WHILEND;

      ELSE

{ The request is for a majority of the pages in memory.  Again, use the search algorithm that uses the
{ segment/page frame link to find all pages of the segment.  We need to check if the page offset is within
{ the range requested and decide if the page satisfies the selection criteria.

        pfti := aste_p^.pft_link.fwd;
        WHILE (pfti <> 0) AND (page_count > 0) DO
          IF (mmv$pft_p^ [pfti].sva.offset >= low_offset) AND
                (mmv$pft_p^ [pfti].sva.offset <= high_offset) THEN
            page_count := page_count - 1;
            IF mmv$pft_p^ [pfti].queue_id IN selection_page_queues THEN
              mmv$pfti_array_p^.pftis [pfti_index] := pfti;
              pfti_index := pfti_index + 1;
            IFEND;
          IFEND;
          pfti := mmv$pft_p^ [pfti].segment_link.fwd;
        WHILEND;

      IFEND;
    IFEND;  { pages_in_memory > 0 }

    mmv$pfti_array_p^.pfti_first := 0;
    mmv$pfti_array_p^.pfti_index := 0;
    mmv$pfti_array_p^.last_pfti_index := pfti_index;
    mmv$pfti_array_p^.pftis [pfti_index] := 0;
    xpfti := mmv$pfti_array_p^.pftis [0];

  PROCEND mmp$initialize_find_next_pfti;



?? TITLE := 'INLINE Procedures used in other modules', EJECT ??
?? SET (LISTALL := ON) ??
*copyc mmp$delete_last_pfti_from_array
?? SKIP := 2 ??
*copyc mmp$fetch_pfti_array_size
?? SKIP := 2 ??
*copyc mmp$find_next_pfti
?? SKIP := 2 ??
*copyc mmp$reset_find_next_pfti
?? SKIP := 2 ??
*copyc mmp$reset_store_next_pfti
?? SKIP := 2 ??
*copyc mmp$store_next_pfti
?? SKIP := 2 ??
*copyc mmp$reset_store_pfti_reverse
?? SKIP := 2 ??
*copyc mmp$store_pfti_reverse
?? POP ??
?? OLDTITLE ??
MODEND mmm$pfti_manager
