Skip to content

Commit c9eebe3

Browse files
fuweiddcantah
authored andcommitted
mount: support direct-io for loopback device
Signed-off-by: Wei Fu <[email protected]> (cherry picked from commit 72b7d16) Signed-off-by: Danny Canter <[email protected]>
1 parent 1c2977d commit c9eebe3

2 files changed

Lines changed: 41 additions & 24 deletions

File tree

mount/losetup_linux.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ func setupLoopDev(backingFile, loopDev string, param LoopParams) (_ *os.File, re
8989
return nil, fmt.Errorf("could not set loop fd for device: %s: %w", loopDev, err)
9090
}
9191

92+
defer func() {
93+
if retErr != nil {
94+
_ = unix.IoctlSetInt(int(loop.Fd()), unix.LOOP_CLR_FD, 0)
95+
}
96+
}()
97+
9298
// 3. Set Info
9399
info := unix.LoopInfo64{}
94100
copy(info.File_name[:], backingFile)
@@ -100,27 +106,20 @@ func setupLoopDev(backingFile, loopDev string, param LoopParams) (_ *os.File, re
100106
info.Flags |= unix.LO_FLAGS_AUTOCLEAR
101107
}
102108

103-
if param.Direct {
104-
info.Flags |= unix.LO_FLAGS_DIRECT_IO
105-
}
106-
107109
err = unix.IoctlLoopSetStatus64(int(loop.Fd()), &info)
108-
if err == nil {
109-
return loop, nil
110+
if err != nil {
111+
return nil, fmt.Errorf("failed to set loop device info: %w", err)
110112
}
111113

114+
// 4. Set Direct IO
112115
if param.Direct {
113-
// Retry w/o direct IO flag in case kernel does not support it. The downside is that
114-
// it will suffer from double cache problem.
115-
info.Flags &= ^(uint32(unix.LO_FLAGS_DIRECT_IO))
116-
err = unix.IoctlLoopSetStatus64(int(loop.Fd()), &info)
117-
if err == nil {
118-
return loop, nil
116+
err = unix.IoctlSetInt(int(loop.Fd()), unix.LOOP_SET_DIRECT_IO, 1)
117+
if err != nil {
118+
return nil, fmt.Errorf("failed to setup loop with direct: %w", err)
119119
}
120120
}
121121

122-
_ = unix.IoctlSetInt(int(loop.Fd()), unix.LOOP_CLR_FD, 0)
123-
return nil, fmt.Errorf("failed to set loop device info: %v", err)
122+
return loop, nil
124123
}
125124

126125
// setupLoop looks for (and possibly creates) a free loop device, and

mount/mount_linux.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,25 +63,34 @@ func (m *Mount) mount(target string) (err error) {
6363
}
6464

6565
flags, data, losetup := parseMountOptions(options)
66-
if len(data) > pagesize {
67-
return errors.New("mount options is too long")
68-
}
6966

7067
// propagation types.
7168
const ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE
7269

7370
// Ensure propagation type change flags aren't included in other calls.
7471
oflags := flags &^ ptypes
7572

73+
var loopParams LoopParams
74+
if losetup {
75+
loopParams = LoopParams{
76+
Readonly: oflags&unix.MS_RDONLY == unix.MS_RDONLY,
77+
Autoclear: true,
78+
}
79+
loopParams.Direct, data = hasDirectIO(data)
80+
}
81+
82+
dataInStr := strings.Join(data, ",")
83+
if len(dataInStr) > pagesize {
84+
return errors.New("mount options is too long")
85+
}
86+
7687
// In the case of remounting with changed data (data != ""), need to call mount (moby/moby#34077).
77-
if flags&unix.MS_REMOUNT == 0 || data != "" {
88+
if flags&unix.MS_REMOUNT == 0 || dataInStr != "" {
7889
// Initial call applying all non-propagation flags for mount
7990
// or remount with changed data
8091
source := m.Source
8192
if losetup {
82-
loFile, err := setupLoop(m.Source, LoopParams{
83-
Readonly: oflags&unix.MS_RDONLY == unix.MS_RDONLY,
84-
Autoclear: true})
93+
loFile, err := setupLoop(m.Source, loopParams)
8594
if err != nil {
8695
return err
8796
}
@@ -90,7 +99,7 @@ func (m *Mount) mount(target string) (err error) {
9099
// Mount the loop device instead
91100
source = loFile.Name()
92101
}
93-
if err := mountAt(chdir, source, target, m.Type, uintptr(oflags), data); err != nil {
102+
if err := mountAt(chdir, source, target, m.Type, uintptr(oflags), dataInStr); err != nil {
94103
return err
95104
}
96105
}
@@ -199,7 +208,7 @@ func UnmountAll(mount string, flags int) error {
199208

200209
// parseMountOptions takes fstab style mount options and parses them for
201210
// use with a standard mount() syscall
202-
func parseMountOptions(options []string) (int, string, bool) {
211+
func parseMountOptions(options []string) (int, []string, bool) {
203212
var (
204213
flag int
205214
losetup bool
@@ -252,7 +261,16 @@ func parseMountOptions(options []string) (int, string, bool) {
252261
data = append(data, o)
253262
}
254263
}
255-
return flag, strings.Join(data, ","), losetup
264+
return flag, data, losetup
265+
}
266+
267+
func hasDirectIO(opts []string) (bool, []string) {
268+
for idx, opt := range opts {
269+
if opt == "direct-io" {
270+
return true, append(opts[:idx], opts[idx+1:]...)
271+
}
272+
}
273+
return false, opts
256274
}
257275

258276
// compactLowerdirOption updates overlay lowdir option and returns the common

0 commit comments

Comments
 (0)