github.com/yasker/longhorn-engine@v0.0.0-20160621014712-6ed6cfca0729/replica/diff_disk.go (about)

     1  package replica
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/frostschutz/go-fibmap"
     8  	"github.com/rancher/longhorn/types"
     9  )
    10  
    11  type diffDisk struct {
    12  	rmLock sync.Mutex
    13  	// mapping of sector to index in the files array. a value of 0 is special meaning
    14  	// we don't know the location yet.
    15  	location []byte
    16  	// list of files in child, parent, grandparent, etc order.
    17  	// index 0 is nil and index 1 is the active write layer
    18  	files      []types.DiffDisk
    19  	sectorSize int64
    20  }
    21  
    22  func (d *diffDisk) RemoveIndex(index int) error {
    23  	if err := d.files[index].Close(); err != nil {
    24  		return err
    25  	}
    26  
    27  	for i := 0; i < len(d.location); i++ {
    28  		if d.location[i] >= byte(index) {
    29  			// set back to unknown
    30  			d.location[i] = 0
    31  		}
    32  	}
    33  
    34  	d.files = append(d.files[:index], d.files[index+1:]...)
    35  
    36  	return nil
    37  }
    38  
    39  func (d *diffDisk) WriteAt(buf []byte, offset int64) (int, error) {
    40  	startOffset := offset % d.sectorSize
    41  	startCut := d.sectorSize - startOffset
    42  	endOffset := (int64(len(buf)) + offset) % d.sectorSize
    43  
    44  	if len(buf) == 0 {
    45  		return 0, nil
    46  	}
    47  
    48  	if startOffset == 0 && endOffset == 0 {
    49  		return d.fullWriteAt(buf, offset)
    50  	}
    51  
    52  	// single block
    53  	if startCut >= int64(len(buf)) {
    54  		return d.readModifyWrite(buf, offset)
    55  	}
    56  
    57  	if _, err := d.readModifyWrite(buf[0:startCut], offset); err != nil {
    58  		return 0, err
    59  	}
    60  
    61  	if _, err := d.fullWriteAt(buf[startCut:int64(len(buf))-endOffset], offset+startCut); err != nil {
    62  		return 0, err
    63  	}
    64  
    65  	if _, err := d.readModifyWrite(buf[int64(len(buf))-endOffset:], offset+int64(len(buf))-endOffset); err != nil {
    66  		return 0, err
    67  	}
    68  
    69  	return len(buf), nil
    70  }
    71  
    72  func (d *diffDisk) readModifyWrite(buf []byte, offset int64) (int, error) {
    73  	if len(buf) == 0 {
    74  		return 0, nil
    75  	}
    76  
    77  	d.rmLock.Lock()
    78  	defer d.rmLock.Unlock()
    79  
    80  	readBuf := make([]byte, d.sectorSize)
    81  	readOffset := (offset / d.sectorSize) * d.sectorSize
    82  
    83  	if _, err := d.fullReadAt(readBuf, readOffset); err != nil {
    84  		return 0, err
    85  	}
    86  
    87  	copy(readBuf[offset%d.sectorSize:], buf)
    88  
    89  	return d.fullWriteAt(readBuf, readOffset)
    90  }
    91  
    92  func (d *diffDisk) fullWriteAt(buf []byte, offset int64) (int, error) {
    93  	if int64(len(buf))%d.sectorSize != 0 || offset%d.sectorSize != 0 {
    94  		return 0, fmt.Errorf("Write len(%d), offset %d not a multiple of %d", len(buf), offset, d.sectorSize)
    95  	}
    96  
    97  	target := byte(len(d.files) - 1)
    98  	startSector := offset / d.sectorSize
    99  	sectors := int64(len(buf)) / d.sectorSize
   100  
   101  	c, err := d.files[target].WriteAt(buf, offset)
   102  
   103  	// Regardless of err mark bytes as written
   104  	for i := int64(0); i < sectors; i++ {
   105  		d.location[startSector+i] = target
   106  	}
   107  
   108  	return c, err
   109  }
   110  
   111  func (d *diffDisk) ReadAt(buf []byte, offset int64) (int, error) {
   112  	startOffset := offset % d.sectorSize
   113  	startCut := d.sectorSize - startOffset
   114  	endOffset := (int64(len(buf)) + offset) % d.sectorSize
   115  
   116  	if len(buf) == 0 {
   117  		return 0, nil
   118  	}
   119  
   120  	if startOffset == 0 && endOffset == 0 {
   121  		return d.fullReadAt(buf, offset)
   122  	}
   123  
   124  	readBuf := make([]byte, d.sectorSize)
   125  	if _, err := d.fullReadAt(readBuf, offset-startOffset); err != nil {
   126  		return 0, err
   127  	}
   128  
   129  	copy(buf, readBuf[startOffset:])
   130  
   131  	if startCut >= int64(len(buf)) {
   132  		return len(buf), nil
   133  	}
   134  
   135  	if _, err := d.fullReadAt(buf[startCut:int64(len(buf))-endOffset], offset+startCut); err != nil {
   136  		return 0, err
   137  	}
   138  
   139  	if endOffset > 0 {
   140  		if _, err := d.fullReadAt(readBuf, offset+int64(len(buf))-endOffset); err != nil {
   141  			return 0, err
   142  		}
   143  
   144  		copy(buf[int64(len(buf))-endOffset:], readBuf[:endOffset])
   145  	}
   146  
   147  	return len(buf), nil
   148  }
   149  
   150  func (d *diffDisk) fullReadAt(buf []byte, offset int64) (int, error) {
   151  	if int64(len(buf))%d.sectorSize != 0 || offset%d.sectorSize != 0 {
   152  		return 0, fmt.Errorf("Read not a multiple of %d", d.sectorSize)
   153  	}
   154  
   155  	if len(buf) == 0 {
   156  		return 0, nil
   157  	}
   158  
   159  	count := 0
   160  	sectors := int64(len(buf)) / d.sectorSize
   161  	readSectors := int64(1)
   162  	startSector := offset / d.sectorSize
   163  	target, err := d.lookup(startSector)
   164  	if err != nil {
   165  		return count, err
   166  	}
   167  
   168  	for i := int64(1); i < sectors; i++ {
   169  		newTarget, err := d.lookup(startSector + i)
   170  		if err != nil {
   171  			return count, err
   172  		}
   173  
   174  		if newTarget == target {
   175  			readSectors++
   176  		} else {
   177  			c, err := d.read(target, buf, offset, i-readSectors, readSectors)
   178  			count += c
   179  			if err != nil {
   180  				return count, err
   181  			}
   182  			readSectors = 1
   183  			target = newTarget
   184  		}
   185  	}
   186  
   187  	if readSectors > 0 {
   188  		c, err := d.read(target, buf, offset, sectors-readSectors, readSectors)
   189  		count += c
   190  		if err != nil {
   191  			return count, err
   192  		}
   193  	}
   194  
   195  	return count, nil
   196  }
   197  
   198  func (d *diffDisk) read(target byte, buf []byte, offset int64, startSector int64, sectors int64) (int, error) {
   199  	bufStart := startSector * d.sectorSize
   200  	bufEnd := sectors * d.sectorSize
   201  	newBuf := buf[bufStart : bufStart+bufEnd]
   202  	return d.files[target].ReadAt(newBuf, offset+bufStart)
   203  }
   204  
   205  func (d *diffDisk) lookup(sector int64) (byte, error) {
   206  	if sector >= int64(len(d.location)) {
   207  		// We know the IO will result in EOF
   208  		return byte(len(d.files) - 1), nil
   209  	}
   210  
   211  	// small optimization
   212  	if int64(len(d.files)) == 2 {
   213  		return 1, nil
   214  	}
   215  
   216  	target := d.location[sector]
   217  
   218  	if target == 0 {
   219  		for i := len(d.files) - 1; i > 0; i-- {
   220  			if i == 1 {
   221  				// This is important that for index 1 we don't check Fiemap because it may be a base image file
   222  				// Also the result has to be 1
   223  				d.location[sector] = byte(i)
   224  				return byte(i), nil
   225  			}
   226  
   227  			e, err := fibmap.Fiemap(d.files[i].Fd(), uint64(sector*d.sectorSize), uint64(d.sectorSize), 1)
   228  			if err != 0 {
   229  				return byte(0), err
   230  			}
   231  			if len(e) > 0 {
   232  				d.location[sector] = byte(i)
   233  				return byte(i), nil
   234  			}
   235  		}
   236  		return byte(len(d.files) - 1), nil
   237  	}
   238  	return target, nil
   239  }