# sync_file_range ____________________________________________
#
# Why is there a 'sync_file_range' and a 'sync_file_range2'? As the
# man page says:
#
#   Some architectures (e.g., PowerPC, ARM) need 64-bit arguments to be
#   aligned in a suitable pair of registers. On such architectures, the
#   call signature of sync_file_range()... would force a register to be
#   be wasted as padding between the 'fd' and 'offset' arguments.
#
# SYSCALL_DEFINE4(sync_file_range, int, fd, loff_t, offset, loff_t, nbytes,
#				unsigned int, flags)
# SYSCALL_DEFINE4(sync_file_range2, int, fd, unsigned int, flags,
#				 loff_t, offset, loff_t, nbytes)
#
# But, sync_file_range2() is just a wrapper around sync_file_range, so
# we can just probe that.

@define _SYSCALL_SYNC_FILE_RANGE_NAME
%(
	name = "sync_file_range"
%)

@define _SYSCALL_SYNC_FILE_RANGE_ARGSTR
%(
	argstr = sprintf("%d, %d, %d, %s", fd, offset, nbytes, flags_str)
%)

probe syscall.sync_file_range = dw_syscall.sync_file_range !,
                                nd_syscall.sync_file_range ? {}
probe syscall.sync_file_range.return = dw_syscall.sync_file_range.return !,
                                       nd_syscall.sync_file_range.return ? {}

# dw_sync_file_range _____________________________________________________

probe dw_syscall.sync_file_range = kernel.function("sys_sync_file_range").call ?
{
	@_SYSCALL_SYNC_FILE_RANGE_NAME
	fd = __int32($fd)
	offset = $offset
	nbytes = $nbytes
	flags = __uint32($flags)
	flags_str = _sync_file_range_flags_str(flags)
	@_SYSCALL_SYNC_FILE_RANGE_ARGSTR
}
probe dw_syscall.sync_file_range.return =
	kernel.function("sys_sync_file_range").return ?
{
	@_SYSCALL_SYNC_FILE_RANGE_NAME
	@SYSC_RETVALSTR($return)
}

# nd_sync_file_range _____________________________________________________

probe nd_syscall.sync_file_range = nd1_syscall.sync_file_range!, nd2_syscall.sync_file_range!, tp_syscall.sync_file_range
  { }

probe nd1_syscall.sync_file_range = kprobe.function("sys_sync_file_range") ?
{
	asmlinkage()
	@_SYSCALL_SYNC_FILE_RANGE_NAME
	fd = int_arg(1)
%( CONFIG_64BIT == "y" %?
	offset = longlong_arg(2)
	nbytes = longlong_arg(3)
	flags = uint_arg(4)
%:
%( arch == "arm" %?
	# arm has some odd rules regarding long long arguments.
	offset = longlong_arg(3)
	nbytes = longlong_arg(5)
	flags = uint_arg(7)
%:
	offset = longlong_arg(2)
	nbytes = longlong_arg(4)
	flags = uint_arg(6)
%)
%)
	flags_str = _sync_file_range_flags_str(flags)
	@_SYSCALL_SYNC_FILE_RANGE_ARGSTR
}

