Line data Source code
1 : /* SPDX-License-Identifier: BSD-3-Clause
2 : * Copyright (C) 2018 Intel Corporation.
3 : * All rights reserved.
4 : */
5 :
6 : #include "ftl_band.h"
7 : #include "ftl_core.h"
8 : #include "ftl_debug.h"
9 : #include "ftl_io.h"
10 : #include "ftl_internal.h"
11 : #include "spdk/ftl.h"
12 : #include "spdk/likely.h"
13 :
14 : struct ftl_reloc;
15 : struct ftl_band_reloc;
16 :
17 : /* TODO: Should probably change the move naming nomenclature to something more descriptive */
18 : enum ftl_reloc_move_state {
19 : FTL_RELOC_STATE_READ = 0,
20 : FTL_RELOC_STATE_PIN,
21 : FTL_RELOC_STATE_WRITE,
22 : FTL_RELOC_STATE_WAIT,
23 : FTL_RELOC_STATE_HALT,
24 :
25 : FTL_RELOC_STATE_MAX
26 : };
27 :
28 : struct ftl_reloc_move {
29 : /* FTL device */
30 : struct spdk_ftl_dev *dev;
31 :
32 : struct ftl_reloc *reloc;
33 :
34 : /* Request for doing IO */
35 : struct ftl_rq *rq;
36 :
37 : /* Move state (read, write) */
38 : enum ftl_reloc_move_state state;
39 :
40 : /* Entry of circular list */
41 : TAILQ_ENTRY(ftl_reloc_move) qentry;
42 : };
43 :
44 : struct ftl_reloc {
45 : /* Device associated with relocate */
46 : struct spdk_ftl_dev *dev;
47 :
48 : /* Indicates relocate is about to halt */
49 : bool halt;
50 :
51 : /* Band which are read to relocate */
52 : struct ftl_band *band;
53 :
54 : /* Bands already read, but waiting for finishing GC */
55 : TAILQ_HEAD(, ftl_band) band_done;
56 : size_t band_done_count;
57 :
58 : /* Flags indicating reloc is waiting for a new band */
59 : bool band_waiting;
60 :
61 : /* Maximum number of IOs per band */
62 : size_t max_qdepth;
63 :
64 : /* Queue of free move objects */
65 : struct ftl_reloc_move *move_buffer;
66 :
67 : /* Array of movers queue for each state */
68 : TAILQ_HEAD(, ftl_reloc_move) move_queue[FTL_RELOC_STATE_MAX];
69 :
70 : };
71 :
72 : static void move_read_cb(struct ftl_rq *rq);
73 : static void move_write_cb(struct ftl_rq *rq);
74 : static void move_set_state(struct ftl_reloc_move *mv, enum ftl_reloc_move_state state);
75 : static void move_write(struct ftl_reloc *reloc, struct ftl_reloc_move *mv);
76 : static void move_read_error_cb(struct ftl_rq *rq, struct ftl_band *band, uint64_t idx,
77 : uint64_t count);
78 :
79 : static void
80 0 : move_deinit(struct ftl_reloc_move *mv)
81 : {
82 0 : assert(mv);
83 0 : ftl_rq_del(mv->rq);
84 0 : }
85 :
86 : static int
87 0 : move_init(struct ftl_reloc *reloc, struct ftl_reloc_move *mv)
88 : {
89 0 : mv->state = FTL_RELOC_STATE_HALT;
90 0 : TAILQ_INSERT_TAIL(&reloc->move_queue[FTL_RELOC_STATE_HALT], mv, qentry);
91 :
92 0 : mv->reloc = reloc;
93 0 : mv->dev = reloc->dev;
94 0 : mv->rq = ftl_rq_new(mv->dev, mv->dev->md_size);
95 :
96 0 : if (!mv->rq) {
97 0 : return -ENOMEM;
98 : }
99 0 : mv->rq->owner.priv = mv;
100 :
101 0 : return 0;
102 : }
103 :
104 : struct ftl_reloc *
105 0 : ftl_reloc_init(struct spdk_ftl_dev *dev)
106 : {
107 : struct ftl_reloc *reloc;
108 : struct ftl_reloc_move *move;
109 : size_t i, count;
110 :
111 0 : reloc = calloc(1, sizeof(*reloc));
112 0 : if (!reloc) {
113 0 : return NULL;
114 : }
115 :
116 0 : reloc->dev = dev;
117 0 : reloc->halt = true;
118 0 : reloc->max_qdepth = dev->sb->max_reloc_qdepth;
119 :
120 0 : reloc->move_buffer = calloc(reloc->max_qdepth, sizeof(*reloc->move_buffer));
121 0 : if (!reloc->move_buffer) {
122 0 : FTL_ERRLOG(dev, "Failed to initialize reloc moves pool");
123 0 : goto error;
124 : }
125 :
126 : /* Initialize movers queues */
127 0 : count = SPDK_COUNTOF(reloc->move_queue);
128 0 : for (i = 0; i < count; ++i) {
129 0 : TAILQ_INIT(&reloc->move_queue[i]);
130 : }
131 :
132 0 : for (i = 0; i < reloc->max_qdepth; ++i) {
133 0 : move = &reloc->move_buffer[i];
134 :
135 0 : if (move_init(reloc, move)) {
136 0 : goto error;
137 : }
138 : }
139 :
140 0 : TAILQ_INIT(&reloc->band_done);
141 :
142 0 : return reloc;
143 0 : error:
144 0 : ftl_reloc_free(reloc);
145 0 : return NULL;
146 : }
147 :
148 : struct ftl_reloc_task_fini {
149 : struct ftl_reloc_task *task;
150 : spdk_msg_fn cb;
151 : void *cb_arg;
152 : };
153 :
154 : void
155 0 : ftl_reloc_free(struct ftl_reloc *reloc)
156 : {
157 : size_t i;
158 :
159 0 : if (!reloc) {
160 0 : return;
161 : }
162 :
163 0 : if (reloc->move_buffer) {
164 0 : for (i = 0; i < reloc->max_qdepth; ++i) {
165 0 : move_deinit(&reloc->move_buffer[i]);
166 : }
167 : }
168 :
169 0 : free(reloc->move_buffer);
170 0 : free(reloc);
171 : }
172 :
173 : void
174 0 : ftl_reloc_halt(struct ftl_reloc *reloc)
175 : {
176 0 : struct spdk_ftl_dev *dev = reloc->dev;
177 :
178 0 : if (dev->conf.prep_upgrade_on_shutdown && 0 == dev->num_free) {
179 : /*
180 : * In shutdown upgrade procedure, it is required to have
181 : * at least one free band. Keep reloc running to reclaim
182 : * the band.
183 : */
184 0 : return;
185 : }
186 :
187 0 : reloc->halt = true;
188 : }
189 :
190 : void
191 0 : ftl_reloc_resume(struct ftl_reloc *reloc)
192 : {
193 : struct ftl_reloc_move *mv, *next;
194 0 : reloc->halt = false;
195 :
196 0 : TAILQ_FOREACH_SAFE(mv, &reloc->move_queue[FTL_RELOC_STATE_HALT], qentry,
197 : next) {
198 0 : move_set_state(mv, FTL_RELOC_STATE_READ);
199 : }
200 0 : }
201 :
202 : static void
203 0 : move_set_state(struct ftl_reloc_move *mv, enum ftl_reloc_move_state state)
204 : {
205 0 : struct ftl_reloc *reloc = mv->reloc;
206 :
207 0 : switch (state) {
208 0 : case FTL_RELOC_STATE_READ:
209 0 : mv->rq->owner.cb = move_read_cb;
210 0 : mv->rq->owner.error = move_read_error_cb;
211 0 : mv->rq->iter.idx = 0;
212 0 : mv->rq->iter.count = 0;
213 0 : mv->rq->success = true;
214 0 : break;
215 :
216 0 : case FTL_RELOC_STATE_WRITE:
217 0 : mv->rq->owner.cb = move_write_cb;
218 0 : mv->rq->owner.error = NULL;
219 0 : break;
220 :
221 0 : case FTL_RELOC_STATE_PIN:
222 : case FTL_RELOC_STATE_WAIT:
223 : case FTL_RELOC_STATE_HALT:
224 0 : break;
225 :
226 0 : default:
227 0 : ftl_abort();
228 : break;
229 : }
230 :
231 0 : if (mv->state != state) {
232 : /* Remove the mover from previous queue */
233 0 : TAILQ_REMOVE(&reloc->move_queue[mv->state], mv, qentry);
234 : /* Insert the mover to the new queue */
235 0 : TAILQ_INSERT_TAIL(&reloc->move_queue[state], mv, qentry);
236 : /* Update state */
237 0 : mv->state = state;
238 : }
239 0 : }
240 :
241 : static void
242 0 : move_get_band_cb(struct ftl_band *band, void *cntx, bool status)
243 : {
244 0 : struct ftl_reloc *reloc = cntx;
245 :
246 0 : if (spdk_likely(status)) {
247 0 : reloc->band = band;
248 0 : ftl_band_iter_init(band);
249 : }
250 0 : reloc->band_waiting = false;
251 0 : }
252 :
253 : static void
254 0 : move_grab_new_band(struct ftl_reloc *reloc)
255 : {
256 0 : if (!reloc->band_waiting) {
257 0 : if (!ftl_needs_reloc(reloc->dev)) {
258 0 : return;
259 : }
260 :
261 : /* Limit number of simultaneously relocated bands */
262 0 : if (reloc->band_done_count > 2) {
263 0 : return;
264 : }
265 :
266 0 : reloc->band_waiting = true;
267 0 : ftl_band_get_next_gc(reloc->dev, move_get_band_cb, reloc);
268 : }
269 : }
270 :
271 : static struct ftl_band *
272 0 : move_get_band(struct ftl_reloc *reloc)
273 : {
274 0 : struct ftl_band *band = reloc->band;
275 :
276 0 : if (!band) {
277 0 : move_grab_new_band(reloc);
278 0 : return NULL;
279 : }
280 :
281 0 : if (!ftl_band_filled(band, band->md->iter.offset)) {
282 : /* Band still not read, we can continue reading */
283 0 : return band;
284 : }
285 :
286 0 : TAILQ_INSERT_TAIL(&reloc->band_done, band, queue_entry);
287 0 : reloc->band_done_count++;
288 0 : reloc->band = NULL;
289 :
290 0 : return NULL;
291 : }
292 :
293 : static void
294 0 : move_advance_rq(struct ftl_rq *rq)
295 : {
296 0 : struct ftl_band *band = rq->io.band;
297 : uint64_t offset, i;
298 0 : struct ftl_rq_entry *entry = &rq->entries[rq->iter.idx];
299 :
300 0 : assert(rq->iter.idx + rq->iter.count <= rq->num_blocks);
301 :
302 0 : for (i = 0; i < rq->iter.count; i++) {
303 0 : offset = ftl_band_block_offset_from_addr(band, rq->io.addr);
304 :
305 0 : assert(offset < ftl_get_num_blocks_in_band(band->dev));
306 0 : assert(ftl_band_block_offset_valid(band, offset));
307 :
308 0 : entry->lba = band->p2l_map.band_map[offset].lba;
309 0 : entry->addr = rq->io.addr;
310 0 : entry->owner.priv = band;
311 0 : entry->seq_id = band->p2l_map.band_map[offset].seq_id;
312 :
313 0 : entry++;
314 0 : rq->io.addr = ftl_band_next_addr(band, rq->io.addr, 1);
315 0 : band->owner.cnt++;
316 : }
317 :
318 : /* Increase QD for the request */
319 0 : rq->iter.qd++;
320 :
321 : /* Advanced request iterator */
322 0 : rq->iter.idx += rq->iter.count;
323 0 : }
324 :
325 : static void
326 0 : move_init_entries(struct ftl_rq *rq, uint64_t idx, uint64_t count)
327 : {
328 0 : uint64_t i = 0;
329 0 : struct ftl_rq_entry *iter = &rq->entries[idx];
330 :
331 0 : assert(idx + count <= rq->num_blocks);
332 :
333 0 : i = 0;
334 0 : while (i < count) {
335 0 : iter->addr = FTL_ADDR_INVALID;
336 0 : iter->owner.priv = NULL;
337 0 : iter->lba = FTL_LBA_INVALID;
338 0 : iter->seq_id = 0;
339 0 : iter++;
340 0 : i++;
341 : }
342 0 : }
343 :
344 : static void
345 0 : move_read_error_cb(struct ftl_rq *rq, struct ftl_band *band, uint64_t idx, uint64_t count)
346 : {
347 0 : move_init_entries(rq, idx, count);
348 0 : band->owner.cnt -= count;
349 0 : }
350 :
351 : static void
352 0 : move_read_cb(struct ftl_rq *rq)
353 : {
354 0 : struct ftl_reloc_move *mv = rq->owner.priv;
355 :
356 : /* Decrease QD of the request */
357 0 : assert(rq->iter.qd > 0);
358 0 : rq->iter.qd--;
359 :
360 0 : if (rq->iter.idx != rq->num_blocks || rq->iter.qd) {
361 0 : return;
362 : }
363 :
364 0 : move_set_state(mv, FTL_RELOC_STATE_PIN);
365 : }
366 :
367 : static void
368 0 : move_rq_pad(struct ftl_rq *rq, struct ftl_band *band)
369 : {
370 0 : struct ftl_rq_entry *entry = &rq->entries[rq->iter.idx];
371 :
372 0 : for (; rq->iter.idx < rq->num_blocks; ++rq->iter.idx) {
373 0 : entry->addr = rq->io.addr;
374 0 : entry->owner.priv = band;
375 0 : entry->lba = FTL_LBA_INVALID;
376 0 : entry->seq_id = 0;
377 0 : entry++;
378 0 : rq->io.addr = ftl_band_next_addr(band, rq->io.addr, 1);
379 0 : band->owner.cnt++;
380 : }
381 :
382 0 : assert(rq->iter.idx == rq->num_blocks);
383 0 : }
384 :
385 : static void
386 0 : move_read(struct ftl_reloc *reloc, struct ftl_reloc_move *mv, struct ftl_band *band)
387 : {
388 0 : struct ftl_rq *rq = mv->rq;
389 0 : uint64_t blocks = ftl_get_num_blocks_in_band(band->dev);
390 0 : uint64_t pos = band->md->iter.offset;
391 0 : uint64_t begin = ftl_bitmap_find_first_set(band->p2l_map.valid, pos, UINT64_MAX);
392 : uint64_t end, band_left, rq_left;
393 :
394 0 : if (spdk_likely(begin < blocks)) {
395 0 : if (begin > pos) {
396 0 : ftl_band_iter_advance(band, begin - pos);
397 0 : } else if (begin == pos) {
398 : /* Valid block at the position of iterator */
399 : } else {
400 : /* Inconsistent state */
401 0 : ftl_abort();
402 : }
403 0 : } else if (UINT64_MAX == begin) {
404 : /* No more valid LBAs in the band */
405 0 : band_left = ftl_band_user_blocks_left(band, pos);
406 0 : ftl_band_iter_advance(band, band_left);
407 :
408 0 : assert(ftl_band_filled(band, band->md->iter.offset));
409 :
410 0 : if (rq->iter.idx) {
411 0 : move_rq_pad(rq, band);
412 0 : move_set_state(mv, FTL_RELOC_STATE_WAIT);
413 0 : rq->iter.qd++;
414 0 : rq->owner.cb(rq);
415 : }
416 :
417 0 : return;
418 : } else {
419 : /* Inconsistent state */
420 0 : ftl_abort();
421 : }
422 :
423 0 : rq_left = rq->num_blocks - rq->iter.idx;
424 0 : assert(rq_left > 0);
425 :
426 : /* Find next clear bit, but no further than max request count */
427 0 : end = ftl_bitmap_find_first_clear(band->p2l_map.valid, begin + 1, begin + rq_left);
428 0 : if (end != UINT64_MAX) {
429 0 : rq_left = end - begin;
430 : }
431 :
432 0 : band_left = ftl_band_user_blocks_left(band, band->md->iter.offset);
433 0 : rq->iter.count = spdk_min(rq_left, band_left);
434 :
435 0 : ftl_band_rq_read(band, rq);
436 :
437 0 : move_advance_rq(rq);
438 :
439 : /* Advance band iterator */
440 0 : ftl_band_iter_advance(band, rq->iter.count);
441 :
442 : /* If band is fully written pad rest of request */
443 0 : if (ftl_band_filled(band, band->md->iter.offset)) {
444 0 : move_rq_pad(rq, band);
445 : }
446 :
447 0 : if (rq->iter.idx == rq->num_blocks) {
448 : /*
449 : * All request entries scheduled for reading,
450 : * We can change state to waiting
451 : */
452 0 : move_set_state(mv, FTL_RELOC_STATE_WAIT);
453 : }
454 : }
455 :
456 : static void
457 0 : move_pin_cb(struct spdk_ftl_dev *dev, int status, struct ftl_l2p_pin_ctx *pin_ctx)
458 : {
459 0 : struct ftl_reloc_move *mv = pin_ctx->cb_ctx;
460 0 : struct ftl_rq *rq = mv->rq;
461 :
462 0 : if (status) {
463 0 : rq->iter.status = status;
464 0 : pin_ctx->lba = FTL_LBA_INVALID;
465 : }
466 :
467 0 : if (--rq->iter.remaining == 0) {
468 0 : if (rq->iter.status) {
469 : /* unpin and try again */
470 0 : ftl_rq_unpin(rq);
471 0 : move_set_state(mv, FTL_RELOC_STATE_PIN);
472 0 : return;
473 : }
474 :
475 0 : move_set_state(mv, FTL_RELOC_STATE_WRITE);
476 : }
477 : }
478 :
479 : static void
480 0 : move_pin(struct ftl_reloc_move *mv)
481 : {
482 0 : struct ftl_rq *rq = mv->rq;
483 0 : struct ftl_rq_entry *entry = rq->entries;
484 : uint64_t i;
485 :
486 0 : move_set_state(mv, FTL_RELOC_STATE_WAIT);
487 :
488 0 : rq->iter.remaining = rq->iter.count = rq->num_blocks;
489 0 : rq->iter.status = 0;
490 :
491 0 : for (i = 0; i < rq->num_blocks; i++) {
492 0 : if (entry->lba != FTL_LBA_INVALID) {
493 0 : ftl_l2p_pin(rq->dev, entry->lba, 1, move_pin_cb, mv, &entry->l2p_pin_ctx);
494 : } else {
495 0 : ftl_l2p_pin_skip(rq->dev, move_pin_cb, mv, &entry->l2p_pin_ctx);
496 : }
497 0 : entry++;
498 : }
499 0 : }
500 :
501 : static void
502 0 : move_finish_write(struct ftl_rq *rq)
503 : {
504 : uint64_t i;
505 0 : struct spdk_ftl_dev *dev = rq->dev;
506 0 : struct ftl_rq_entry *iter = rq->entries;
507 0 : ftl_addr addr = rq->io.addr;
508 0 : struct ftl_band *rq_band = rq->io.band;
509 : struct ftl_band *band;
510 :
511 0 : for (i = 0; i < rq->num_blocks; ++i, ++iter) {
512 0 : band = iter->owner.priv;
513 :
514 0 : if (band) {
515 0 : assert(band->owner.cnt > 0);
516 0 : band->owner.cnt--;
517 : }
518 0 : if (iter->lba != FTL_LBA_INVALID) {
519 : /* Update L2P table */
520 0 : ftl_l2p_update_base(dev, iter->lba, addr, iter->addr);
521 0 : ftl_l2p_unpin(dev, iter->lba, 1);
522 : }
523 0 : addr = ftl_band_next_addr(rq_band, addr, 1);
524 : }
525 0 : }
526 :
527 : static void
528 0 : move_write_cb(struct ftl_rq *rq)
529 : {
530 0 : struct ftl_reloc_move *mv = rq->owner.priv;
531 :
532 0 : assert(rq->iter.qd == 1);
533 0 : rq->iter.qd--;
534 :
535 0 : if (spdk_likely(rq->success)) {
536 0 : move_finish_write(rq);
537 0 : move_set_state(mv, FTL_RELOC_STATE_READ);
538 : } else {
539 : /* Write failed, repeat write */
540 0 : move_set_state(mv, FTL_RELOC_STATE_WRITE);
541 : }
542 0 : }
543 :
544 : static void
545 0 : move_write(struct ftl_reloc *reloc, struct ftl_reloc_move *mv)
546 : {
547 0 : struct spdk_ftl_dev *dev = mv->dev;
548 0 : struct ftl_rq *rq = mv->rq;
549 :
550 0 : assert(rq->iter.idx == rq->num_blocks);
551 :
552 : /* Request contains data to be placed on a new location, submit it */
553 0 : ftl_writer_queue_rq(&dev->writer_gc, rq);
554 0 : rq->iter.qd++;
555 :
556 0 : move_set_state(mv, FTL_RELOC_STATE_WAIT);
557 0 : }
558 :
559 : static void
560 0 : move_run(struct ftl_reloc *reloc, struct ftl_reloc_move *mv)
561 : {
562 : struct ftl_band *band;
563 :
564 0 : switch (mv->state) {
565 0 : case FTL_RELOC_STATE_READ: {
566 0 : if (spdk_unlikely(reloc->halt)) {
567 0 : move_set_state(mv, FTL_RELOC_STATE_HALT);
568 0 : break;
569 : }
570 :
571 0 : band = move_get_band(reloc);
572 0 : if (!band) {
573 0 : break;
574 : }
575 :
576 0 : move_read(reloc, mv, band);
577 : }
578 0 : break;
579 :
580 0 : case FTL_RELOC_STATE_PIN:
581 0 : move_pin(mv);
582 0 : ftl_add_io_activity(reloc->dev);
583 0 : break;
584 :
585 0 : case FTL_RELOC_STATE_WRITE:
586 0 : if (spdk_unlikely(reloc->halt)) {
587 0 : ftl_rq_unpin(mv->rq);
588 0 : move_set_state(mv, FTL_RELOC_STATE_HALT);
589 0 : break;
590 : }
591 :
592 0 : ftl_add_io_activity(reloc->dev);
593 0 : move_write(reloc, mv);
594 0 : break;
595 :
596 0 : case FTL_RELOC_STATE_HALT:
597 : case FTL_RELOC_STATE_WAIT:
598 0 : break;
599 :
600 0 : default:
601 0 : assert(0);
602 : ftl_abort();
603 : break;
604 : }
605 0 : }
606 :
607 : static void
608 0 : move_handle_band_error(struct ftl_band *band)
609 : {
610 0 : struct ftl_reloc *reloc = band->dev->reloc;
611 : /*
612 : * Handle band error, it's because an error occurred during reading,
613 : * Add band to the close band list, will try reloc it in a moment
614 : */
615 0 : TAILQ_REMOVE(&reloc->band_done, band, queue_entry);
616 0 : reloc->band_done_count--;
617 :
618 0 : band->md->state = FTL_BAND_STATE_CLOSING;
619 0 : ftl_band_set_state(band, FTL_BAND_STATE_CLOSED);
620 0 : }
621 :
622 : static void
623 0 : move_release_bands(struct ftl_reloc *reloc)
624 : {
625 : struct ftl_band *band;
626 :
627 0 : if (TAILQ_EMPTY(&reloc->band_done)) {
628 0 : return;
629 : }
630 :
631 0 : band = TAILQ_FIRST(&reloc->band_done);
632 :
633 0 : if (band->owner.cnt || ftl_band_qd(band)) {
634 : /* Band still in use */
635 0 : return;
636 : }
637 :
638 0 : if (ftl_band_empty(band)) {
639 0 : assert(ftl_band_filled(band, band->md->iter.offset));
640 0 : TAILQ_REMOVE(&reloc->band_done, band, queue_entry);
641 0 : reloc->band_done_count--;
642 0 : ftl_band_free(band);
643 : } else {
644 0 : move_handle_band_error(band);
645 : }
646 : }
647 :
648 : bool
649 0 : ftl_reloc_is_halted(const struct ftl_reloc *reloc)
650 : {
651 : size_t i, count;
652 :
653 0 : count = SPDK_COUNTOF(reloc->move_queue);
654 0 : for (i = 0; i < count; ++i) {
655 0 : if (i == FTL_RELOC_STATE_HALT) {
656 0 : continue;
657 : }
658 :
659 0 : if (!TAILQ_EMPTY(&reloc->move_queue[i])) {
660 0 : return false;
661 : }
662 : }
663 :
664 0 : return true;
665 : }
666 :
667 : void
668 0 : ftl_reloc(struct ftl_reloc *reloc)
669 : {
670 : size_t i, count;
671 :
672 0 : count = SPDK_COUNTOF(reloc->move_queue);
673 0 : for (i = 0; i < count; ++i) {
674 0 : if (TAILQ_EMPTY(&reloc->move_queue[i])) {
675 0 : continue;
676 : }
677 :
678 0 : move_run(reloc, TAILQ_FIRST(&reloc->move_queue[i]));
679 : }
680 :
681 0 : move_release_bands(reloc);
682 0 : }
|