mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
Since commit 760b3dffb0 ("qdl: Rework qdl_write to limit write sizes
to out_maxpktsize") outgoing transfers has been chunked into blocks of
wMaxPacketSize.
As reported by Wiktor Drewniak, Maksim Paimushkin, and Luca Weiss in [1]
there's huge benefits to be found in reverting this change, but out of
caution the limit was untouched.
With the transition to libusb, the throughput dropped another ~15% on my
machine. The numbers for HighSpeed and SuperSpeed are also in the same
ballpark.
With SuperSpeed, benchmarking of different chunk sizes in the megabyte
range shows improvement over these numbers in the range of 15-20x on the
same machine/board combination.
The bug report related to the reduction in size describes a host machine
running out of swiotlb, from the fact that we requested 1MB physically
contiguous memory. It's unclear if attempts was made to correct the
kernel's behavior. libusb does inquiry the capabilities of the kernel
and will split the buffer and submitting multiple URBs at once, as
needed. While no definitive guidance has been found, multiple sources
seems to recommend passing 1-2MB of buffer to libusb at a time. So,
let's move the default chunk size back up to 1MB and hope that libusb
resolves the reported problem.
Additionally, introduce a new option --out-chunk-size, which allow the
user to override the chunk size. This would allow any user to reduce the
size if needed, but also allow the value to be increased as needed.
[1] https://github.com/linux-msm/qdl/pull/39
Reported-by: Wiktor Drewniak
Reported-by: Maksim Paimushkin
Reported-by: Luca Weiss
Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
217 lines
5.5 KiB
C
217 lines
5.5 KiB
C
/*
|
|
* Copyright (c) 2016-2017, Linaro Ltd.
|
|
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <libxml/parser.h>
|
|
#include <libxml/tree.h>
|
|
|
|
#include "qdl.h"
|
|
#include "patch.h"
|
|
#include "ufs.h"
|
|
|
|
#define MAX_USBFS_BULK_SIZE (16*1024)
|
|
|
|
enum {
|
|
QDL_FILE_UNKNOWN,
|
|
QDL_FILE_PATCH,
|
|
QDL_FILE_PROGRAM,
|
|
QDL_FILE_READ,
|
|
QDL_FILE_UFS,
|
|
QDL_FILE_CONTENTS,
|
|
};
|
|
|
|
bool qdl_debug;
|
|
static struct qdl_device qdl;
|
|
|
|
static int detect_type(const char *xml_file)
|
|
{
|
|
xmlNode *root;
|
|
xmlDoc *doc;
|
|
xmlNode *node;
|
|
int type = QDL_FILE_UNKNOWN;
|
|
|
|
doc = xmlReadFile(xml_file, NULL, 0);
|
|
if (!doc) {
|
|
fprintf(stderr, "[PATCH] failed to parse %s\n", xml_file);
|
|
return -EINVAL;
|
|
}
|
|
|
|
root = xmlDocGetRootElement(doc);
|
|
if (!xmlStrcmp(root->name, (xmlChar*)"patches")) {
|
|
type = QDL_FILE_PATCH;
|
|
} else if (!xmlStrcmp(root->name, (xmlChar*)"data")) {
|
|
for (node = root->children; node ; node = node->next) {
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
if (!xmlStrcmp(node->name, (xmlChar*)"program")) {
|
|
type = QDL_FILE_PROGRAM;
|
|
break;
|
|
}
|
|
if (!xmlStrcmp(node->name, (xmlChar*)"read")) {
|
|
type = QDL_FILE_READ;
|
|
break;
|
|
}
|
|
if (!xmlStrcmp(node->name, (xmlChar*)"ufs")) {
|
|
type = QDL_FILE_UFS;
|
|
break;
|
|
}
|
|
}
|
|
} else if (!xmlStrcmp(root->name, (xmlChar*)"contents")) {
|
|
type = QDL_FILE_CONTENTS;
|
|
}
|
|
|
|
xmlFreeDoc(doc);
|
|
|
|
return type;
|
|
}
|
|
|
|
static void print_usage(void)
|
|
{
|
|
extern const char *__progname;
|
|
fprintf(stderr,
|
|
"%s [--debug] [--storage <emmc|nand|ufs>] [--finalize-provisioning] [--include <PATH>] <prog.mbn> [<program> <patch> ...]\n",
|
|
__progname);
|
|
}
|
|
|
|
enum {
|
|
OPT_OUT_CHUNK_SIZE = 1000,
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char *prog_mbn, *storage="ufs";
|
|
char *incdir = NULL;
|
|
char *serial = NULL;
|
|
int type;
|
|
int ret;
|
|
int opt;
|
|
bool qdl_finalize_provisioning = false;
|
|
long out_chunk_size;
|
|
|
|
static struct option options[] = {
|
|
{"debug", no_argument, 0, 'd'},
|
|
{"include", required_argument, 0, 'i'},
|
|
{"finalize-provisioning", no_argument, 0, 'l'},
|
|
{"out-chunk-size", required_argument, 0, OPT_OUT_CHUNK_SIZE },
|
|
{"serial", required_argument, 0, 'S'},
|
|
{"storage", required_argument, 0, 's'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
while ((opt = getopt_long(argc, argv, "di:S:", options, NULL )) != -1) {
|
|
switch (opt) {
|
|
case 'd':
|
|
qdl_debug = true;
|
|
break;
|
|
case 'i':
|
|
incdir = optarg;
|
|
break;
|
|
case 'l':
|
|
qdl_finalize_provisioning = true;
|
|
break;
|
|
case OPT_OUT_CHUNK_SIZE:
|
|
out_chunk_size = strtol(optarg, NULL, 10);
|
|
qdl_set_out_chunk_size(&qdl, out_chunk_size);
|
|
break;
|
|
case 's':
|
|
storage = optarg;
|
|
break;
|
|
case 'S':
|
|
serial = optarg;
|
|
break;
|
|
default:
|
|
print_usage();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* at least 2 non optional args required */
|
|
if ((optind + 2) > argc) {
|
|
print_usage();
|
|
return 1;
|
|
}
|
|
|
|
prog_mbn = argv[optind++];
|
|
|
|
do {
|
|
type = detect_type(argv[optind]);
|
|
if (type < 0 || type == QDL_FILE_UNKNOWN)
|
|
errx(1, "failed to detect file type of %s\n", argv[optind]);
|
|
|
|
switch (type) {
|
|
case QDL_FILE_PATCH:
|
|
ret = patch_load(argv[optind]);
|
|
if (ret < 0)
|
|
errx(1, "patch_load %s failed", argv[optind]);
|
|
break;
|
|
case QDL_FILE_PROGRAM:
|
|
ret = program_load(argv[optind], !strcmp(storage, "nand"));
|
|
if (ret < 0)
|
|
errx(1, "program_load %s failed", argv[optind]);
|
|
break;
|
|
case QDL_FILE_READ:
|
|
ret = read_op_load(argv[optind]);
|
|
if (ret < 0)
|
|
errx(1, "read_op_load %s failed", argv[optind]);
|
|
break;
|
|
case QDL_FILE_UFS:
|
|
ret = ufs_load(argv[optind],qdl_finalize_provisioning);
|
|
if (ret < 0)
|
|
errx(1, "ufs_load %s failed", argv[optind]);
|
|
break;
|
|
default:
|
|
errx(1, "%s type not yet supported", argv[optind]);
|
|
break;
|
|
}
|
|
} while (++optind < argc);
|
|
|
|
ret = qdl_open(&qdl, serial);
|
|
if (ret)
|
|
return 1;
|
|
|
|
qdl.mappings[0] = prog_mbn;
|
|
ret = sahara_run(&qdl, qdl.mappings, true, NULL, NULL);
|
|
if (ret < 0)
|
|
return 1;
|
|
|
|
ret = firehose_run(&qdl, incdir, storage);
|
|
if (ret < 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|