| /* |
| * Copyright (c) 2014-2020 Douglas Gilbert. |
| * All rights reserved. |
| * Use of this source code is governed by a BSD-style |
| * license that can be found in the BSD_LICENSE file. |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| * |
| * Version 1.02 [20201124] |
| */ |
| |
| // C headers |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <limits.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #define __STDC_FORMAT_MACROS 1 |
| #include <inttypes.h> |
| |
| // C++ headers |
| #include <array> |
| |
| #include "sg_scat_gath.h" |
| #include "sg_lib.h" |
| #include "sg_pr2serr.h" |
| |
| using namespace std; |
| |
| #define MAX_SGL_NUM_VAL (INT32_MAX - 1) /* should reduce for testing */ |
| // #define MAX_SGL_NUM_VAL 7 /* should reduce for testing */ |
| #if MAX_SGL_NUM_VAL > INT32_MAX |
| #error "MAX_SGL_NUM_VAL cannot exceed 2^31 - 1" |
| #endif |
| |
| bool |
| scat_gath_list::empty() const |
| { |
| return sgl.empty(); |
| } |
| |
| bool |
| scat_gath_list::empty_or_00() const |
| { |
| if (sgl.empty()) |
| return true; |
| return ((sgl.size() == 1) && (sgl[0].lba == 0) && (sgl[0].num == 0)); |
| } |
| |
| int |
| scat_gath_list::num_elems() const |
| { |
| return sgl.size(); |
| } |
| |
| |
| /* Read numbers (up to 64 bits in size) from command line (comma (or |
| * (single) space **) separated list). Assumed decimal unless prefixed |
| * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex). |
| * Returns 0 if ok, or 1 if error. Assumed to be LBA (64 bit) and |
| * number_of_block (32 bit) pairs. ** Space on command line needs to |
| * be escaped, otherwise it is an operand/option separator. */ |
| bool |
| scat_gath_list::load_from_cli(const char * cl_p, bool b_vb) |
| { |
| bool split, full_pair; |
| int in_len, k, j; |
| const int max_nbs = MAX_SGL_NUM_VAL; |
| int64_t ll, large_num; |
| uint64_t prev_lba; |
| char * cp; |
| char * c2p; |
| const char * lcp; |
| class scat_gath_elem sge; |
| |
| if (NULL == cl_p) { |
| pr2serr("%s: bad arguments\n", __func__); |
| goto err_out; |
| } |
| lcp = cl_p; |
| in_len = strlen(cl_p); |
| if ('-' == cl_p[0]) { /* read from stdin */ |
| pr2serr("%s: logic error: no stdin here\n", __func__); |
| goto err_out; |
| } else { /* list of numbers (default decimal) on command line */ |
| k = strspn(cl_p, "0123456789aAbBcCdDeEfFhHxXiIkKmMgGtTpP, "); |
| if (in_len != k) { |
| if (b_vb) |
| pr2serr("%s: error at pos %d\n", __func__, k + 1); |
| goto err_out; |
| } |
| j = 0; |
| full_pair = true; |
| for (k = 0, split = false; ; ++k) { |
| if (split) { |
| /* splitting given elem with large number_of_blocks into |
| * multiple elems within array being built */ |
| ++j; |
| sge.lba = prev_lba + (uint64_t)max_nbs; |
| if (large_num > max_nbs) { |
| sge.num = (uint32_t)max_nbs; |
| prev_lba = sge.lba; |
| large_num -= max_nbs; |
| sgl.push_back(sge); |
| } else { |
| sge.num = (uint32_t)large_num; |
| split = false; |
| if (b_vb) |
| pr2serr("%s: split large sg elem into %d element%s\n", |
| __func__, j, (j == 1 ? "" : "s")); |
| sgl.push_back(sge); |
| goto check_for_next; |
| } |
| continue; |
| } |
| full_pair = false; |
| ll = sg_get_llnum(lcp); |
| if (-1 != ll) { |
| sge.lba = (uint64_t)ll; |
| cp = (char *)strchr(lcp, ','); |
| c2p = (char *)strchr(lcp, ' '); |
| if (NULL == cp) { |
| cp = c2p; |
| if (NULL == cp) |
| break; |
| } |
| if (c2p && (c2p < cp)) |
| cp = c2p; |
| lcp = cp + 1; |
| } else { |
| if (b_vb) |
| pr2serr("%s: error at pos %d\n", __func__, |
| (int)(lcp - cl_p + 1)); |
| goto err_out; |
| } |
| ll = sg_get_llnum(lcp); |
| if (ll >= 0) { |
| full_pair = true; |
| if (ll > max_nbs) { |
| sge.num = (uint32_t)max_nbs; |
| prev_lba = sge.lba; |
| large_num = ll - max_nbs; |
| split = true; |
| j = 1; |
| continue; |
| } |
| sge.num = (uint32_t)ll; |
| } else { /* bad or negative number as number_of_blocks */ |
| if (b_vb) |
| pr2serr("%s: bad number at pos %d\n", __func__, |
| (int)(lcp - cl_p + 1)); |
| goto err_out; |
| } |
| sgl.push_back(sge); |
| check_for_next: |
| cp = (char *)strchr(lcp, ','); |
| c2p = (char *)strchr(lcp, ' '); |
| if (NULL == cp) { |
| cp = c2p; |
| if (NULL == cp) |
| break; |
| } |
| if (c2p && (c2p < cp)) |
| cp = c2p; |
| lcp = cp + 1; |
| } /* end of for loop over items in operand */ |
| /* other than first pair, expect even number of items */ |
| if ((k > 0) && (! full_pair)) { |
| if (b_vb) |
| pr2serr("%s: expected even number of items: " |
| "LBA0,NUM0,LBA1,NUM1...\n", __func__); |
| goto err_out; |
| } |
| } |
| return true; |
| err_out: |
| if (0 == m_errno) |
| m_errno = SG_LIB_SYNTAX_ERROR; |
| return false; |
| } |
| |
| bool |
| scat_gath_list::file2sgl_helper(FILE * fp, const char * fnp, bool def_hex, |
| bool flexible, bool b_vb) |
| { |
| bool bit0; |
| bool pre_addr1 = true; |
| bool pre_hex_seen = false; |
| int in_len, k, j, m, ind; |
| const int max_nbs = MAX_SGL_NUM_VAL; |
| int off = 0; |
| int64_t ll; |
| uint64_t ull, prev_lba; |
| char * lcp; |
| class scat_gath_elem sge; |
| char line[1024]; |
| |
| for (j = 0 ; ; ++j) { |
| if (NULL == fgets(line, sizeof(line), fp)) |
| break; |
| // could improve with carry_over logic if sizeof(line) too small |
| in_len = strlen(line); |
| if (in_len > 0) { |
| if ('\n' == line[in_len - 1]) { |
| --in_len; |
| line[in_len] = '\0'; |
| } else { |
| m_errno = SG_LIB_SYNTAX_ERROR; |
| if (b_vb) |
| pr2serr("%s: %s: line too long, max %d bytes\n", |
| __func__, fnp, (int)(sizeof(line) - 1)); |
| goto err_out; |
| } |
| } |
| if (in_len < 1) |
| continue; |
| lcp = line; |
| m = strspn(lcp, " \t"); |
| if (m == in_len) |
| continue; |
| lcp += m; |
| in_len -= m; |
| if ('#' == *lcp) |
| continue; |
| if (pre_addr1 || pre_hex_seen) { |
| /* Accept lines with leading 'HEX' and ignore as long as there |
| * is one _before_ any LBA,NUM lines in the file. This allows |
| * HEX marked sgls to be concaternated together. */ |
| if (('H' == toupper(lcp[0])) && ('E' == toupper(lcp[1])) && |
| ('X' == toupper(lcp[2]))) { |
| pre_hex_seen = true; |
| if (def_hex) |
| continue; /* bypass 'HEX' marker line if expecting hex */ |
| else { |
| if (flexible) { |
| def_hex = true; /* okay, switch to hex parse */ |
| continue; |
| } else { |
| pr2serr("%s: %s: 'hex' string detected on line %d, " |
| "expecting decimal\n", __func__, fnp, j + 1); |
| m_errno = EINVAL; |
| goto err_out; |
| } |
| } |
| } |
| } |
| k = strspn(lcp, "0123456789aAbBcCdDeEfFhHxXbBdDiIkKmMgGtTpP, \t"); |
| if ((k < in_len) && ('#' != lcp[k])) { |
| m_errno = EINVAL; |
| if (b_vb) |
| pr2serr("%s: %s: syntax error at line %d, pos %d\n", |
| __func__, fnp, j + 1, m + k + 1); |
| goto err_out; |
| } |
| for (k = 0; k < 256; ++k) { |
| /* limit parseable items on one line to 256 */ |
| if (def_hex) { /* don't accept negatives or multipliers */ |
| if (1 == sscanf(lcp, "%" SCNx64, &ull)) |
| ll = (int64_t)ull; |
| else |
| ll = -1; /* use (2**64 - 1) as error flag */ |
| } else |
| ll = sg_get_llnum(lcp); |
| if (-1 != ll) { |
| ind = ((off + k) >> 1); |
| bit0 = !! (0x1 & (off + k)); |
| if (ind >= SG_SGL_MAX_ELEMENTS) { |
| m_errno = EINVAL; |
| if (b_vb) |
| pr2serr("%s: %s: array length exceeded\n", __func__, |
| fnp); |
| goto err_out; |
| } |
| if (bit0) { /* bit0 set when decoding a NUM */ |
| if (ll < 0) { |
| m_errno = EINVAL; |
| if (b_vb) |
| pr2serr("%s: %s: bad number in line %d, at pos " |
| "%d\n", __func__, fnp, j + 1, |
| (int)(lcp - line + 1)); |
| goto err_out; |
| } |
| if (ll > max_nbs) { |
| int h = 1; |
| |
| /* split up this elem into multiple, smaller elems */ |
| do { |
| sge.num = (uint32_t)max_nbs; |
| prev_lba = sge.lba; |
| sgl.push_back(sge); |
| sge.lba = prev_lba + (uint64_t)max_nbs; |
| ++h; |
| off += 2; |
| ll -= max_nbs; |
| } while (ll > max_nbs); |
| if (b_vb) |
| pr2serr("%s: split large sg elem into %d " |
| "elements\n", __func__, h); |
| } |
| sge.num = (uint32_t)ll; |
| sgl.push_back(sge); |
| } else { /* bit0 clear when decoding a LBA */ |
| if (pre_addr1) |
| pre_addr1 = false; |
| sge.lba = (uint64_t)ll; |
| } |
| } else { /* failed to decode number on line */ |
| if ('#' == *lcp) { /* numbers before #, rest of line comment */ |
| --k; |
| break; /* goes to next line */ |
| } |
| m_errno = EINVAL; |
| if (b_vb) |
| pr2serr("%s: %s: error in line %d, at pos %d\n", |
| __func__, fnp, j + 1, (int)(lcp - line + 1)); |
| goto err_out; |
| } |
| lcp = strpbrk(lcp, " ,\t#"); |
| if ((NULL == lcp) || ('#' == *lcp)) |
| break; |
| lcp += strspn(lcp, " ,\t"); |
| if ('\0' == *lcp) |
| break; |
| } /* <<< end of for(k < 256) loop */ |
| off += (k + 1); |
| } /* <<< end of for loop, one iteration per line */ |
| /* allow one items, but not higher odd number of items */ |
| if ((off > 1) && (0x1 & off)) { |
| m_errno = EINVAL; |
| if (b_vb) |
| pr2serr("%s: %s: expect even number of items: " |
| "LBA0,NUM0,LBA1,NUM1...\n", __func__, fnp); |
| goto err_out; |
| } |
| clearerr(fp); /* even EOF on first pass needs this before rescan */ |
| return true; |
| err_out: |
| clearerr(fp); |
| return false; |
| } |
| |
| /* Read numbers from filename (or stdin), line by line (comma (or (single) |
| * space) separated list); places starting_LBA,number_of_block pairs in an |
| * array of scat_gath_elem elements pointed to by the returned value. If |
| * this fails NULL is returned and an error number is written to errp (if it |
| * is non-NULL). Assumed decimal (and may have suffix multipliers) when |
| * def_hex==false; if a number is prefixed by '0x', '0X' or contains trailing |
| * 'h' or 'H' that denotes a hex number. When def_hex==true all numbers are |
| * assumed to be hex (ignored '0x' prefixes and 'h' suffixes) and multiplers |
| * are not permitted. Heap allocates an array just big enough to hold all |
| * elements if the file is countable. Pipes and stdin are not considered |
| * countable. In the non-countable case an array of MAX_FIXED_SGL_ELEMS |
| * elements is pre-allocated; if it is exceeded sg_convert_errno(EDOM) is |
| * placed in *errp (if it is non-NULL). One of the first actions is to write |
| * 0 to *errp (if it is non-NULL) so the caller does not need to zero it |
| * before calling. */ |
| bool |
| scat_gath_list::load_from_file(const char * file_name, bool def_hex, |
| bool flexible, bool b_vb) |
| { |
| bool have_stdin; |
| bool have_err = false; |
| FILE * fp; |
| const char * fnp; |
| |
| have_stdin = ((1 == strlen(file_name)) && ('-' == file_name[0])); |
| if (have_stdin) { |
| fp = stdin; |
| fnp = "<stdin>"; |
| } else { |
| fnp = file_name; |
| fp = fopen(fnp, "r"); |
| if (NULL == fp) { |
| m_errno = errno; |
| if (b_vb) |
| pr2serr("%s: opening %s: %s\n", __func__, fnp, |
| safe_strerror(m_errno)); |
| return false; |
| } |
| } |
| if (! file2sgl_helper(fp, fnp, def_hex, flexible, b_vb)) |
| have_err = true; |
| if (! have_stdin) |
| fclose(fp); |
| return have_err ? false : true; |
| } |
| |
| const char * |
| scat_gath_list::linearity_as_str() const |
| { |
| switch (linearity) { |
| case SGL_LINEAR: |
| return "linear"; |
| case SGL_MONOTONIC: |
| return "monotonic"; |
| case SGL_MONO_OVERLAP: |
| return "monotonic, overlapping"; |
| case SGL_NON_MONOTONIC: |
| return "non-monotonic"; |
| default: |
| return "unknown"; |
| } |
| } |
| |
| void |
| scat_gath_list::set_weaker_linearity(enum sgl_linearity_e lin) |
| { |
| int i_lin = (int)lin; |
| |
| if (i_lin > (int)linearity) |
| linearity = lin; |
| } |
| |
| /* id_str may be NULL (if so replace by "unknown"), present to enhance verbose |
| * output. */ |
| void |
| scat_gath_list::dbg_print(bool skip_meta, const char * id_str, bool to_stdout, |
| bool show_sgl) const |
| { |
| int num = sgl.size(); |
| const char * caller = id_str ? id_str : "unknown"; |
| FILE * fp = to_stdout ? stdout : stderr; |
| |
| if (! skip_meta) { |
| fprintf(fp, "%s: elems=%d, sgl %spresent, linearity=%s\n", |
| caller, num, (sgl.empty() ? "not " : ""), |
| linearity_as_str()); |
| fprintf(fp, " sum=%" PRId64 ", sum_hard=%s lowest=0x%" PRIx64 |
| ", high_lba_p1=", sum, (sum_hard ? "true" : "false"), |
| lowest_lba); |
| fprintf(fp, "0x%" PRIx64 "\n", high_lba_p1); |
| } |
| fprintf(fp, " >> %s scatter gather list (%d element%s):\n", caller, num, |
| (num == 1 ? "" : "s")); |
| if (show_sgl) { |
| int k; |
| |
| for (k = 0; k < num; ++k) { |
| const class scat_gath_elem & sge = sgl[k]; |
| |
| fprintf(fp, " lba: 0x%" PRIx64 ", number: 0x%" PRIx32, |
| sge.lba, sge.num); |
| if (sge.lba > 0) |
| fprintf(fp, " [next lba: 0x%" PRIx64 "]", sge.lba + sge.num); |
| fprintf(fp, "\n"); |
| } |
| } |
| } |
| |
| /* Assumes sgl array (vector) is setup. The other fields in this object are |
| * set by analyzing sgl in a single pass. The fields that are set are: |
| * fragmented, lowest_lba, high_lba_p1, monotonic, overlapping, sum and |
| * sum_hard. Degenerate elements (i.e. those with 0 blocks) are ignored apart |
| * from when one is last which makes sum_hard false and its LBA becomes |
| * high_lba_p1 if it is the highest in the list. An empty sgl is equivalent |
| * to a 1 element list with [0, 0], so sum_hard==false, monit==true, |
| * fragmented==false and overlapping==false . id_str may be NULL, present |
| * to enhance verbose output. */ |
| void |
| scat_gath_list::sum_scan(const char * id_str, bool show_sgl, bool b_vb) |
| { |
| bool degen = false; |
| bool first = true; |
| bool regular = true; /* no overlapping segments detected */ |
| int k; |
| int elems = sgl.size(); |
| uint32_t prev_num, t_num; |
| uint64_t prev_lba, t_lba, low, high, end; |
| |
| sum = 0; |
| for (k = 0, low = 0, high = 0; k < elems; ++k) { |
| const class scat_gath_elem & sge = sgl[k]; |
| |
| degen = false; |
| t_num = sge.num; |
| if (0 == t_num) { |
| degen = true; |
| if (! first) |
| continue; /* ignore degen element that not first */ |
| } |
| if (first) { |
| low = sge.lba; |
| sum = t_num; |
| high = sge.lba + sge.num; |
| first = false; |
| } else { |
| t_lba = sge.lba; |
| if ((prev_lba + prev_num) != t_lba) |
| set_weaker_linearity(SGL_MONOTONIC); |
| sum += t_num; |
| end = t_lba + t_num; |
| if (end > high) |
| high = end; /* high is one plus highest LBA */ |
| if (prev_lba < t_lba) |
| ; |
| else if (prev_lba == t_lba) { |
| if (prev_num > 0) { |
| set_weaker_linearity(SGL_MONO_OVERLAP); |
| break; |
| } |
| } else { |
| low = t_lba; |
| set_weaker_linearity(SGL_NON_MONOTONIC); |
| break; |
| } |
| if (regular) { |
| if ((prev_lba + prev_num) > t_lba) |
| regular = false; |
| } |
| } |
| prev_lba = sge.lba; |
| prev_num = sge.num; |
| } /* end of for loop while still elements and monot true */ |
| |
| if (k < elems) { /* only here if above breaks are taken */ |
| prev_lba = t_lba; |
| ++k; |
| for ( ; k < elems; ++k) { |
| const class scat_gath_elem & sge = sgl[k]; |
| |
| degen = false; |
| t_lba = sge.lba; |
| t_num = sge.num; |
| if (0 == t_num) { |
| degen = true; |
| continue; |
| } |
| sum += t_num; |
| end = t_lba + t_num; |
| if (end > high) |
| high = end; |
| if (prev_lba > t_lba) { |
| if (t_lba < low) |
| low = t_lba; |
| } |
| prev_lba = t_lba; |
| } |
| } else |
| if (! regular) |
| set_weaker_linearity(SGL_MONO_OVERLAP); |
| |
| lowest_lba = low; |
| if (degen && (elems > 0)) { /* last element always impacts high_lba_p1 */ |
| t_lba = sgl[elems - 1].lba; |
| high_lba_p1 = (t_lba > high) ? t_lba : high; |
| } else |
| high_lba_p1 = high; |
| sum_hard = (elems > 0) ? ! degen : false; |
| if (b_vb) |
| dbg_print(false, id_str, false, show_sgl); |
| } |
| |
| /* Usually will append (or add to start if empty) sge unless 'extra_blks' |
| * exceeds MAX_SGL_NUM_VAL. In that case multiple sge_s are added with |
| * sge.num = MAX_SGL_NUM_VAL or less (for final sge) until extra_blks is |
| * exhausted. Returns new size of scatter gather list. */ |
| int |
| scat_gath_list::append_1or(int64_t extra_blks, int64_t start_lba) |
| { |
| int o_num = sgl.size(); |
| const int max_nbs = MAX_SGL_NUM_VAL; |
| int64_t cnt = 0; |
| class scat_gath_elem sge; |
| |
| if ((extra_blks <= 0) && (start_lba < 0)) |
| return o_num; /* nothing to do */ |
| if ((o_num > 0) && (! sum_hard)) { |
| sge = sgl[o_num - 1]; /* assume sge.num==0 */ |
| if (sge.lba == (uint64_t)start_lba) { |
| if (extra_blks <= max_nbs) |
| sge.num = extra_blks; |
| else |
| sge.num = max_nbs; |
| sgl[o_num - 1] = sge; |
| cnt = sge.num; |
| sum += cnt; |
| sum_hard = true; |
| if (cnt <= extra_blks) { |
| high_lba_p1 = sge.lba + cnt; |
| return o_num; |
| } |
| } |
| } else if (0 == o_num) { |
| lowest_lba = start_lba; |
| if (0 == extra_blks) { |
| sge.lba = start_lba; |
| sge.num = 0; |
| sgl.push_back(sge); |
| high_lba_p1 = sge.lba; |
| return sgl.size(); |
| } |
| } |
| for ( ; cnt < extra_blks; cnt += max_nbs) { |
| sge.lba = start_lba + cnt; |
| if ((extra_blks - cnt) <= max_nbs) |
| sge.num = extra_blks - cnt; |
| else |
| sge.num = max_nbs; |
| sgl.push_back(sge); |
| sum += sge.num; |
| } /* always loops at least once */ |
| sum_hard = true; |
| high_lba_p1 = sge.lba + sge.num; |
| return sgl.size(); |
| } |
| |
| int |
| scat_gath_list::append_1or(int64_t extra_blks) |
| { |
| int o_num = sgl.size(); |
| |
| if (o_num < 1) |
| return append_1or(extra_blks, 0); |
| |
| class scat_gath_elem sge = sgl[o_num - 1]; |
| |
| return append_1or(extra_blks, sge.lba + sge.num); |
| } |
| |
| bool |
| sgls_eq_off(const scat_gath_list & left, int l_e_ind, int l_blk_off, |
| const scat_gath_list & right, int r_e_ind, int r_blk_off, |
| bool allow_partial) |
| { |
| int lelems = left.sgl.size(); |
| int relems = right.sgl.size(); |
| |
| while ((l_e_ind < lelems) && (r_e_ind < relems)) { |
| if ((left.sgl[l_e_ind].lba + l_blk_off) != |
| (right.sgl[r_e_ind].lba + r_blk_off)) |
| return false; |
| |
| int lrem = left.sgl[l_e_ind].num - l_blk_off; |
| int rrem = right.sgl[r_e_ind].num - r_blk_off; |
| |
| if (lrem == rrem) { |
| ++l_e_ind; |
| l_blk_off = 0; |
| ++r_e_ind; |
| r_blk_off = 0; |
| } else if (lrem < rrem) { |
| ++l_e_ind; |
| l_blk_off = 0; |
| r_blk_off += lrem; |
| } else { |
| ++r_e_ind; |
| r_blk_off = 0; |
| l_blk_off += rrem; |
| } |
| } |
| if ((l_e_ind >= lelems) && (r_e_ind >= relems)) |
| return true; |
| return allow_partial; |
| } |
| |
| /* If bad arguments returns -1, otherwise returns the lowest LBA in *sglp . |
| * If no elements considered returns 0. If ignore_degen is true than |
| * ignores all elements with sge.num zero unless always_last is also |
| * true in which case the last element is always considered. */ |
| int64_t |
| scat_gath_list::get_lowest_lba(bool ignore_degen, bool always_last) const |
| { |
| int k; |
| const int num_elems = sgl.size(); |
| bool some = (num_elems > 0); |
| int64_t res = INT64_MAX; |
| |
| for (k = 0; k < num_elems; ++k) { |
| if ((0 == sgl[k].num) && ignore_degen) |
| continue; |
| if ((int64_t)sgl[k].lba < res) |
| res = sgl[k].lba; |
| } |
| if (always_last && some) { |
| if ((int64_t)sgl[k - 1].lba < res) |
| res = sgl[k - 1].lba; |
| } |
| return (INT64_MAX == res) ? 0 : res; |
| } |
| |
| /* Returns >= 0 if sgl can be simplified to a single LBA. So an empty sgl |
| * will return 0; a one element sgl will return its LBA. A multiple element |
| * sgl only returns the first element's LBA (that is not degenerate) if the |
| * sgl is monotonic and not fragmented. In the extreme case takes last |
| * element's LBA if all prior elements are degenerate. Else returns -1 . |
| * Assumes sgl_sum_scan() has been called. */ |
| int64_t |
| scat_gath_list::get_low_lba_from_linear() const |
| { |
| const int num_elems = sgl.size(); |
| int k; |
| |
| if (num_elems <= 1) |
| return (1 == num_elems) ? sgl[0].lba : 0; |
| else { |
| if (linearity == SGL_LINEAR) { |
| for (k = 0; k < (num_elems - 1); ++k) { |
| if (sgl[k].num > 0) |
| return sgl[k].lba; |
| } |
| /* take last element's LBA if all earlier are degenerate */ |
| return sgl[k].lba; |
| } else |
| return -1; |
| } |
| } |
| |
| bool |
| scat_gath_list::is_pipe_suitable() const |
| { |
| return (lowest_lba == 0) && (linearity == SGL_LINEAR); |
| } |
| |
| scat_gath_iter::scat_gath_iter(const scat_gath_list & parent) |
| : sglist(parent), it_el_ind(0), it_blk_off(0), blk_idx(0) |
| { |
| int elems = sglist.num_elems(); |
| |
| if (elems > 0) |
| extend_last = (0 == sglist.sgl[elems - 1].num); |
| } |
| |
| bool |
| scat_gath_iter::set_by_blk_idx(int64_t _blk_idx) |
| { |
| bool first; |
| int k; |
| const int elems = sglist.sgl.size(); |
| const int last_ind = elems - 1; |
| int64_t bc = _blk_idx; |
| |
| if (bc < 0) |
| return false; |
| |
| if (bc == blk_idx) |
| return true; |
| else if (bc > blk_idx) { |
| k = it_el_ind; |
| bc -= blk_idx; |
| } else |
| k = 0; |
| for (first = true; k < elems; ++k, first = false) { |
| uint32_t num = ((k == last_ind) && extend_last) ? MAX_SGL_NUM_VAL : |
| sglist.sgl[k].num; |
| if (first) { |
| if ((int64_t)(num - it_blk_off) < bc) |
| bc -= (num - it_blk_off); |
| else { |
| it_blk_off = bc + it_blk_off; |
| break; |
| } |
| } else { |
| if ((int64_t)num < bc) |
| bc -= num; |
| else { |
| it_blk_off = (uint32_t)bc; |
| break; |
| } |
| } |
| } |
| it_el_ind = k; |
| blk_idx = _blk_idx; |
| |
| if (k < elems) |
| return true; |
| else if ((k == elems) && (0 == it_blk_off)) |
| return true; /* EOL */ |
| else |
| return false; |
| } |
| |
| /* Given a blk_count, the iterator (*iter_p) is moved toward the EOL. |
| * Returns true unless blk_count takes iterator two or more past the last |
| * element. So if blk_count takes the iterator to the EOL, this function |
| * returns true. Takes into account iterator's extend_last flag. */ |
| bool |
| scat_gath_iter::add_blks(uint64_t blk_count) |
| { |
| bool first; |
| int k; |
| const int elems = sglist.sgl.size(); |
| const int last_ind = elems - 1; |
| uint64_t bc = blk_count; |
| |
| if (0 == bc) |
| return true; |
| for (first = true, k = it_el_ind; k < elems; ++k) { |
| uint32_t num = ((k == last_ind) && extend_last) ? MAX_SGL_NUM_VAL : |
| sglist.sgl[k].num; |
| if (first) { |
| first = false; |
| if ((uint64_t)(num - it_blk_off) <= bc) |
| bc -= (num - it_blk_off); |
| else { |
| it_blk_off = bc + it_blk_off; |
| break; |
| } |
| } else { |
| if ((uint64_t)num <= bc) |
| bc -= num; |
| else { |
| it_blk_off = (uint32_t)bc; |
| break; |
| } |
| } |
| } |
| it_el_ind = k; |
| blk_idx += blk_count; |
| |
| if (k < elems) |
| return true; |
| else if ((k == elems) && (0 == it_blk_off)) |
| return true; /* EOL */ |
| else |
| return false; |
| } |
| |
| /* Move the iterator from its current position (which may be to EOL) towards |
| * the start of the sgl (i.e. backwards) for blk_count blocks. Returns true |
| * if iterator is valid after the move, else returns false. N.B. if false is |
| * returned, then the iterator is invalid and may need to set it to a valid |
| * value. */ |
| bool |
| scat_gath_iter::sub_blks(uint64_t blk_count) |
| { |
| bool first; |
| int k = it_el_ind; |
| uint64_t bc = 0; |
| const uint64_t orig_blk_count = blk_count; |
| |
| if (0 == blk_count) |
| return true; |
| for (first = true; k >= 0; --k) { |
| if (first) { |
| if (blk_count > (uint64_t)it_blk_off) |
| blk_count -= it_blk_off; |
| else { |
| it_blk_off -= blk_count; |
| break; |
| } |
| first = false; |
| } else { |
| uint32_t off = sglist.sgl[k].num; |
| |
| bc = blk_count; |
| if (bc > (uint64_t)off) |
| blk_count -= off; |
| else { |
| bc = off - bc; |
| break; |
| } |
| } |
| } |
| if (k < 0) { |
| blk_idx = 0; |
| it_blk_off = 0; |
| return false; /* bad situation */ |
| } |
| if ((int64_t)orig_blk_count <= blk_idx) |
| blk_idx -= orig_blk_count; |
| else |
| blk_idx = 0; |
| it_el_ind = k; |
| if (! first) |
| it_blk_off = (uint32_t)bc; |
| return true; |
| } |
| |
| /* Returns LBA referred to by iterator if valid or returns SG_LBA_INVALID |
| * (-1) if at end or invalid. */ |
| int64_t |
| scat_gath_iter::current_lba() const |
| { |
| const int elems = sglist.sgl.size(); |
| int64_t res = SG_LBA_INVALID; /* for at end or invalid (-1) */ |
| |
| if (it_el_ind < elems) { |
| class scat_gath_elem sge = sglist.sgl[it_el_ind]; |
| |
| if ((uint32_t)it_blk_off < sge.num) |
| return sge.lba + it_blk_off; |
| else if (((uint32_t)it_blk_off == sge.num) && |
| ((it_el_ind + 1) < elems)) { |
| class scat_gath_iter iter(*this); |
| |
| ++iter.it_el_ind; |
| iter.it_blk_off = 0; |
| /* worst case recursion will stop at end of sgl */ |
| return iter.current_lba(); |
| } |
| } |
| return res; |
| } |
| |
| int64_t |
| scat_gath_iter::current_lba_rem_num(int & rem_num) const |
| { |
| const int elems = sglist.sgl.size(); |
| int64_t res = SG_LBA_INVALID; /* for at end or invalid (-1) */ |
| |
| if (it_el_ind < elems) { |
| class scat_gath_elem sge = sglist.sgl[it_el_ind]; |
| |
| if ((uint32_t)it_blk_off < sge.num) { |
| rem_num = sge.num - it_blk_off; |
| return sge.lba + it_blk_off; |
| } else if (((uint32_t)it_blk_off == sge.num) && |
| ((it_el_ind + 1) < elems)) { |
| class scat_gath_iter iter(*this); |
| |
| ++iter.it_el_ind; |
| iter.it_blk_off = 0; |
| /* worst case recursion will stop at end of sgl */ |
| return iter.current_lba_rem_num(rem_num); |
| } |
| } |
| rem_num = -1; |
| return res; |
| } |
| |
| class scat_gath_elem |
| scat_gath_iter::current_elem() const |
| { |
| const int elems = sglist.sgl.size(); |
| class scat_gath_elem sge; |
| |
| sge.make_bad(); |
| if (it_el_ind < elems) |
| return sglist.sgl[it_el_ind]; |
| return sge; |
| } |
| |
| /* Returns true of no sgl or sgl is at the end [elems, 0], otherwise it |
| * returns false. */ |
| bool |
| scat_gath_iter::at_end() const |
| { |
| const int elems = sglist.sgl.size(); |
| |
| return ((0 == elems) || ((it_el_ind == elems) && (0 == it_blk_off))); |
| } |
| |
| /* Returns true if associated iterator is monotonic (increasing) and not |
| * fragmented. Empty sgl and single element degenerate considered linear. |
| * Assumes sgl_sum_scan() has been called on sgl. */ |
| bool |
| scat_gath_iter::is_sgl_linear() const |
| { |
| return sglist.linearity == SGL_LINEAR; |
| } |
| |
| /* Should return 1 or more unless max_n<=0 or at_end() */ |
| int |
| scat_gath_iter::linear_for_n_blks(int max_n) const |
| { |
| int k, rem; |
| const int elems = sglist.sgl.size(); |
| uint64_t prev_lba; |
| class scat_gath_elem sge; |
| |
| if (at_end() || (max_n <= 0)) |
| return 0; |
| sge = sglist.sgl[it_el_ind]; |
| rem = (int)sge.num - it_blk_off; |
| if (rem <= 0) { |
| sge = sglist.sgl[it_el_ind + 1]; |
| rem = (int)sge.num; |
| } |
| if (max_n <= rem) |
| return max_n; |
| prev_lba = sge.lba + sge.num; |
| for (k = it_el_ind + 1; k < elems; ++k) { |
| sge = sglist.sgl[k]; |
| if (sge.lba != prev_lba) |
| return rem; |
| rem += sge.num; |
| if (max_n <= rem) |
| return max_n; |
| prev_lba = sge.lba + sge.num; |
| } |
| return rem; |
| } |
| |
| /* id_str may be NULL (if so replace by "unknown"), present to enhance verbose |
| * output. */ |
| void |
| scat_gath_iter::dbg_print(const char * id_str, bool to_stdout, |
| int verbose) const |
| { |
| const char * caller = id_str ? id_str : "unknown"; |
| FILE * fp = to_stdout ? stdout : stderr; |
| |
| fprintf(fp, "%s: it_el_ind=%d, it_blk_off=%d, blk_idx=%" PRId64 "\n", |
| caller, it_el_ind, it_blk_off, blk_idx); |
| fprintf(fp, " extend_last=%d\n", extend_last); |
| if (verbose) |
| sglist.dbg_print(false, " iterator's", to_stdout, verbose > 1); |
| } |
| |
| /* Calculates difference between iterators, logically: res <-- lhs - rhs |
| * Checks that lhsp and rhsp have same underlying sgl, if not returns |
| * INT_MIN. Assumes iterators close enough for result to lie in range |
| * from (-INT_MAX) to INT_MAX (inclusive). */ |
| int |
| diff_between_iters(const class scat_gath_iter & left, |
| const class scat_gath_iter & right) |
| { |
| int res, k, r_e_ind, l_e_ind; |
| |
| if (&left.sglist != &right.sglist) { |
| pr2serr("%s: bad args\n", __func__); |
| return INT_MIN; |
| } |
| r_e_ind = right.it_el_ind; |
| l_e_ind = left.it_el_ind; |
| if (l_e_ind < r_e_ind) { /* so difference will be negative */ |
| res = diff_between_iters(right, left); /* cheat */ |
| if (INT_MIN == res) |
| return res; |
| return -res; |
| } else if (l_e_ind == r_e_ind) |
| return (int)left.it_blk_off - (int)right.it_blk_off; |
| /* (l_e_ind > r_e_ind) so (lhs > rhs) */ |
| res = (int)right.sglist.sgl[r_e_ind].num - right.it_blk_off; |
| for (k = 1; (r_e_ind + k) < l_e_ind; ++k) { |
| // pr2serr("%s: k=%d, res=%d, num=%d\n", __func__, k, res, |
| // (int)right.sglist.sgl[r_e_ind + k].num); |
| res += (int)right.sglist.sgl[r_e_ind + k].num; |
| } |
| res += left.it_blk_off; |
| // pr2serr("%s: at exit res=%d\n", __func__, res); |
| return res; |
| } |
| |
| /* Compares from the current iterator positions of left and left until |
| * the shorter list is exhausted. Returns false on the first inequality. |
| * If no inequality and both remaining lists are same length then returns |
| * true. If no inequality but remaining lists differ in length then returns |
| * allow_partial. */ |
| bool |
| sgls_eq_from_iters(const class scat_gath_iter & left, |
| const class scat_gath_iter & right, |
| bool allow_partial) |
| { |
| return sgls_eq_off(left.sglist, left.it_el_ind, left.it_blk_off, |
| right.sglist, right.it_el_ind, right.it_blk_off, |
| allow_partial); |
| } |