// Get the next splice, read it into the response. func (s *spliceServer) serveChunk(req *Request, rep *Response) (err error) { if req.Start == 0 { err := s.prepareServe(req.Hash) if err != nil { rep.Have = false return nil } } rep.Have = true spl := s.serve(req.Hash, int64(req.Start)) if spl == nil { return s.store.ServeChunk(req, rep) } defer splice.Done(spl.pair) data := make([]byte, spl.size) n, err := spl.pair.Read(data) if err != nil { return s.store.ServeChunk(req, rep) } rep.Chunk = data[:n] rep.Size = n rep.Last = spl.last return nil }
func (ms *MountState) trySplice(header []byte, req *request, fdData *ReadResultFd) error { pair, err := splice.Get() if err != nil { return err } defer splice.Done(pair) total := len(header) + fdData.Size() if err := pair.Grow(total); err != nil { return err } _, err = pair.Write(header) if err != nil { return err } var n int if fdData.Off < 0 { n, err = pair.LoadFrom(fdData.Fd, fdData.Size()) } else { n, err = pair.LoadFromAt(fdData.Fd, fdData.Size(), fdData.Off) } if err == io.EOF || (err == nil && n < fdData.Size()) { discard := make([]byte, len(header)) _, err = pair.Read(discard) if err != nil { return err } header = req.serializeHeader(n) newFd := ReadResultFd{ Fd: pair.ReadFd(), Off: -1, Sz: n, } return ms.trySplice(header, req, &newFd) } if err != nil { // TODO - extract the data from splice. return err } if n != fdData.Size() { return fmt.Errorf("wrote %d, want %d", n, fdData.Size()) } _, err = pair.WriteTo(uintptr(ms.mountFd), total) if err != nil { return err } return nil }
func (s *spliceServer) Close() { chans := []chan ServeSplice{} s.mu.Lock() for _, p := range s.pending { chans = append(chans, p...) } s.pending = map[serverKey][]chan ServeSplice{} s.mu.Unlock() for _, c := range chans { for r := range c { splice.Done(r.pair) } } }
// trySplice: Zero-copy read from fdData.Fd into /dev/fuse // // This is a four-step process: // // 1) Splice data form fdData.Fd into the "pair1" pipe buffer --> pair1: [payload] // Now we know the actual payload length and can // construct the reply header // 2) Write header into the "pair2" pipe buffer --> pair2: [header] // 4) Splice data from "pair1" into "pair2" --> pair2: [header][payload] // 3) Splice the data from "pair2" into /dev/fuse // // This dance is neccessary because header and payload cannot be split across // two splices and we cannot seek in a pipe buffer. func (ms *Server) trySplice(header []byte, req *request, fdData *readResultFd) error { var err error // Get a pair of connected pipes pair1, err := splice.Get() if err != nil { return err } defer splice.Done(pair1) // Grow buffer pipe to requested size + one extra page // Without the extra page the kernel will block once the pipe is almost full pair1Sz := fdData.Size() + os.Getpagesize() if err := pair1.Grow(pair1Sz); err != nil { return err } // Read data from file payloadLen, err := pair1.LoadFromAt(fdData.Fd, fdData.Size(), fdData.Off) if err != nil { // TODO - extract the data from splice. return err } // Get another pair of connected pipes pair2, err := splice.Get() if err != nil { return err } defer splice.Done(pair2) // Grow pipe to header + actually read size + one extra page // Without the extra page the kernel will block once the pipe is almost full header = req.serializeHeader(payloadLen) total := len(header) + payloadLen pair2Sz := total + os.Getpagesize() if err := pair2.Grow(pair2Sz); err != nil { return err } // Write header into pair2 n, err := pair2.Write(header) if err != nil { return err } if n != len(header) { return fmt.Errorf("Short write into splice: wrote %d, want %d", n, len(header)) } // Write data into pair2 n, err = pair2.LoadFrom(pair1.ReadFd(), payloadLen) if err != nil { return err } if n != payloadLen { return fmt.Errorf("Short splice: wrote %d, want %d", n, payloadLen) } // Write header + data to /dev/fuse _, err = pair2.WriteTo(uintptr(ms.mountFd), total) if err != nil { return err } return nil }