Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so empty

Empty file.
68 changes: 68 additions & 0 deletions test/functional/api/cas/ioctl/cas_requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#
# Copyright(c) 2021 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#

from api.cas.ioctl.cas_structs import *
from api.cas.ioctl.ioctl import IOWR


class IORequest:
def __init__(self,
command_number: RequestCode,
command_direction=None):
self.command_number = command_number.value
self.command_struct = self.get_struct()
self.command = command_direction(self.command_number, self.command_struct)

def get_struct(self):
pass


class StartCacheRequest(IORequest):
def __init__(self,
cache_path_name: str,
cache_id: int = 1,
init_cache: InitCache = InitCache.CACHE_INIT_NEW,
caching_mode: CacheMode = CacheMode.default,
line_size: CacheLineSize = CacheLineSize.default,
force: int = 1):
self.cache_id = ctypes.c_uint16(cache_id).value
self.init_cache = init_cache.value
self.cache_path_name = ctypes.create_string_buffer(
bytes(cache_path_name, encoding='ascii'), MAX_STR_LEN).value
self.caching_mode = caching_mode.value
self.line_size = line_size.value
self.force = ctypes.c_uint8(force).value
super().__init__(RequestCode.START_CACHE_CODE, IOWR)

def get_struct(self):
return StartCacheStructure(
cache_id=self.cache_id,
init_cache=self.init_cache,
cache_path_name=self.cache_path_name,
caching_mode=self.caching_mode,
line_size=self.line_size,
force=self.force
)

def __repr__(self):
return f'{self.command_struct}'


class StopCacheRequest(IORequest):
def __init__(self,
cache_id: int = 1,
flush_data: int = 1):
self.cache_id = ctypes.c_uint16(cache_id).value
self.flush_data = ctypes.c_uint8(flush_data).value
super().__init__(RequestCode.STOP_CACHE_CODE, IOWR)

def get_struct(self):
return StopCacheStructure(
cache_id=self.cache_id,
flush_data=self.flush_data
)

def __repr__(self):
return f'{self.command_struct}'
108 changes: 108 additions & 0 deletions test/functional/api/cas/ioctl/cas_structs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#
# Copyright(c) 2021 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#

import ctypes
from enum import Enum


