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