mount: support direct-io for loopback device

Signed-off-by: Wei Fu <fuweid89@gmail.com>
This commit is contained in:
Wei Fu 2023-06-15 06:04:19 +00:00
parent ded713010c
commit 72b7d16505
2 changed files with 41 additions and 24 deletions

View File

@ -89,6 +89,12 @@ func setupLoopDev(backingFile, loopDev string, param LoopParams) (_ *os.File, re
return nil, fmt.Errorf("could not set loop fd for device: %s: %w", loopDev, err)
}
defer func() {
if retErr != nil {
_ = unix.IoctlSetInt(int(loop.Fd()), unix.LOOP_CLR_FD, 0)
}
}()
// 3. Set Info
info := unix.LoopInfo64{}
copy(info.File_name[:], backingFile)
@ -100,27 +106,20 @@ func setupLoopDev(backingFile, loopDev string, param LoopParams) (_ *os.File, re
info.Flags |= unix.LO_FLAGS_AUTOCLEAR
}
if param.Direct {
info.Flags |= unix.LO_FLAGS_DIRECT_IO
}
err = unix.IoctlLoopSetStatus64(int(loop.Fd()), &info)
if err == nil {
return loop, nil
if err != nil {
return nil, fmt.Errorf("failed to set loop device info: %w", err)
}
// 4. Set Direct IO
if param.Direct {
// Retry w/o direct IO flag in case kernel does not support it. The downside is that
// it will suffer from double cache problem.
info.Flags &= ^(uint32(unix.LO_FLAGS_DIRECT_IO))
err = unix.IoctlLoopSetStatus64(int(loop.Fd()), &info)
if err == nil {
return loop, nil
err = unix.IoctlSetInt(int(loop.Fd()), unix.LOOP_SET_DIRECT_IO, 1)
if err != nil {
return nil, fmt.Errorf("failed to setup loop with direct: %w", err)
}
}
_ = unix.IoctlSetInt(int(loop.Fd()), unix.LOOP_CLR_FD, 0)
return nil, fmt.Errorf("failed to set loop device info: %v", err)
return loop, nil
}
// setupLoop looks for (and possibly creates) a free loop device, and

View File

@ -63,9 +63,6 @@ func (m *Mount) mount(target string) (err error) {
}
flags, data, losetup := parseMountOptions(options)
if len(data) > pagesize {
return errors.New("mount options is too long")
}
// propagation types.
const ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE
@ -73,15 +70,27 @@ func (m *Mount) mount(target string) (err error) {
// Ensure propagation type change flags aren't included in other calls.
oflags := flags &^ ptypes
var loopParams LoopParams
if losetup {
loopParams = LoopParams{
Readonly: oflags&unix.MS_RDONLY == unix.MS_RDONLY,
Autoclear: true,
}
loopParams.Direct, data = hasDirectIO(data)
}
dataInStr := strings.Join(data, ",")
if len(dataInStr) > pagesize {
return errors.New("mount options is too long")
}
// In the case of remounting with changed data (data != ""), need to call mount (moby/moby#34077).
if flags&unix.MS_REMOUNT == 0 || data != "" {
if flags&unix.MS_REMOUNT == 0 || dataInStr != "" {
// Initial call applying all non-propagation flags for mount
// or remount with changed data
source := m.Source
if losetup {
loFile, err := setupLoop(m.Source, LoopParams{
Readonly: oflags&unix.MS_RDONLY == unix.MS_RDONLY,
Autoclear: true})
loFile, err := setupLoop(m.Source, loopParams)
if err != nil {
return err
}
@ -90,7 +99,7 @@ func (m *Mount) mount(target string) (err error) {
// Mount the loop device instead
source = loFile.Name()
}
if err := mountAt(chdir, source, target, m.Type, uintptr(oflags), data); err != nil {
if err := mountAt(chdir, source, target, m.Type, uintptr(oflags), dataInStr); err != nil {
return err
}
}
@ -199,7 +208,7 @@ func UnmountAll(mount string, flags int) error {
// parseMountOptions takes fstab style mount options and parses them for
// use with a standard mount() syscall
func parseMountOptions(options []string) (int, string, bool) {
func parseMountOptions(options []string) (int, []string, bool) {
var (
flag int
losetup bool
@ -252,7 +261,16 @@ func parseMountOptions(options []string) (int, string, bool) {
data = append(data, o)
}
}
return flag, strings.Join(data, ","), losetup
return flag, data, losetup
}
func hasDirectIO(opts []string) (bool, []string) {
for idx, opt := range opts {
if opt == "direct-io" {
return true, append(opts[:idx], opts[idx+1:]...)
}
}
return false, opts
}
// compactLowerdirOption updates overlay lowdir option and returns the common