class RequestCode(Enum):
START_CACHE_CODE = ctypes.c_uint(21).value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this construct do? I just checked that type(c_uint(21).value) == int so its equivalent with START_CACHE_CODE = 21 (maybe aside from integer wraparound effects, but that doesn't seem to be needed here)

STOP_CACHE_CODE = ctypes.c_uint(2).value
SET_CACHE_STATE_CODE = ctypes.c_uint(3).value
INSERT_CORE_CODE = ctypes.c_uint(22).value
REMOVE_CORE_CODE = ctypes.c_uint(23).value
RESET_STATS_CODE = ctypes.c_uint(6).value
FLUSH_CACHE_CODE = ctypes.c_uint(9).value
INTERRUPT_FLUSHING_CODE = ctypes.c_uint(10).value
FLUSH_CORE_CODE = ctypes.c_uint(11).value
CACHE_INFO_CODE = ctypes.c_uint(24).value
CORE_INFO_CODE = ctypes.c_uint(25).value
PARTITION_INFO_CODE = ctypes.c_uint(14).value
PARTITION_SET_CODE = ctypes.c_uint(15).value
GET_CACHE_COUNT_CODE = ctypes.c_uint(16).value
LIST_CACHE_CODE = ctypes.c_uint(17).value
UPGRADE_CODE = ctypes.c_uint(19).value
GET_CORE_POOL_COUNT_CODE = ctypes.c_uint(26).value
GET_CORE_POOL_PATHS_CODE = ctypes.c_uint(27).value
CORE_POOL_REMOVE_CODE = ctypes.c_uint(28).value
CACHE_CHECK_DEVICE_CODE = ctypes.c_uint(29).value
SET_CORE_PARAM_CODE = ctypes.c_uint(30).value
GET_CORE_PARAM_CODE = ctypes.c_uint(31).value
SET_CACHE_PARAM_CODE = ctypes.c_uint(32).value
GET_CACHE_PARAM_CODE = ctypes.c_uint(33).value
GET_STATS_CODE = ctypes.c_uint(34).value
PURGE_CACHE_CODE = ctypes.c_uint(35).value
PURGE_CORE_CODE = ctypes.c_uint(36).value


KiB = ctypes.c_ulonglong(1024).value
MAX_STR_LEN = 4096
MAX_ELEVATOR_NAME = 16


class InitCache(Enum):
CACHE_INIT_NEW = ctypes.c_uint8(0).value
CACHE_INIT_LOAD = ctypes.c_uint8(1).value


class CacheMode(Enum):
ocf_cache_mode_wt = ctypes.c_int(0).value
ocf_cache_mode_wb = ctypes.c_int(1).value
ocf_cache_mode_wa = ctypes.c_int(2).value
ocf_cache_mode_pt = ctypes.c_int(3).value
ocf_cache_mode_wi = ctypes.c_int(4).value
ocf_cache_mode_wo = ctypes.c_int(5).value
default = ocf_cache_mode_wt


class CacheLineSize(Enum):
ocf_cache_line_size_4 = ctypes.c_ulonglong(4).value * KiB
ocf_cache_line_size_8 = ctypes.c_ulonglong(8).value * KiB
ocf_cache_line_size16 = ctypes.c_ulonglong(16).value * KiB
ocf_cache_line_size_32 = ctypes.c_ulonglong(32).value * KiB
ocf_cache_line_size_64 = ctypes.c_ulonglong(64).value * KiB
default = ocf_cache_line_size_4
Comment on lines +60 to +66
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we use class Size for proper units and then convert bytes value to ctypes type?



class StartCacheStructure(ctypes.Structure):
_fields_ = [
('cache_id', ctypes.c_uint16),
('init_cache', ctypes.c_uint8),
('cache_path_name', ctypes.c_char * MAX_STR_LEN),
('caching_mode', ctypes.c_int),
('flush_data', ctypes.c_uint8),
('line_size', ctypes.c_ulonglong),
('force', ctypes.c_uint8),
('min_free_ram', ctypes.c_uint64),
('metadata_mode_optimal', ctypes.c_uint8),
('cache_elevator', ctypes.c_char * MAX_ELEVATOR_NAME),
('ext_err_code', ctypes.c_int)
]

def __repr__(self):
return (f'cache_id: {self.cache_id}\n'
f'init_cache: {self.init_cache}\n'
f'cache_path_name: {self.cache_path_name}\n'
f'caching_mode: {self.caching_mode}\n'
f'flush_data: {self.flush_data}\n'
f'line_size: {self.line_size}\n'
f'force: {self.force}\n'
f'min_free_ram: {self.min_free_ram}\n'
f'metadata_mode_optimal: {self.metadata_mode_optimal}\n'
f'cache_elevator: {self.cache_elevator}\n'
f'ext_err_code: {self.ext_err_code}\n')


class StopCacheStructure(ctypes.Structure):
_fields_ = [
('cache_id', ctypes.c_uint16),
('flush_data', ctypes.c_uint8),
('ext_err_code', ctypes.c_int)
]

def __repr__(self):
return (f'cache_id: {self.cache_id}\n'
f'flush_data: {self.flush_data}\n'
f'ext_err_code: {self.ext_err_code}\n')
147 changes: 147 additions & 0 deletions test/functional/api/cas/ioctl/ioctl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#
# Copyright(c) 2021 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#

import ctypes
import marshal
import os
from time import sleep

from core.test_run import TestRun
from test_tools.fs_utils import chmod_numerical, remove, check_if_directory_exists, \
create_directory

IOC_NRBITS = 8
IOC_TYPEBITS = 8
IOC_SIZEBITS = 14
IOC_DIRBITS = 2

IOC_NRMASK = (1 << IOC_NRBITS) - 1 # 255
IOC_TYPEMASK = (1 << IOC_TYPEBITS) - 1 # 255
IOC_SIZEMASK = (1 << IOC_SIZEBITS) - 1 # 16 383
IOC_DIRMASK = (1 << IOC_DIRBITS) - 1 # 3

IOC_NRSHIFT = 0 # 0
IOC_TYPESHIFT = IOC_NRSHIFT + IOC_NRBITS # 8
IOC_SIZESHIFT = IOC_TYPESHIFT + IOC_TYPEBITS # 16
IOC_DIRSHIFT = IOC_SIZESHIFT + IOC_SIZEBITS # 30

IOC_NONE = 0
IOC_WRITE = 1
IOC_READ = 2

KCAS_IOCTL_MAGIC = 0xBA # 186

IOC_IN = IOC_WRITE << IOC_DIRSHIFT # 1 073 741 824
IOC_OUT = IOC_READ << IOC_DIRSHIFT # 2 147 483 648
IOC_INOUT = (IOC_WRITE | IOC_READ) << IOC_DIRSHIFT # 3 221 225 472
IOCSIZE_MASK = IOC_SIZEMASK << IOC_SIZESHIFT # 1 073 676 288
IOCSIZE_SHIFT = IOC_SIZESHIFT # 16


def IOC(dir, type, nr, size):
if dir > IOC_DIRMASK:
raise OverflowError(f"IO direction value {dir} exceeds {IOC_DIRMASK}")
dir <<= IOC_DIRSHIFT

if type > IOC_TYPEMASK:
raise OverflowError(f"IO type value {type} exceeds {IOC_TYPEMASK}")
type <<= IOC_TYPESHIFT

if nr > IOC_NRMASK:
raise OverflowError(f"IO command value {nr} exceeds {IOC_NRMASK}")
nr <<= IOC_NRSHIFT

if size > IOC_SIZEMASK:
raise OverflowError(f"IO size value {size} exceeds {IOC_SIZEMASK}")
size <<= IOC_SIZESHIFT

return dir | type | nr | size
Comment on lines +15 to +60
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are libs for that https://pypi.org/project/ioctl-opt/



def IOC_TYPECHECK(item):
return ctypes.sizeof(item)


def IO(nr):
return IOC(IOC_NONE, KCAS_IOCTL_MAGIC, nr, 0)


def IOR(nr, size):
return IOC(IOC_READ, KCAS_IOCTL_MAGIC, nr, IOC_TYPECHECK(size))


def IOW(nr, size):
return IOC(IOC_WRITE, KCAS_IOCTL_MAGIC, nr, IOC_TYPECHECK(size))


def IOWR(nr, size):
return IOC(IOC_READ | IOC_WRITE, KCAS_IOCTL_MAGIC, nr, IOC_TYPECHECK(size))


def IOR_BAD(nr, size):
return IOC(IOC_READ, KCAS_IOCTL_MAGIC, nr, ctypes.sizeof(size))


def IOW_BAD(nr, size):
return IOC(IOC_WRITE, KCAS_IOCTL_MAGIC, nr, ctypes.sizeof(size))


def IOWR_BAD(nr, size):
return IOC(IOC_READ | IOC_WRITE, KCAS_IOCTL_MAGIC, nr, ctypes.sizeof(size))


def IOC_DIR(nr):
return (nr >> IOC_DIRSHIFT) & IOC_DIRMASK


def IOC_TYPE(nr):
return (nr >> IOC_TYPESHIFT) & IOC_TYPEMASK


def IOC_NR(nr):
return (nr >> IOC_NRSHIFT) & IOC_NRMASK


def IOC_SIZE(nr):
return (nr >> IOC_SIZESHIFT) & IOC_SIZEMASK


temp_dir = '/tmp/cas'
struct_path = os.path.join(temp_dir, 'dump_file')
script_source = os.path.join(f'{os.path.dirname(__file__)}', 'send_ioctl_script.py')
script_dest = os.path.join(temp_dir, 'send_ioctl_script.py')
Comment on lines +111 to +114
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct_path and script_source and local (so os.path is ok), but script_dest is remote (so Linux - posixpath required)
both struct_path (local) and script_dest (remote) use the same temp_dir, which is posix-style



def send_script_with_dumped_args():
if not check_if_directory_exists(temp_dir):
create_directory(temp_dir, True)

TestRun.executor.rsync_to(script_source, script_dest)
chmod_numerical(script_dest, 550)

TestRun.executor.rsync_to(struct_path, struct_path)
chmod_numerical(struct_path, 440)
Comment on lines +117 to +125
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We finally have DUT-side scripting! ❤️
Although it may have been more generalized for use in other places (even as a part of TF).

Comment on lines +121 to +125
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rsync to be replaced



def cas_ioctl(cas_ioctl_request, interrupt: bool = False):
if not os.path.exists(temp_dir):
os.mkdir(temp_dir)

with open(struct_path, 'wb') as dump_file:
marshal.dump(cas_ioctl_request.command_struct, dump_file)

send_script_with_dumped_args()
if interrupt:
pid = TestRun.executor.run_in_background(
f"{script_dest} -c {cas_ioctl_request.command} -s {struct_path}"
)
sleep(2)
TestRun.executor.kill_process(pid)
Comment on lines +140 to +141
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should at least take timeout as an argument. Otherwise it's unusable with commands like cache flush, which typically takes much longer than 2 seconds.

else:
TestRun.executor.run(f"{script_dest} -c {cas_ioctl_request.command} -s {struct_path}")
if check_if_directory_exists(temp_dir):
remove(temp_dir, True, True, True)
if os.path.exists(struct_path):
os.remove(struct_path)
Comment on lines +144 to +147
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Virtually all ioctls handled by CAS return information from kernel via ioctl structure, which means we want to have a way to receive this information back from DUT once ioctl is finished.

38 changes: 38 additions & 0 deletions test/functional/api/cas/ioctl/send_ioctl_script.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
#
# Copyright(c) 2021 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#

import argparse
import marshal
import os
import sys
from fcntl import ioctl


def main():
CAS_DEVICE = '/dev/cas_ctrl'

parser = argparse.ArgumentParser(description=f'Send ioctl request to {CAS_DEVICE}')

parser.add_argument('-c', '--command', action='store', dest='command', type=str,
required=True, help=f"Specific request code to send to {CAS_DEVICE}")
parser.add_argument('-s', '--struct', action='store', dest='struct', type=str,
required=True, help="Structure of CAS request")
args = parser.parse_args()

with open(args.struct, 'rb') as struct_file:
struct = bytearray(marshal.load(struct_file))

fd = os.open(CAS_DEVICE, os.O_RDWR)
try:
ioctl(fd, int(args.command), struct)
except OSError as err:
print(f"IOCTL request returned error: {err}", sys.stdout)
finally:
os.close(fd)


if __name__ == '__main__':
main()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty

Empty file.
29 changes: 29 additions & 0 deletions test/functional/tests/ioctl/common_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
# Copyright(c) 2021 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#

from api.cas.cli_messages import __check_string_msg
from core.test_run import TestRun


interrupt_stop = [
r"Waiting for cache stop interrupted\. Stop will finish asynchronously\."
]

interrupt_start = [
r"Cache added successfully, but waiting interrupted\. Rollback"
]

load_and_force = [
r"cache\d+: Using \'force\' flag is forbidden for load operation\."
]
Comment on lines +10 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think those messages should be in this file. Maybe more appropriate place would be in api/cas/cli_messages.py?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or keep separate api/cas/ioctl/cli_messages.py



def clear_dmesg():
TestRun.executor.run_expect_success('dmesg -C')


def check_dmesg(searched_phrase: str):
dmesg_out = TestRun.executor.run_expect_success("dmesg").stdout
__check_string_msg(dmesg_out, searched_phrase)
Comment on lines +23 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those as well, I think clearing/checking dmesg is more generic operation, not limited only to ioctl tests.

Loading