]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | # |
2 | # Copyright(c) 2019 Intel Corporation | |
3 | # SPDX-License-Identifier: BSD-3-Clause-Clear | |
4 | # | |
5 | ||
6 | from ctypes import ( | |
7 | POINTER, | |
8 | c_void_p, | |
9 | c_uint32, | |
10 | c_char_p, | |
11 | create_string_buffer, | |
12 | memmove, | |
13 | memset, | |
14 | Structure, | |
15 | CFUNCTYPE, | |
16 | c_int, | |
17 | c_uint, | |
18 | c_uint64, | |
19 | sizeof, | |
20 | cast, | |
21 | string_at, | |
22 | ) | |
23 | from hashlib import md5 | |
24 | import weakref | |
25 | ||
26 | from .io import Io, IoOps, IoDir | |
27 | from .shared import OcfErrorCode, Uuid | |
28 | from ..ocf import OcfLib | |
29 | from ..utils import print_buffer, Size as S | |
30 | from .data import Data | |
31 | ||
32 | ||
33 | class VolumeCaps(Structure): | |
34 | _fields_ = [("_atomic_writes", c_uint32, 1)] | |
35 | ||
36 | ||
37 | class VolumeOps(Structure): | |
38 | SUBMIT_IO = CFUNCTYPE(None, POINTER(Io)) | |
39 | SUBMIT_FLUSH = CFUNCTYPE(None, c_void_p) | |
40 | SUBMIT_METADATA = CFUNCTYPE(None, c_void_p) | |
41 | SUBMIT_DISCARD = CFUNCTYPE(None, c_void_p) | |
42 | SUBMIT_WRITE_ZEROES = CFUNCTYPE(None, c_void_p) | |
43 | OPEN = CFUNCTYPE(c_int, c_void_p) | |
44 | CLOSE = CFUNCTYPE(None, c_void_p) | |
45 | GET_MAX_IO_SIZE = CFUNCTYPE(c_uint, c_void_p) | |
46 | GET_LENGTH = CFUNCTYPE(c_uint64, c_void_p) | |
47 | ||
48 | _fields_ = [ | |
49 | ("_submit_io", SUBMIT_IO), | |
50 | ("_submit_flush", SUBMIT_FLUSH), | |
51 | ("_submit_metadata", SUBMIT_METADATA), | |
52 | ("_submit_discard", SUBMIT_DISCARD), | |
53 | ("_submit_write_zeroes", SUBMIT_WRITE_ZEROES), | |
54 | ("_open", OPEN), | |
55 | ("_close", CLOSE), | |
56 | ("_get_max_io_size", GET_MAX_IO_SIZE), | |
57 | ("_get_length", GET_LENGTH), | |
58 | ] | |
59 | ||
60 | ||
61 | class VolumeProperties(Structure): | |
62 | _fields_ = [ | |
63 | ("_name", c_char_p), | |
64 | ("_io_priv_size", c_uint32), | |
65 | ("_volume_priv_size", c_uint32), | |
66 | ("_caps", VolumeCaps), | |
67 | ("_ops", VolumeOps), | |
68 | ("_io_ops", IoOps), | |
f67539c2 | 69 | ("_deinit", c_char_p), |
9f95a23c TL |
70 | ] |
71 | ||
72 | ||
73 | class VolumeIoPriv(Structure): | |
f67539c2 | 74 | _fields_ = [("_data", c_void_p), ("_offset", c_uint64)] |
9f95a23c TL |
75 | |
76 | ||
77 | class Volume(Structure): | |
f67539c2 TL |
78 | VOLUME_POISON = 0x13 |
79 | ||
9f95a23c TL |
80 | _fields_ = [("_storage", c_void_p)] |
81 | _instances_ = {} | |
82 | _uuid_ = {} | |
83 | ||
84 | props = None | |
85 | ||
86 | def __init__(self, size: S, uuid=None): | |
87 | super().__init__() | |
88 | self.size = size | |
89 | if uuid: | |
90 | if uuid in type(self)._uuid_: | |
91 | raise Exception( | |
92 | "Volume with uuid {} already created".format(uuid) | |
93 | ) | |
94 | self.uuid = uuid | |
95 | else: | |
96 | self.uuid = str(id(self)) | |
97 | ||
98 | type(self)._uuid_[self.uuid] = weakref.ref(self) | |
99 | ||
100 | self.data = create_string_buffer(int(self.size)) | |
f67539c2 | 101 | memset(self.data, self.VOLUME_POISON, self.size) |
9f95a23c TL |
102 | self._storage = cast(self.data, c_void_p) |
103 | ||
104 | self.reset_stats() | |
105 | self.opened = False | |
106 | ||
f67539c2 TL |
107 | def get_copy(self): |
108 | new_volume = Volume(self.size) | |
109 | memmove(new_volume.data, self.data, self.size) | |
110 | return new_volume | |
111 | ||
9f95a23c TL |
112 | @classmethod |
113 | def get_props(cls): | |
114 | if not cls.props: | |
115 | cls.props = VolumeProperties( | |
116 | _name=str(cls.__name__).encode("ascii"), | |
117 | _io_priv_size=sizeof(VolumeIoPriv), | |
118 | _volume_priv_size=0, | |
119 | _caps=VolumeCaps(_atomic_writes=0), | |
120 | _ops=VolumeOps( | |
121 | _submit_io=cls._submit_io, | |
122 | _submit_flush=cls._submit_flush, | |
123 | _submit_metadata=cls._submit_metadata, | |
124 | _submit_discard=cls._submit_discard, | |
125 | _submit_write_zeroes=cls._submit_write_zeroes, | |
126 | _open=cls._open, | |
127 | _close=cls._close, | |
128 | _get_max_io_size=cls._get_max_io_size, | |
129 | _get_length=cls._get_length, | |
130 | ), | |
131 | _io_ops=IoOps( | |
132 | _set_data=cls._io_set_data, _get_data=cls._io_get_data | |
133 | ), | |
f67539c2 | 134 | _deinit=0, |
9f95a23c TL |
135 | ) |
136 | ||
137 | return cls.props | |
138 | ||
139 | @classmethod | |
140 | def get_instance(cls, ref): | |
141 | instance = cls._instances_[ref]() | |
142 | if instance is None: | |
143 | print("tried to access {} but it's gone".format(ref)) | |
144 | ||
145 | return instance | |
146 | ||
147 | @classmethod | |
148 | def get_by_uuid(cls, uuid): | |
149 | return cls._uuid_[uuid]() | |
150 | ||
151 | @staticmethod | |
152 | @VolumeOps.SUBMIT_IO | |
153 | def _submit_io(io): | |
154 | io_structure = cast(io, POINTER(Io)) | |
f67539c2 TL |
155 | volume = Volume.get_instance( |
156 | OcfLib.getInstance().ocf_io_get_volume(io_structure) | |
157 | ) | |
9f95a23c TL |
158 | |
159 | volume.submit_io(io_structure) | |
160 | ||
161 | @staticmethod | |
162 | @VolumeOps.SUBMIT_FLUSH | |
163 | def _submit_flush(flush): | |
164 | io_structure = cast(flush, POINTER(Io)) | |
f67539c2 TL |
165 | volume = Volume.get_instance( |
166 | OcfLib.getInstance().ocf_io_get_volume(io_structure) | |
167 | ) | |
9f95a23c TL |
168 | |
169 | volume.submit_flush(io_structure) | |
170 | ||
171 | @staticmethod | |
172 | @VolumeOps.SUBMIT_METADATA | |
173 | def _submit_metadata(meta): | |
174 | pass | |
175 | ||
176 | @staticmethod | |
177 | @VolumeOps.SUBMIT_DISCARD | |
178 | def _submit_discard(discard): | |
179 | io_structure = cast(discard, POINTER(Io)) | |
f67539c2 TL |
180 | volume = Volume.get_instance( |
181 | OcfLib.getInstance().ocf_io_get_volume(io_structure) | |
182 | ) | |
9f95a23c TL |
183 | |
184 | volume.submit_discard(io_structure) | |
185 | ||
186 | @staticmethod | |
187 | @VolumeOps.SUBMIT_WRITE_ZEROES | |
188 | def _submit_write_zeroes(write_zeroes): | |
189 | pass | |
190 | ||
191 | @staticmethod | |
192 | @CFUNCTYPE(c_int, c_void_p) | |
193 | def _open(ref): | |
194 | uuid_ptr = cast( | |
195 | OcfLib.getInstance().ocf_volume_get_uuid(ref), POINTER(Uuid) | |
196 | ) | |
197 | uuid = str(uuid_ptr.contents._data, encoding="ascii") | |
198 | try: | |
199 | volume = Volume.get_by_uuid(uuid) | |
f67539c2 | 200 | except: # noqa E722 TODO:Investigate whether this really should be so broad |
9f95a23c TL |
201 | print("Tried to access unallocated volume {}".format(uuid)) |
202 | print("{}".format(Volume._uuid_)) | |
203 | return -1 | |
204 | ||
205 | if volume.opened: | |
206 | return OcfErrorCode.OCF_ERR_NOT_OPEN_EXC | |
207 | ||
208 | Volume._instances_[ref] = weakref.ref(volume) | |
209 | ||
210 | return volume.open() | |
211 | ||
212 | @staticmethod | |
213 | @VolumeOps.CLOSE | |
214 | def _close(ref): | |
215 | volume = Volume.get_instance(ref) | |
216 | volume.close() | |
217 | volume.opened = False | |
218 | ||
219 | @staticmethod | |
220 | @VolumeOps.GET_MAX_IO_SIZE | |
221 | def _get_max_io_size(ref): | |
222 | return Volume.get_instance(ref).get_max_io_size() | |
223 | ||
224 | @staticmethod | |
225 | @VolumeOps.GET_LENGTH | |
226 | def _get_length(ref): | |
227 | return Volume.get_instance(ref).get_length() | |
228 | ||
229 | @staticmethod | |
230 | @IoOps.SET_DATA | |
231 | def _io_set_data(io, data, offset): | |
232 | io_priv = cast( | |
233 | OcfLib.getInstance().ocf_io_get_priv(io), POINTER(VolumeIoPriv) | |
234 | ) | |
235 | data = Data.get_instance(data) | |
f67539c2 | 236 | io_priv.contents._offset = offset |
9f95a23c TL |
237 | io_priv.contents._data = data.handle |
238 | ||
239 | return 0 | |
240 | ||
241 | @staticmethod | |
242 | @IoOps.GET_DATA | |
243 | def _io_get_data(io): | |
244 | io_priv = cast( | |
245 | OcfLib.getInstance().ocf_io_get_priv(io), POINTER(VolumeIoPriv) | |
246 | ) | |
247 | return io_priv.contents._data | |
248 | ||
249 | def open(self): | |
250 | self.opened = True | |
251 | return 0 | |
252 | ||
253 | def close(self): | |
254 | pass | |
255 | ||
256 | def get_length(self): | |
257 | return self.size | |
258 | ||
259 | def get_max_io_size(self): | |
260 | return S.from_KiB(128) | |
261 | ||
262 | def submit_flush(self, flush): | |
263 | flush.contents._end(flush, 0) | |
264 | ||
265 | def submit_discard(self, discard): | |
266 | try: | |
267 | dst = self._storage + discard.contents._addr | |
f67539c2 | 268 | memset(dst, 0, discard.contents._bytes) |
9f95a23c TL |
269 | |
270 | discard.contents._end(discard, 0) | |
f67539c2 | 271 | except: # noqa E722 |
9f95a23c TL |
272 | discard.contents._end(discard, -5) |
273 | ||
274 | def get_stats(self): | |
275 | return self.stats | |
276 | ||
277 | def reset_stats(self): | |
278 | self.stats = {IoDir.WRITE: 0, IoDir.READ: 0} | |
279 | ||
280 | def submit_io(self, io): | |
281 | try: | |
282 | self.stats[IoDir(io.contents._dir)] += 1 | |
283 | ||
f67539c2 TL |
284 | io_priv = cast( |
285 | OcfLib.getInstance().ocf_io_get_priv(io), POINTER(VolumeIoPriv)) | |
286 | offset = io_priv.contents._offset | |
287 | ||
9f95a23c | 288 | if io.contents._dir == IoDir.WRITE: |
f67539c2 TL |
289 | src_ptr = cast(OcfLib.getInstance().ocf_io_get_data(io), c_void_p) |
290 | src = Data.get_instance(src_ptr.value).handle.value + offset | |
9f95a23c TL |
291 | dst = self._storage + io.contents._addr |
292 | elif io.contents._dir == IoDir.READ: | |
f67539c2 TL |
293 | dst_ptr = cast(OcfLib.getInstance().ocf_io_get_data(io), c_void_p) |
294 | dst = Data.get_instance(dst_ptr.value).handle.value + offset | |
9f95a23c TL |
295 | src = self._storage + io.contents._addr |
296 | ||
297 | memmove(dst, src, io.contents._bytes) | |
f67539c2 | 298 | io_priv.contents._offset += io.contents._bytes |
9f95a23c TL |
299 | |
300 | io.contents._end(io, 0) | |
f67539c2 | 301 | except: # noqa E722 |
9f95a23c TL |
302 | io.contents._end(io, -5) |
303 | ||
f67539c2 | 304 | def dump(self, offset=0, size=0, ignore=VOLUME_POISON, **kwargs): |
9f95a23c TL |
305 | if size == 0: |
306 | size = int(self.size) - int(offset) | |
f67539c2 | 307 | |
9f95a23c TL |
308 | print_buffer( |
309 | self._storage, | |
f67539c2 TL |
310 | size, |
311 | ignore=ignore, | |
312 | **kwargs | |
9f95a23c TL |
313 | ) |
314 | ||
315 | def md5(self): | |
316 | m = md5() | |
317 | m.update(string_at(self._storage, self.size)) | |
318 | return m.hexdigest() | |
319 | ||
320 | ||
321 | class ErrorDevice(Volume): | |
322 | def __init__(self, size, error_sectors: set = None, uuid=None): | |
323 | super().__init__(size, uuid) | |
324 | self.error_sectors = error_sectors or set() | |
325 | ||
326 | def set_mapping(self, error_sectors: set): | |
327 | self.error_sectors = error_sectors | |
328 | ||
329 | def submit_io(self, io): | |
330 | if io.contents._addr in self.error_sectors: | |
331 | io.contents._end(io, -5) | |
332 | self.stats["errors"][io.contents._dir] += 1 | |
333 | else: | |
334 | super().submit_io(io) | |
335 | ||
336 | def reset_stats(self): | |
337 | super().reset_stats() | |
338 | self.stats["errors"] = {IoDir.WRITE: 0, IoDir.READ: 0} | |
339 | ||
340 | ||
f67539c2 TL |
341 | class TraceDevice(Volume): |
342 | def __init__(self, size, trace_fcn=None, uuid=None): | |
343 | super().__init__(size, uuid) | |
344 | self.trace_fcn = trace_fcn | |
345 | ||
346 | def submit_io(self, io): | |
347 | submit = True | |
348 | ||
349 | if self.trace_fcn: | |
350 | submit = self.trace_fcn(self, io) | |
351 | ||
352 | if submit: | |
353 | super().submit_io(io) | |
354 | ||
355 | ||
9f95a23c TL |
356 | lib = OcfLib.getInstance() |
357 | lib.ocf_io_get_priv.restype = POINTER(VolumeIoPriv) | |
f67539c2 TL |
358 | lib.ocf_io_get_volume.argtypes = [c_void_p] |
359 | lib.ocf_io_get_volume.restype = c_void_p | |
360 | lib.ocf_io_get_data.argtypes = [c_void_p] | |
361 | lib.ocf_io_get_data.restype = c_void_p |