/* kernel 4.17+ */
probe nd2_syscall.sync_file_range = __nd2_syscall.sync_file_range,
                                    __nd2_syscall.compat_sync_file_range
{
	@_SYSCALL_SYNC_FILE_RANGE_NAME
	@_SYSCALL_SYNC_FILE_RANGE_ARGSTR
}
probe __nd2_syscall.sync_file_range =
	kprobe.function(@arch_syscall_prefix "sys_sync_file_range") ?
{
	__set_syscall_pt_regs(pointer_arg(1))
	fd = int_arg(1)
	offset = longlong_arg(2)
	nbytes = longlong_arg(3)
	flags = uint_arg(4)
	flags_str = _sync_file_range_flags_str(flags)
},
{
%( @_IS_SREG_KERNEL %?
	if (@probewrite(fd))
	  set_int_arg(1, fd)

	if (@probewrite(offset))
	  set_longlong_arg(2, offset)

	if (@probewrite(nbytes))
	  set_longlong_arg(3, nbytes)

	if (@probewrite(flags))
	  set_uint_arg(4, flags)
%)
}
probe __nd2_syscall.compat_sync_file_range =
	kprobe.function(@arch_syscall_prefix "compat_sys_x86_sync_file_range") ?
{
	__set_syscall_pt_regs(pointer_arg(1))
	fd = int_arg(1)
	offset = (uint_arg(3) << 32) | uint_arg(2)
	nbytes = (uint_arg(5) << 32) | uint_arg(4)
	flags = uint_arg(6)
	flags_str = _sync_file_range_flags_str(flags)
},
{
%( @_IS_SREG_KERNEL %?
	if (@probewrite(fd))
	  set_int_arg(1, fd)

	if (@probewrite(offset)) {
	  set_uint_arg(2, offset & 0xffffffff)
	  set_uint_arg(3, offset >> 32)
	}

	if (@probewrite(nbytes)) {
	  set_uint_arg(4, nbytes & 0xffffffff)
	  set_uint_arg(5, nbytes >> 32)
	}

	if (@probewrite(flags))
	  set_uint_arg(6, flags)
%)
}

/* kernel 3.5+, but undesirable because it affects all syscalls */
probe tp_syscall.sync_file_range = kernel.trace("sys_enter")
{
	__set_syscall_pt_regs($regs)
	@__syscall_compat_gate(@const("__NR_sync_file_range"),
			       @const("__NR_compat_sync_file_range"))
	@_SYSCALL_SYNC_FILE_RANGE_NAME
	fd = int_arg(1)
	if (@__compat_task) {
		offset = (uint_arg(3) << 32) | uint_arg(2)
		nbytes = (uint_arg(5) << 32) | uint_arg(4)
		flags = uint_arg(6)
	}
	else {
		offset = longlong_arg(2)
		nbytes = longlong_arg(3)
		flags = uint_arg(4)
	}
	flags_str = _sync_file_range_flags_str(flags)
	@_SYSCALL_SYNC_FILE_RANGE_ARGSTR
},
{
%( @_IS_SREG_KERNEL %?
	if (@probewrite(fd))
	  set_int_arg(1, fd)

	if (@__compat_task) {
		if (@probewrite(offset)) {
		  set_uint_arg(2, offset & 0xffffffff)
		  set_uint_arg(3, offset >> 32)
		}

		if (@probewrite(nbytes)) {
		  set_uint_arg(4, nbytes & 0xffffffff)
		  set_uint_arg(5, nbytes >> 32)
		}
		if (@probewrite(flags))
		  set_uint_arg(6, flags)
	} else {
		if (@probewrite(offset))
		  set_longlong_arg(2, offset)

		if (@probewrite(nbytes))
		  set_longlong_arg(3, nbytes)

		if (@probewrite(flags))
		  set_uint_arg(4, flags)
	}
%)
}

probe nd_syscall.sync_file_range.return = nd1_syscall.sync_file_range.return!, nd2_syscall.sync_file_range.return!, tp_syscall.sync_file_range.return
  { }

probe nd1_syscall.sync_file_range.return =
	kprobe.function("sys_sync_file_range").return ?
{
	@_SYSCALL_SYNC_FILE_RANGE_NAME
	@SYSC_RETVALSTR(returnval())
}

/* kernel 4.17+ */
probe nd2_syscall.sync_file_range.return =
	kprobe.function(@arch_syscall_prefix "sys_sync_file_range").return ?,
	kprobe.function(@arch_syscall_prefix "compat_sys_x86_sync_file_range").return ?
{
	@_SYSCALL_SYNC_FILE_RANGE_NAME
	@SYSC_RETVALSTR(returnval())
}

/* kernel 3.5+, but undesirable because it affects all syscalls */
probe tp_syscall.sync_file_range.return = kernel.trace("sys_exit")
{
	__set_syscall_pt_regs($regs)
	@__syscall_compat_gate(@const("__NR_sync_file_range"),
			       @const("__NR_compat_sync_file_range"))
	@_SYSCALL_SYNC_FILE_RANGE_NAME
	@SYSC_RETVALSTR($ret)
}
