2

This is the old task of implementing double-indirect inodes for the xv6 system. In double indirect a file can have 16523 blocks (11 direct-pointer + 1 single-indirect-pointer*128 + 1 double-indirect-pointer*128*128), and my implementation satisfies this condition according to the usertests output (I will present it below).

The full code of the xv6 system can be found at https://github.com/mit-pdos/xv6-public, and since if you are here you probably familiar with it, I will only present the modifications I made in the implementation of bmap() and itrunc() methods at fs.c:

// NINDIRECT is 128
// NDIRECT is 11
// There is one less direct pointer to make room for the double-indirect

// Return the disk block address of the nth block in inode ip.
// If there is no such block, bmap allocates one.
static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a;
  uint *indirect;
  struct buf *bp;
  struct buf *dbp;

  // Direct Pointer
  if(bn < NDIRECT){
    // get direct pointer, allocate if necessary
    if((addr = ip->addrs[bn]) == 0)
      ip->addrs[bn] = addr = balloc(ip->dev);
    return addr;
  }
  bn -= NDIRECT;

  // Indirect Pointer
  if(bn < NINDIRECT){
    // load indirect block, allocate if necessary
    if((addr = ip->addrs[NDIRECT]) == 0)
      ip->addrs[NDIRECT] = addr = balloc(ip->dev);
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    // get direct pointer, allocate if necessary
    if((addr = a[bn]) == 0){
      a[bn] = addr = balloc(ip->dev);
      log_write(bp);
    }
    // release indirect block
    brelse(bp);
    // return disk block address
    return addr;
  }
  bn -= NINDIRECT;

  // Double-indirect Pointer
  if(bn < NDINDIRECT){
    // load double-indirect block, allocate if necessary
    if((addr = ip->addrs[NDIRECT+1]) == 0)
      ip->addrs[NDIRECT] = addr = balloc(ip->dev);
    dbp = bread(ip->dev,addr);
    indirect = (uint*)dbp->data;
    // load indirect block, allocate if necessary
    if((addr = indirect[bn/NINDIRECT]) == 0)
      indirect[bn/NINDIRECT] = addr = balloc(ip->dev);
    bp = bread(ip->dev,addr);
    a = (uint*)bp->data;
    // get direct pointer, allocate if necessary
    if((addr = a[bn%NINDIRECT]) == 0){
      a[bn%NINDIRECT] = addr = balloc(ip->dev);
      log_write(bp);
    }
    // release indirect & double-indirect blocks
    brelse(bp);
    brelse(dbp);
    // return disk block address
    return addr;
  }

  panic("bmap: out of range");
}

// Truncate inode (discard contents).
// Only called when the inode has no links
// to it (no directory entries referring to it)
// and has no in-memory reference to it (is
// not an open file or current directory).
static void
itrunc(struct inode *ip)
{
  int i, j, k;
  struct buf *bp;
  struct buf *dbp;
  uint *a;
  uint *d;

  // free the direct pointers
  for(i = 0; i < NDIRECT; i++){
    if(ip->addrs[i]){
      bfree(ip->dev, ip->addrs[i]);
      ip->addrs[i] = 0;
    }
  }

  // free the indirect pointer
  if(ip->addrs[NDIRECT]){
    // load the indirect block
    bp = bread(ip->dev, ip->addrs[NDIRECT]);
    a = (uint*)bp->data;
    // iterate over the block, and free all direct pointers
    for(j = 0; j < NINDIRECT; j++){
      if(a[j])
        bfree(ip->dev, a[j]);
    }
    // free the indirect pointer
    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT]);
    ip->addrs[NDIRECT] = 0;
  }

  // free the double indirect pointer
  if(ip->addrs[NDIRECT+1]){
    // load the double-indirect block
    dbp = bread(ip->dev, ip->addrs[NDIRECT+1]);
    d = (uint*)dbp->data;
    // iterate over the block, and free all indirect blocks
    for(k = 0; k < NINDIRECT; k++){
      // load the indirect block
      bp = bread(ip->dev, d[k]);
      a = (uint*)bp->data;
      // iterate over the block, and free all direct pointers
      for(j = 0; j < NINDIRECT; j++){
        if(a[j])
          bfree(ip->dev,a[j]);
      }
      // free the infirect pointer
      brelse(bp);
      bfree(ip->dev, d[k]);
      d[k] = 0;
    }
    // free the double-indirect pointer
    brelse(dbp);
    bfree(ip->dev, ip->addrs[NDIRECT+1]);
    ip->addrs[NDIRECT+1] = 0;
  }

  ip->size = 0;
  iupdate(ip);
}

