Skip to content

Commit

Permalink
adding API and tests for IOCTL requests (and bypassing casadm)
Browse files Browse the repository at this point in the history
Signed-off-by: Karolina Rogowska <karolina.rogowska@intel.com>
  • Loading branch information
karolinavelkaja committed Aug 12, 2021
1 parent 5afc8af commit 6bcd126
Show file tree
Hide file tree
Showing 9 changed files with 592 additions and 0 deletions.
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
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


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


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')


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)


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)
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)
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()
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\."
]


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)
Loading

0 comments on commit 6bcd126

Please sign in to comment.