summaryrefslogtreecommitdiff
path: root/cmd/ioworkload/scenario_close.go
blob: fc5044cca6d9cc0877a8d3edbf0fa49152b09c6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package main

import (
	"fmt"
	"path/filepath"
	"syscall"
)

const sysCloseRange = 436

// closeBasic opens multiple files and closes them.
func closeBasic() error {
	dir, cleanup, err := makeTempDir("close-basic")
	if err != nil {
		return err
	}
	defer cleanup()

	var fds []int
	for i := range 3 {
		path := filepath.Join(dir, fmt.Sprintf("closefile-%d.txt", i))
		fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
		if err != nil {
			return fmt.Errorf("open %d: %w", i, err)
		}
		fds = append(fds, fd)
	}
	for _, fd := range fds {
		if err := syscall.Close(fd); err != nil {
			return fmt.Errorf("close fd %d: %w", fd, err)
		}
	}
	return nil
}

// closeRange opens multiple files and closes a range of them via close_range(2).
func closeRange() error {
	dir, cleanup, err := makeTempDir("close-range")
	if err != nil {
		return err
	}
	defer cleanup()

	var fds []int
	for i := range 3 {
		path := filepath.Join(dir, fmt.Sprintf("closerangefile-%d.txt", i))
		fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
		if err != nil {
			return fmt.Errorf("open %d: %w", i, err)
		}
		fds = append(fds, fd)
	}

	if fds[2]-fds[0] != 2 {
		return fmt.Errorf("fds not contiguous: %v", fds)
	}

	first := uintptr(fds[0])
	last := uintptr(fds[len(fds)-1])
	_, _, errno := syscall.Syscall(sysCloseRange, first, last, 0)
	if errno != 0 {
		return fmt.Errorf("close_range: %w", errno)
	}
	return nil
}

// closeInvalidFd attempts to close a very high fd number that is not open.
// The close fails with EBADF, but ior should capture the enter_close tracepoint
// because arguments are read on syscall entry before the kernel returns an error.
func closeInvalidFd() error {
	err := syscall.Close(99999)
	if err == nil {
		return fmt.Errorf("expected close of invalid fd to fail")
	}
	return nil
}

// closeDoubleClose opens a file, closes it normally, then closes the same fd again.
// The second close fails with EBADF, but ior should capture both enter_close
// tracepoints because arguments are read on syscall entry.
func closeDoubleClose() error {
	dir, cleanup, err := makeTempDir("close-double-close")
	if err != nil {
		return err
	}
	defer cleanup()

	path := filepath.Join(dir, "doubleclosefile.txt")
	fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
	if err != nil {
		return fmt.Errorf("open: %w", err)
	}

	if err := syscall.Close(fd); err != nil {
		return fmt.Errorf("first close: %w", err)
	}

	err = syscall.Close(fd)
	if err == nil {
		return fmt.Errorf("expected second close of same fd to fail")
	}
	return nil
}

// closeRangeEmpty calls close_range(2) with a range of very high fd numbers
// (9000–9999) where no fds are open. The syscall succeeds (empty range is valid),
// and ior should capture the enter_close_range tracepoint.
func closeRangeEmpty() error {
	// Retry a few times to reduce event-loss flakiness under heavy test load.
	for i := 0; i < 5; i++ {
		_, _, errno := syscall.Syscall(sysCloseRange, 9000, 9999, 0)
		if errno != 0 {
			return fmt.Errorf("close_range: %w", errno)
		}
	}
	return nil
}