1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2018 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include <ocf/ocf.h> 7 #include <execinfo.h> 8 9 #include "spdk/env.h" 10 #include "spdk/log.h" 11 12 #include "ctx.h" 13 #include "data.h" 14 15 ocf_ctx_t vbdev_ocf_ctx; 16 17 static ctx_data_t * 18 vbdev_ocf_ctx_data_alloc(uint32_t pages) 19 { 20 struct bdev_ocf_data *data; 21 void *buf; 22 uint32_t sz; 23 24 data = vbdev_ocf_data_alloc(1); 25 26 sz = pages * PAGE_SIZE; 27 buf = spdk_malloc(sz, PAGE_SIZE, NULL, 28 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 29 if (buf == NULL) { 30 return NULL; 31 } 32 33 vbdev_ocf_iovs_add(data, buf, sz); 34 35 data->size = sz; 36 37 return data; 38 } 39 40 static void 41 vbdev_ocf_ctx_data_free(ctx_data_t *ctx_data) 42 { 43 struct bdev_ocf_data *data = ctx_data; 44 int i; 45 46 if (!data) { 47 return; 48 } 49 50 for (i = 0; i < data->iovcnt; i++) { 51 spdk_free(data->iovs[i].iov_base); 52 } 53 54 vbdev_ocf_data_free(data); 55 } 56 57 static int 58 vbdev_ocf_ctx_data_mlock(ctx_data_t *ctx_data) 59 { 60 /* TODO [mlock]: add mlock option */ 61 return 0; 62 } 63 64 static void 65 vbdev_ocf_ctx_data_munlock(ctx_data_t *ctx_data) 66 { 67 /* TODO [mlock]: add mlock option */ 68 } 69 70 static size_t 71 iovec_flatten(struct iovec *iov, size_t iovcnt, void *buf, size_t size, size_t offset) 72 { 73 size_t i, len, done = 0; 74 75 for (i = 0; i < iovcnt; i++) { 76 if (offset >= iov[i].iov_len) { 77 offset -= iov[i].iov_len; 78 continue; 79 } 80 81 if (iov[i].iov_base == NULL) { 82 continue; 83 } 84 85 if (done >= size) { 86 break; 87 } 88 89 len = MIN(size - done, iov[i].iov_len - offset); 90 memcpy(buf, iov[i].iov_base + offset, len); 91 buf += len; 92 done += len; 93 offset = 0; 94 } 95 96 return done; 97 } 98 99 static uint32_t 100 vbdev_ocf_ctx_data_rd(void *dst, ctx_data_t *src, uint32_t size) 101 { 102 struct bdev_ocf_data *s = src; 103 uint32_t size_local; 104 105 size_local = iovec_flatten(s->iovs, s->iovcnt, dst, size, s->seek); 106 s->seek += size_local; 107 108 return size_local; 109 } 110 111 static size_t 112 buf_to_iovec(const void *buf, size_t size, struct iovec *iov, size_t iovcnt, size_t offset) 113 { 114 size_t i, len, done = 0; 115 116 for (i = 0; i < iovcnt; i++) { 117 if (offset >= iov[i].iov_len) { 118 offset -= iov[i].iov_len; 119 continue; 120 } 121 122 if (iov[i].iov_base == NULL) { 123 continue; 124 } 125 126 if (done >= size) { 127 break; 128 } 129 130 len = MIN(size - done, iov[i].iov_len - offset); 131 memcpy(iov[i].iov_base + offset, buf, len); 132 buf += len; 133 done += len; 134 offset = 0; 135 } 136 137 return done; 138 } 139 140 static uint32_t 141 vbdev_ocf_ctx_data_wr(ctx_data_t *dst, const void *src, uint32_t size) 142 { 143 struct bdev_ocf_data *d = dst; 144 uint32_t size_local; 145 146 size_local = buf_to_iovec(src, size, d->iovs, d->iovcnt, d->seek); 147 d->seek += size_local; 148 149 return size_local; 150 } 151 152 static size_t 153 iovset(struct iovec *iov, size_t iovcnt, int byte, size_t size, size_t offset) 154 { 155 size_t i, len, done = 0; 156 157 for (i = 0; i < iovcnt; i++) { 158 if (offset >= iov[i].iov_len) { 159 offset -= iov[i].iov_len; 160 continue; 161 } 162 163 if (iov[i].iov_base == NULL) { 164 continue; 165 } 166 167 if (done >= size) { 168 break; 169 } 170 171 len = MIN(size - done, iov[i].iov_len - offset); 172 memset(iov[i].iov_base + offset, byte, len); 173 done += len; 174 offset = 0; 175 } 176 177 return done; 178 } 179 180 static uint32_t 181 vbdev_ocf_ctx_data_zero(ctx_data_t *dst, uint32_t size) 182 { 183 struct bdev_ocf_data *d = dst; 184 uint32_t size_local; 185 186 size_local = iovset(d->iovs, d->iovcnt, 0, size, d->seek); 187 d->seek += size_local; 188 189 return size_local; 190 } 191 192 static uint32_t 193 vbdev_ocf_ctx_data_seek(ctx_data_t *dst, ctx_data_seek_t seek, uint32_t offset) 194 { 195 struct bdev_ocf_data *d = dst; 196 uint32_t off = 0; 197 198 switch (seek) { 199 case ctx_data_seek_begin: 200 off = MIN(offset, d->size); 201 d->seek = off; 202 break; 203 case ctx_data_seek_current: 204 off = MIN(offset, d->size - d->seek); 205 d->seek += off; 206 break; 207 } 208 209 return off; 210 } 211 212 static uint64_t 213 vbdev_ocf_ctx_data_cpy(ctx_data_t *dst, ctx_data_t *src, uint64_t to, 214 uint64_t from, uint64_t bytes) 215 { 216 struct bdev_ocf_data *s = src; 217 struct bdev_ocf_data *d = dst; 218 uint32_t it_iov = 0; 219 uint32_t it_off = 0; 220 uint32_t n, sz; 221 222 bytes = MIN(bytes, s->size - from); 223 bytes = MIN(bytes, d->size - to); 224 sz = bytes; 225 226 while (from || bytes) { 227 if (s->iovs[it_iov].iov_len == it_off) { 228 it_iov++; 229 it_off = 0; 230 continue; 231 } 232 233 if (from) { 234 n = MIN(from, s->iovs[it_iov].iov_len); 235 from -= n; 236 } else { 237 n = MIN(bytes, s->iovs[it_iov].iov_len); 238 buf_to_iovec(s->iovs[it_iov].iov_base + it_off, n, d->iovs, d->iovcnt, to); 239 bytes -= n; 240 to += n; 241 } 242 243 it_off += n; 244 } 245 246 return sz; 247 } 248 249 static void 250 vbdev_ocf_ctx_data_secure_erase(ctx_data_t *ctx_data) 251 { 252 struct bdev_ocf_data *data = ctx_data; 253 struct iovec *iovs = data->iovs; 254 int i; 255 256 for (i = 0; i < data->iovcnt; i++) { 257 if (env_memset(iovs[i].iov_base, iovs[i].iov_len, 0)) { 258 assert(false); 259 } 260 } 261 } 262 263 int 264 vbdev_ocf_queue_create(ocf_cache_t cache, ocf_queue_t *queue, const struct ocf_queue_ops *ops) 265 { 266 int rc; 267 struct vbdev_ocf_cache_ctx *ctx = ocf_cache_get_priv(cache); 268 269 pthread_mutex_lock(&ctx->lock); 270 rc = ocf_queue_create(cache, queue, ops); 271 pthread_mutex_unlock(&ctx->lock); 272 return rc; 273 } 274 275 void 276 vbdev_ocf_queue_put(ocf_queue_t queue) 277 { 278 ocf_cache_t cache = ocf_queue_get_cache(queue); 279 struct vbdev_ocf_cache_ctx *ctx = ocf_cache_get_priv(cache); 280 281 pthread_mutex_lock(&ctx->lock); 282 ocf_queue_put(queue); 283 pthread_mutex_unlock(&ctx->lock); 284 } 285 286 void 287 vbdev_ocf_cache_ctx_put(struct vbdev_ocf_cache_ctx *ctx) 288 { 289 if (env_atomic_dec_return(&ctx->refcnt) == 0) { 290 pthread_mutex_destroy(&ctx->lock); 291 free(ctx); 292 } 293 } 294 295 void 296 vbdev_ocf_cache_ctx_get(struct vbdev_ocf_cache_ctx *ctx) 297 { 298 env_atomic_inc(&ctx->refcnt); 299 } 300 301 struct cleaner_priv { 302 struct spdk_poller *poller; 303 ocf_queue_t mngt_queue; 304 uint64_t next_run; 305 }; 306 307 static int 308 cleaner_poll(void *arg) 309 { 310 ocf_cleaner_t cleaner = arg; 311 struct cleaner_priv *priv = ocf_cleaner_get_priv(cleaner); 312 313 if (spdk_get_ticks() >= priv->next_run) { 314 ocf_cleaner_run(cleaner, priv->mngt_queue); 315 return SPDK_POLLER_BUSY; 316 } 317 318 return SPDK_POLLER_IDLE; 319 } 320 321 static void 322 cleaner_cmpl(ocf_cleaner_t c, uint32_t interval) 323 { 324 struct cleaner_priv *priv = ocf_cleaner_get_priv(c); 325 326 priv->next_run = spdk_get_ticks() + ((interval * spdk_get_ticks_hz()) / 1000); 327 } 328 329 static int 330 vbdev_ocf_ctx_cleaner_init(ocf_cleaner_t c) 331 { 332 struct cleaner_priv *priv = calloc(1, sizeof(*priv)); 333 ocf_cache_t cache = ocf_cleaner_get_cache(c); 334 struct vbdev_ocf_cache_ctx *cctx = ocf_cache_get_priv(cache); 335 336 if (priv == NULL) { 337 return -ENOMEM; 338 } 339 340 priv->mngt_queue = cctx->mngt_queue; 341 342 ocf_cleaner_set_cmpl(c, cleaner_cmpl); 343 ocf_cleaner_set_priv(c, priv); 344 345 return 0; 346 } 347 348 static void 349 vbdev_ocf_ctx_cleaner_stop(ocf_cleaner_t c) 350 { 351 struct cleaner_priv *priv = ocf_cleaner_get_priv(c); 352 353 if (priv) { 354 spdk_poller_unregister(&priv->poller); 355 free(priv); 356 } 357 } 358 359 static void 360 vbdev_ocf_ctx_cleaner_kick(ocf_cleaner_t cleaner) 361 { 362 struct cleaner_priv *priv = ocf_cleaner_get_priv(cleaner); 363 364 if (priv->poller) { 365 return; 366 } 367 368 /* We start cleaner poller at the same thread where cache was created 369 * TODO: allow user to specify core at which cleaner should run */ 370 priv->poller = SPDK_POLLER_REGISTER(cleaner_poll, cleaner, 0); 371 } 372 373 /* This function is main way by which OCF communicates with user 374 * We don't want to use SPDK_LOG here because debugging information that is 375 * associated with every print message is not helpful in callback that only prints info 376 * while the real source is somewhere in OCF code */ 377 static int 378 vbdev_ocf_ctx_log_printf(ocf_logger_t logger, ocf_logger_lvl_t lvl, 379 const char *fmt, va_list args) 380 { 381 int spdk_lvl; 382 383 switch (lvl) { 384 case log_emerg: 385 case log_alert: 386 case log_crit: 387 case log_err: 388 spdk_lvl = SPDK_LOG_ERROR; 389 break; 390 391 case log_warn: 392 spdk_lvl = SPDK_LOG_WARN; 393 break; 394 395 case log_notice: 396 spdk_lvl = SPDK_LOG_NOTICE; 397 break; 398 399 case log_info: 400 case log_debug: 401 default: 402 spdk_lvl = SPDK_LOG_INFO; 403 } 404 405 spdk_vlog(spdk_lvl, NULL, -1, NULL, fmt, args); 406 return 0; 407 } 408 409 static const struct ocf_ctx_config vbdev_ocf_ctx_cfg = { 410 .name = "OCF SPDK", 411 412 .ops = { 413 .data = { 414 .alloc = vbdev_ocf_ctx_data_alloc, 415 .free = vbdev_ocf_ctx_data_free, 416 .mlock = vbdev_ocf_ctx_data_mlock, 417 .munlock = vbdev_ocf_ctx_data_munlock, 418 .read = vbdev_ocf_ctx_data_rd, 419 .write = vbdev_ocf_ctx_data_wr, 420 .zero = vbdev_ocf_ctx_data_zero, 421 .seek = vbdev_ocf_ctx_data_seek, 422 .copy = vbdev_ocf_ctx_data_cpy, 423 .secure_erase = vbdev_ocf_ctx_data_secure_erase, 424 }, 425 426 .cleaner = { 427 .init = vbdev_ocf_ctx_cleaner_init, 428 .stop = vbdev_ocf_ctx_cleaner_stop, 429 .kick = vbdev_ocf_ctx_cleaner_kick, 430 }, 431 432 .logger = { 433 .print = vbdev_ocf_ctx_log_printf, 434 .dump_stack = NULL, 435 }, 436 437 }, 438 }; 439 440 int 441 vbdev_ocf_ctx_init(void) 442 { 443 int ret; 444 445 ret = ocf_ctx_create(&vbdev_ocf_ctx, &vbdev_ocf_ctx_cfg); 446 if (ret < 0) { 447 return ret; 448 } 449 450 return 0; 451 } 452 453 void 454 vbdev_ocf_ctx_cleanup(void) 455 { 456 ocf_ctx_put(vbdev_ocf_ctx); 457 vbdev_ocf_ctx = NULL; 458 } 459