blob: 1f3cff1836d1aee31d14f228ed50b8ea64dea5f6 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2020 John Chau <john@harmon.hk>
*
*/
#include <command.h>
#include <malloc.h>
#include <part.h>
#include <blk.h>
#include <time.h>
#include <vsprintf.h>
#define BUFSIZE (1 * 1024 * 1024)
static int do_clone(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
{
int srcdev, destdev;
struct blk_desc *srcdesc, *destdesc;
int srcbz, destbz, ret;
char *unit, *buf;
unsigned long wrcnt, rdcnt, requested, srcblk, destblk;
unsigned long timer;
const unsigned long buffersize = 1024 * 1024;
if (argc < 6)
return CMD_RET_USAGE;
srcdev = blk_get_device_by_str(argv[1], argv[2], &srcdesc);
destdev = blk_get_device_by_str(argv[3], argv[4], &destdesc);
if (srcdev < 0) {
printf("Unable to open source device\n");
return 1;
} else if (destdev < 0) {
printf("Unable to open destination device\n");
return 1;
}
requested = dectoul(argv[5], &unit);
srcbz = srcdesc->blksz;
destbz = destdesc->blksz;
if ((srcbz * (buffersize / srcbz) != buffersize) ||
(destbz * (buffersize / destbz) != buffersize)) {
printf("failed: cannot match device block sizes\n");
return 1;
}
if (requested == 0) {
unsigned long a = srcdesc->lba * srcdesc->blksz;
unsigned long b = destdesc->lba * destdesc->blksz;
if (a > b)
requested = a;
else
requested = b;
} else {
switch (unit[0]) {
case 'g':
case 'G':
requested *= 1024 * 1024 * 1024;
break;
case 'm':
case 'M':
requested *= 1024 * 1024;
break;
case 'k':
case 'K':
requested *= 1024;
break;
}
}
printf("Copying %ld bytes from %s:%s to %s:%s\n",
requested, argv[1], argv[2], argv[3], argv[4]);
wrcnt = 0;
rdcnt = 0;
buf = (char *)malloc(BUFSIZE);
srcblk = 0;
destblk = 0;
timer = get_timer(0);
while (wrcnt < requested) {
unsigned long toread = BUFSIZE / srcbz;
unsigned long towrite = BUFSIZE / destbz;
unsigned long offset = 0;
read:
ret = blk_dread(srcdesc, srcblk, toread, buf + offset);
if (ret < 0) {
printf("Src read error @blk %ld\n", srcblk);
goto exit;
}
rdcnt += ret * srcbz;
srcblk += ret;
if (ret < toread) {
toread -= ret;
offset += ret * srcbz;
goto read;
}
offset = 0;
write:
ret = blk_dwrite(destdesc, destblk, towrite, buf + offset);
if (ret < 0) {
printf("Dest write error @blk %ld\n", srcblk);
goto exit;
}
wrcnt += ret * destbz;
destblk += ret;
if (ret < towrite) {
towrite -= ret;
offset += ret * destbz;
goto write;
}
}
exit:
timer = get_timer(timer);
timer = 1000 * timer / CONFIG_SYS_HZ;
printf("%ld read\n", rdcnt);
printf("%ld written\n", wrcnt);
printf("%ldms, %ldkB/s\n", timer, wrcnt / timer);
free(buf);
return 0;
}
U_BOOT_CMD(
clone, 6, 1, do_clone,
"simple storage cloning",
"<src interface> <src dev> <dest interface> <dest dev> <size[K/M/G]>\n"
"clone storage from 'src dev' on 'src interface' to 'dest dev' on 'dest interface' with maximum 'size' bytes (or 0 for clone to end)"
);