| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2016 CNEX Labs |
|---|
| 3 | 4 | * Initial release: Javier Gonzalez <javier@cnexlabs.com> |
|---|
| .. | .. |
|---|
| 16 | 17 | */ |
|---|
| 17 | 18 | |
|---|
| 18 | 19 | #include "pblk.h" |
|---|
| 20 | +#include "pblk-trace.h" |
|---|
| 19 | 21 | #include <linux/delay.h> |
|---|
| 22 | + |
|---|
| 20 | 23 | |
|---|
| 21 | 24 | static void pblk_gc_free_gc_rq(struct pblk_gc_rq *gc_rq) |
|---|
| 22 | 25 | { |
|---|
| .. | .. |
|---|
| 56 | 59 | wake_up_process(gc->gc_writer_ts); |
|---|
| 57 | 60 | } |
|---|
| 58 | 61 | |
|---|
| 59 | | -static void pblk_put_line_back(struct pblk *pblk, struct pblk_line *line) |
|---|
| 62 | +void pblk_put_line_back(struct pblk *pblk, struct pblk_line *line) |
|---|
| 60 | 63 | { |
|---|
| 61 | 64 | struct pblk_line_mgmt *l_mg = &pblk->l_mg; |
|---|
| 62 | 65 | struct list_head *move_list; |
|---|
| 63 | 66 | |
|---|
| 67 | + spin_lock(&l_mg->gc_lock); |
|---|
| 64 | 68 | spin_lock(&line->lock); |
|---|
| 65 | 69 | WARN_ON(line->state != PBLK_LINESTATE_GC); |
|---|
| 66 | 70 | line->state = PBLK_LINESTATE_CLOSED; |
|---|
| 71 | + trace_pblk_line_state(pblk_disk_name(pblk), line->id, |
|---|
| 72 | + line->state); |
|---|
| 73 | + |
|---|
| 74 | + /* We need to reset gc_group in order to ensure that |
|---|
| 75 | + * pblk_line_gc_list will return proper move_list |
|---|
| 76 | + * since right now current line is not on any of the |
|---|
| 77 | + * gc lists. |
|---|
| 78 | + */ |
|---|
| 79 | + line->gc_group = PBLK_LINEGC_NONE; |
|---|
| 67 | 80 | move_list = pblk_line_gc_list(pblk, line); |
|---|
| 68 | 81 | spin_unlock(&line->lock); |
|---|
| 69 | | - |
|---|
| 70 | | - if (move_list) { |
|---|
| 71 | | - spin_lock(&l_mg->gc_lock); |
|---|
| 72 | | - list_add_tail(&line->list, move_list); |
|---|
| 73 | | - spin_unlock(&l_mg->gc_lock); |
|---|
| 74 | | - } |
|---|
| 82 | + list_add_tail(&line->list, move_list); |
|---|
| 83 | + spin_unlock(&l_mg->gc_lock); |
|---|
| 75 | 84 | } |
|---|
| 76 | 85 | |
|---|
| 77 | 86 | static void pblk_gc_line_ws(struct work_struct *work) |
|---|
| .. | .. |
|---|
| 79 | 88 | struct pblk_line_ws *gc_rq_ws = container_of(work, |
|---|
| 80 | 89 | struct pblk_line_ws, ws); |
|---|
| 81 | 90 | struct pblk *pblk = gc_rq_ws->pblk; |
|---|
| 82 | | - struct nvm_tgt_dev *dev = pblk->dev; |
|---|
| 83 | | - struct nvm_geo *geo = &dev->geo; |
|---|
| 84 | 91 | struct pblk_gc *gc = &pblk->gc; |
|---|
| 85 | 92 | struct pblk_line *line = gc_rq_ws->line; |
|---|
| 86 | 93 | struct pblk_gc_rq *gc_rq = gc_rq_ws->priv; |
|---|
| .. | .. |
|---|
| 88 | 95 | |
|---|
| 89 | 96 | up(&gc->gc_sem); |
|---|
| 90 | 97 | |
|---|
| 91 | | - gc_rq->data = vmalloc(array_size(gc_rq->nr_secs, geo->csecs)); |
|---|
| 92 | | - if (!gc_rq->data) { |
|---|
| 93 | | - pblk_err(pblk, "could not GC line:%d (%d/%d)\n", |
|---|
| 94 | | - line->id, *line->vsc, gc_rq->nr_secs); |
|---|
| 95 | | - goto out; |
|---|
| 96 | | - } |
|---|
| 97 | | - |
|---|
| 98 | 98 | /* Read from GC victim block */ |
|---|
| 99 | 99 | ret = pblk_submit_read_gc(pblk, gc_rq); |
|---|
| 100 | 100 | if (ret) { |
|---|
| 101 | | - pblk_err(pblk, "failed GC read in line:%d (err:%d)\n", |
|---|
| 102 | | - line->id, ret); |
|---|
| 101 | + line->w_err_gc->has_gc_err = 1; |
|---|
| 103 | 102 | goto out; |
|---|
| 104 | 103 | } |
|---|
| 105 | 104 | |
|---|
| .. | .. |
|---|
| 133 | 132 | struct pblk_line *line) |
|---|
| 134 | 133 | { |
|---|
| 135 | 134 | struct line_emeta *emeta_buf; |
|---|
| 136 | | - struct pblk_line_mgmt *l_mg = &pblk->l_mg; |
|---|
| 137 | 135 | struct pblk_line_meta *lm = &pblk->lm; |
|---|
| 138 | 136 | unsigned int lba_list_size = lm->emeta_len[2]; |
|---|
| 139 | 137 | __le64 *lba_list; |
|---|
| 140 | 138 | int ret; |
|---|
| 141 | 139 | |
|---|
| 142 | | - emeta_buf = pblk_malloc(lm->emeta_len[0], |
|---|
| 143 | | - l_mg->emeta_alloc_type, GFP_KERNEL); |
|---|
| 140 | + emeta_buf = kvmalloc(lm->emeta_len[0], GFP_KERNEL); |
|---|
| 144 | 141 | if (!emeta_buf) |
|---|
| 145 | 142 | return NULL; |
|---|
| 146 | 143 | |
|---|
| 147 | | - ret = pblk_line_read_emeta(pblk, line, emeta_buf); |
|---|
| 144 | + ret = pblk_line_emeta_read(pblk, line, emeta_buf); |
|---|
| 148 | 145 | if (ret) { |
|---|
| 149 | 146 | pblk_err(pblk, "line %d read emeta failed (%d)\n", |
|---|
| 150 | 147 | line->id, ret); |
|---|
| 151 | | - pblk_mfree(emeta_buf, l_mg->emeta_alloc_type); |
|---|
| 148 | + kvfree(emeta_buf); |
|---|
| 152 | 149 | return NULL; |
|---|
| 153 | 150 | } |
|---|
| 154 | 151 | |
|---|
| .. | .. |
|---|
| 162 | 159 | if (ret) { |
|---|
| 163 | 160 | pblk_err(pblk, "inconsistent emeta (line %d)\n", |
|---|
| 164 | 161 | line->id); |
|---|
| 165 | | - pblk_mfree(emeta_buf, l_mg->emeta_alloc_type); |
|---|
| 162 | + kvfree(emeta_buf); |
|---|
| 166 | 163 | return NULL; |
|---|
| 167 | 164 | } |
|---|
| 168 | 165 | |
|---|
| 169 | | - lba_list = pblk_malloc(lba_list_size, |
|---|
| 170 | | - l_mg->emeta_alloc_type, GFP_KERNEL); |
|---|
| 166 | + lba_list = kvmalloc(lba_list_size, GFP_KERNEL); |
|---|
| 167 | + |
|---|
| 171 | 168 | if (lba_list) |
|---|
| 172 | 169 | memcpy(lba_list, emeta_to_lbas(pblk, emeta_buf), lba_list_size); |
|---|
| 173 | 170 | |
|---|
| 174 | | - pblk_mfree(emeta_buf, l_mg->emeta_alloc_type); |
|---|
| 171 | + kvfree(emeta_buf); |
|---|
| 175 | 172 | |
|---|
| 176 | 173 | return lba_list; |
|---|
| 177 | 174 | } |
|---|
| .. | .. |
|---|
| 182 | 179 | ws); |
|---|
| 183 | 180 | struct pblk *pblk = line_ws->pblk; |
|---|
| 184 | 181 | struct pblk_line *line = line_ws->line; |
|---|
| 185 | | - struct pblk_line_mgmt *l_mg = &pblk->l_mg; |
|---|
| 186 | 182 | struct pblk_line_meta *lm = &pblk->lm; |
|---|
| 183 | + struct nvm_tgt_dev *dev = pblk->dev; |
|---|
| 184 | + struct nvm_geo *geo = &dev->geo; |
|---|
| 187 | 185 | struct pblk_gc *gc = &pblk->gc; |
|---|
| 188 | 186 | struct pblk_line_ws *gc_rq_ws; |
|---|
| 189 | 187 | struct pblk_gc_rq *gc_rq; |
|---|
| .. | .. |
|---|
| 242 | 240 | gc_rq->nr_secs = nr_secs; |
|---|
| 243 | 241 | gc_rq->line = line; |
|---|
| 244 | 242 | |
|---|
| 243 | + gc_rq->data = vmalloc(array_size(gc_rq->nr_secs, geo->csecs)); |
|---|
| 244 | + if (!gc_rq->data) |
|---|
| 245 | + goto fail_free_gc_rq; |
|---|
| 246 | + |
|---|
| 245 | 247 | gc_rq_ws = kmalloc(sizeof(struct pblk_line_ws), GFP_KERNEL); |
|---|
| 246 | 248 | if (!gc_rq_ws) |
|---|
| 247 | | - goto fail_free_gc_rq; |
|---|
| 249 | + goto fail_free_gc_data; |
|---|
| 248 | 250 | |
|---|
| 249 | 251 | gc_rq_ws->pblk = pblk; |
|---|
| 250 | 252 | gc_rq_ws->line = line; |
|---|
| .. | .. |
|---|
| 267 | 269 | goto next_rq; |
|---|
| 268 | 270 | |
|---|
| 269 | 271 | out: |
|---|
| 270 | | - pblk_mfree(lba_list, l_mg->emeta_alloc_type); |
|---|
| 272 | + kvfree(lba_list); |
|---|
| 271 | 273 | kfree(line_ws); |
|---|
| 272 | 274 | kfree(invalid_bitmap); |
|---|
| 273 | 275 | |
|---|
| .. | .. |
|---|
| 276 | 278 | |
|---|
| 277 | 279 | return; |
|---|
| 278 | 280 | |
|---|
| 281 | +fail_free_gc_data: |
|---|
| 282 | + vfree(gc_rq->data); |
|---|
| 279 | 283 | fail_free_gc_rq: |
|---|
| 280 | 284 | kfree(gc_rq); |
|---|
| 281 | 285 | fail_free_lba_list: |
|---|
| 282 | | - pblk_mfree(lba_list, l_mg->emeta_alloc_type); |
|---|
| 286 | + kvfree(lba_list); |
|---|
| 283 | 287 | fail_free_invalid_bitmap: |
|---|
| 284 | 288 | kfree(invalid_bitmap); |
|---|
| 285 | 289 | fail_free_ws: |
|---|
| 286 | 290 | kfree(line_ws); |
|---|
| 287 | 291 | |
|---|
| 292 | + /* Line goes back to closed state, so we cannot release additional |
|---|
| 293 | + * reference for line, since we do that only when we want to do |
|---|
| 294 | + * gc to free line state transition. |
|---|
| 295 | + */ |
|---|
| 288 | 296 | pblk_put_line_back(pblk, line); |
|---|
| 289 | | - kref_put(&line->ref, pblk_line_put); |
|---|
| 290 | 297 | atomic_dec(&gc->read_inflight_gc); |
|---|
| 291 | 298 | |
|---|
| 292 | 299 | pblk_err(pblk, "failed to GC line %d\n", line->id); |
|---|
| .. | .. |
|---|
| 350 | 357 | |
|---|
| 351 | 358 | pblk_gc_kick(pblk); |
|---|
| 352 | 359 | |
|---|
| 353 | | - if (pblk_gc_line(pblk, line)) |
|---|
| 360 | + if (pblk_gc_line(pblk, line)) { |
|---|
| 354 | 361 | pblk_err(pblk, "failed to GC line %d\n", line->id); |
|---|
| 362 | + /* rollback */ |
|---|
| 363 | + spin_lock(&gc->r_lock); |
|---|
| 364 | + list_add_tail(&line->list, &gc->r_list); |
|---|
| 365 | + spin_unlock(&gc->r_lock); |
|---|
| 366 | + } |
|---|
| 355 | 367 | |
|---|
| 356 | 368 | return 0; |
|---|
| 357 | 369 | } |
|---|
| .. | .. |
|---|
| 360 | 372 | struct list_head *group_list) |
|---|
| 361 | 373 | { |
|---|
| 362 | 374 | struct pblk_line *line, *victim; |
|---|
| 363 | | - int line_vsc, victim_vsc; |
|---|
| 375 | + unsigned int line_vsc = ~0x0L, victim_vsc = ~0x0L; |
|---|
| 364 | 376 | |
|---|
| 365 | 377 | victim = list_first_entry(group_list, struct pblk_line, list); |
|---|
| 378 | + |
|---|
| 366 | 379 | list_for_each_entry(line, group_list, list) { |
|---|
| 367 | | - line_vsc = le32_to_cpu(*line->vsc); |
|---|
| 368 | | - victim_vsc = le32_to_cpu(*victim->vsc); |
|---|
| 369 | | - if (line_vsc < victim_vsc) |
|---|
| 380 | + if (!atomic_read(&line->sec_to_update)) |
|---|
| 381 | + line_vsc = le32_to_cpu(*line->vsc); |
|---|
| 382 | + if (line_vsc < victim_vsc) { |
|---|
| 370 | 383 | victim = line; |
|---|
| 384 | + victim_vsc = le32_to_cpu(*victim->vsc); |
|---|
| 385 | + } |
|---|
| 371 | 386 | } |
|---|
| 387 | + |
|---|
| 388 | + if (victim_vsc == ~0x0) |
|---|
| 389 | + return NULL; |
|---|
| 372 | 390 | |
|---|
| 373 | 391 | return victim; |
|---|
| 374 | 392 | } |
|---|
| .. | .. |
|---|
| 405 | 423 | spin_lock(&line->lock); |
|---|
| 406 | 424 | WARN_ON(line->state != PBLK_LINESTATE_CLOSED); |
|---|
| 407 | 425 | line->state = PBLK_LINESTATE_GC; |
|---|
| 426 | + trace_pblk_line_state(pblk_disk_name(pblk), line->id, |
|---|
| 427 | + line->state); |
|---|
| 408 | 428 | spin_unlock(&line->lock); |
|---|
| 409 | 429 | |
|---|
| 410 | 430 | list_del(&line->list); |
|---|
| .. | .. |
|---|
| 441 | 461 | |
|---|
| 442 | 462 | do { |
|---|
| 443 | 463 | spin_lock(&l_mg->gc_lock); |
|---|
| 444 | | - if (list_empty(group_list)) { |
|---|
| 464 | + |
|---|
| 465 | + line = pblk_gc_get_victim_line(pblk, group_list); |
|---|
| 466 | + if (!line) { |
|---|
| 445 | 467 | spin_unlock(&l_mg->gc_lock); |
|---|
| 446 | 468 | break; |
|---|
| 447 | 469 | } |
|---|
| 448 | 470 | |
|---|
| 449 | | - line = pblk_gc_get_victim_line(pblk, group_list); |
|---|
| 450 | | - |
|---|
| 451 | 471 | spin_lock(&line->lock); |
|---|
| 452 | 472 | WARN_ON(line->state != PBLK_LINESTATE_CLOSED); |
|---|
| 453 | 473 | line->state = PBLK_LINESTATE_GC; |
|---|
| 474 | + trace_pblk_line_state(pblk_disk_name(pblk), line->id, |
|---|
| 475 | + line->state); |
|---|
| 454 | 476 | spin_unlock(&line->lock); |
|---|
| 455 | 477 | |
|---|
| 456 | 478 | list_del(&line->list); |
|---|