The output I get from the usertests utility shows that the writing part is successful, but the system panic when it attempt to read the big file:

wrote 16523 sectors
closed the file for writing
opend the file for reading
read 0 sectors
lapicid 1: panic: log_write outside of trans
 80102f18 80101249 80101442 80101b67 80100f89 80104b42 8010482a 80105829 80105572 0

I modified the bigfile() test a bit, so I will post it here too:

void
bigfile(void)
{
  char buf[512];
  int fd, i, sectors;
  unlink("big.file");
  fd = open("big.file", O_CREATE | O_WRONLY);
  if(fd < 0){
    printf(2, "big: cannot open big.file for writing\n");
    exit();
  }

  sectors = 0;
  while(1){
    *(int*)buf = sectors;
    int cc = write(fd, buf, sizeof(buf));
    if(cc <= 0)
      break;
    sectors++;
        if (sectors % 100 == 0){
                printf(2, ".");
                printf(2,"sector: %d\n",sectors);
        }
  }

  printf(1, "\nwrote %d sectors\n", sectors);

  close(fd);
  printf(1,"closed the file for writing\n");

  fd = open("big.file", O_RDONLY);
  if(fd < 0){
    printf(2, "big: cannot re-open big.file for reading\n");
    exit();
  }
  printf(1,"opend the file for reading\n");

  for(i = 0; i < sectors; i++){
    int cc = read(fd, buf, sizeof(buf));
    if(cc <= 0){
      printf(2, "big: read error at sector %d\n", i);
      exit();
    }
    if(*(int*)buf != i){
      printf(2, "big: read the wrong data (%d) for sector %d\n",
             *(int*)buf, i);
      exit();
    }
    if(i%100 == 0)
        printf(2,"read %d sectors\n",i);
  }

  close(fd);
  unlink("big.file");

  printf(1, "bigfile test ok\n");
}

Thanks in advance for any help. P.S: of course I modified the size of FSSIZE in the param.h file to be big enough for double indirect pointers.

Z E Nir
  • 332
  • 1
  • 2
  • 15
  • Check for off-by-one errors. i.e. fiddle with the sizes, adding 1 and subtracting 1 and then testing again. – S.S. Anne Jan 30 '20 at 12:23
  • Thanks. can you be more specific? like - what fields should I fiddle with? `bigfile()` is a very slow test, and since the crash happens at the end of it I don't want to waste 20 minutes for a whim... – Z E Nir Jan 30 '20 at 12:26
  • I'm not terribly familiar with xv6, sorry. I messed with it for a while but I didn't really learn how to add to the kernel. – S.S. Anne Jan 30 '20 at 12:27
  • I did not get the same errors as you. Could you tell us how you change `FSSIZE` and from which git revision you've started – Mathieu Jan 31 '20 at 08:57
  • sorry for the late response. I set `FSSIZE` to 20000 (maybe its too much? 20000 still gave me an error for insufficient available blocks...). It's hard to determine the initial git revision, since it's the original github repository with some modified files from my brother's university... – Z E Nir Feb 02 '20 at 12:08
  • I think you've got too much differences from the github for us to help you. You can see all differences between github and your code with some commands like `git diff` or `git log -p`. When starting, `iinit` function tells some information: `sb: size: ...` What is displayed on your system? – Mathieu Feb 04 '20 at 11:08

0 Answers0