Buffer Management in Multiprocessor Kernel

References: Bach's Chapter12: Figure 12.14

                 ASSUMPTIONS:
1. Buffers are maintained in a freelist and hash queues (HQs).
   Initial: all buffers are in freelist, every HQ is empty.

   The actual hashing scheme is NOT important, just for searching
   convenience. For example, H(bp=(dev,blk)) = dev is a hashing.
   But it does affect concurrency.

2. Each buffer has a lock semaphore=1. 
   Each HQ has a lock semaphore=1.
   freelist has a lock semaphore=1; 
   A semaphore wantfree=0; for procs to wait for free buffers 
   
3. CP and CV are primitive operations on semaphore:
   CP(semaphore): if semaphore value > 0: lock succeeds
                  else return -1 immediately.
   CV(semaphore): if (semaphore.queue NOT empty)
                      V(semaphore);

============= UNIX MP Algorithm =================

mgetblk(dev,blk)
{
  retry loop:
  1. P(HQ);           // lock HQ of bp=(dev,bp)
  2. if (bp in HQ){
        if (!CP(bp)){ // if fail to lock bp
           V(HQ);     // release HQ lock
           P(bp);     // wait in bpQ
          --------------------------
           if (bp changed){ 
               V(bp);   // unlock bp
               retry;  
           }
           if (!CP(HQ)){
               V(bp);
               retry;
           }
        }
        ------------------------------
        // locked bp did not change
        P(freelist);   // lock freelist; (BACH: while(!CP(freelist);
           take bp out of freelist;
        V(freelist);
        V(HQ);         
        return bp;
     }


  // Bach's book does not show WHAT IF the needed buffer is NOT in HQ
  // The following is only an outline of what the algorithm SHOULD DO:
  3. // bp NOT in HQ; HQ still locked at (1)

  3-1: Try to get a free buffer in order to create the needed buffer

  3-2. If (freelist is empty), MUST unlock HQ and WAIT for free buffer
       When resume to run, must retry from beginning;

  3-3. if (freelist not empty)
          try to get a free buffer, fbuf, off the freelist AND lock the buffer:
          
  3-4. if (locked fbuf is for DELWRI); must write it out ASYNC and retry 3-1.

  3-5. Try to reassign the locked fbuf to (dev,blk) and enter it into HQ;
       (what if fbuf was in a different HQ?)

      return fbuf;
}


// UNIX: releasing a bp only puts it back to freelist
// Does NOT need to access any HQ
// MAY unblock a proc who needs THIS bp and one who needs a free buffer.
// Assume: wantfree and bp are semaphores.
  
mbrelse(bp)
{
  P(freelist);
    put bp into freelist;
  V(freelist)

  CV(wantfree);
  V(bp);
} 

========================================================================
             COMMENTS and NEW MP ALGORITHM:

1. Every buffer is for exclusive use (by P(bp)). 

   CAN YOU ALLOW CONCURRENT READERS on the same buffer?
 
2. In mgetblk(), a locked bp=(dev,blk) at (2) may change because:
   At (2) a proc P1 finds bp in HQ and CP(bp) fails, meaning bp is 
   already locked (either by a proc which is using this bp now or by a proc
   which has locked bp via the freelist) ==> P1 releases HQ and P(bp) to 
   wait in bp's queue.
   If bp was locked by a proc via the freelist, bp will be reassigned to
   a different (DEV,BLK).
   When P1 gets up from bpQ, bp has been changed ==> P1 has to give up this
   bp (by V(bp)) and retry again.

   CAN YOU IMPROVE ON THIS? i.e. to ensure a "locked bp" does NOT change?

3. The max. degree of concurrency is the number of HQs but the min. degree 
   of concurrency is 1 because of the freelist bootleneck.
   CAN YOU IMPROVE ON THIS?
=========================================================================