mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 18:13:04 +00:00
mtd: rawnand: use longest matching pattern
Sometimes the exec_op parser does not choose the optimal pattern if multiple patterns with optional elements are available. Since the stack automatically splits operations in multiple exec_op calls, a non-optimal pattern gets broken up into multiple calls. E.g. an OOB read using the vf610 driver: nand: executing subop: nand: ->CMD [0x00] nand: ->ADDR [5 cyc: 00 08 ea 94 02] nand: ->CMD [0x30] nand: ->WAITRDY [max 200000 ms] nand: DATA_IN [64 B] nand: executing subop: nand: CMD [0x00] nand: ADDR [5 cyc: 00 08 ea 94 02] nand: CMD [0x30] nand: WAITRDY [max 200000 ms] nand: ->DATA_IN [64 B] However, the vf610 driver has a pattern which can execute the complete command in a single go... This patch makes sure that the longest matching pattern is chosen instead of the first (potentially only partial) match. With this change the vf610 reads the OOB in a single exec_op call: nand: executing subop: nand: ->CMD [0x00] nand: ->ADDR [5 cyc: 00 08 c0 1d 00] nand: ->CMD [0x30] nand: ->WAITRDY [max 200000 ms] nand: ->DATA_IN [64 B] Reported-by: Sascha Hauer <s.hauer@pengutronix.de> Suggested-by: Boris Brezillon <boris.brezillon@collabora.com> Tested-by: Stefan Agner <stefan@agner.ch> Signed-off-by: Stefan Agner <stefan@agner.ch> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
This commit is contained in:
parent
f2c7c76c5d
commit
2099920ebe
@ -2156,6 +2156,22 @@ static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int nand_op_parser_cmp_ctx(const struct nand_op_parser_ctx *a,
|
||||
const struct nand_op_parser_ctx *b)
|
||||
{
|
||||
if (a->subop.ninstrs < b->subop.ninstrs)
|
||||
return -1;
|
||||
else if (a->subop.ninstrs > b->subop.ninstrs)
|
||||
return 1;
|
||||
|
||||
if (a->subop.last_instr_end_off < b->subop.last_instr_end_off)
|
||||
return -1;
|
||||
else if (a->subop.last_instr_end_off > b->subop.last_instr_end_off)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_op_parser_exec_op - exec_op parser
|
||||
* @chip: the NAND chip
|
||||
@ -2190,30 +2206,38 @@ int nand_op_parser_exec_op(struct nand_chip *chip,
|
||||
unsigned int i;
|
||||
|
||||
while (ctx.subop.instrs < op->instrs + op->ninstrs) {
|
||||
int ret;
|
||||
const struct nand_op_parser_pattern *pattern;
|
||||
struct nand_op_parser_ctx best_ctx;
|
||||
int ret, best_pattern = -1;
|
||||
|
||||
for (i = 0; i < parser->npatterns; i++) {
|
||||
const struct nand_op_parser_pattern *pattern;
|
||||
struct nand_op_parser_ctx test_ctx = ctx;
|
||||
|
||||
pattern = &parser->patterns[i];
|
||||
if (!nand_op_parser_match_pat(pattern, &ctx))
|
||||
if (!nand_op_parser_match_pat(pattern, &test_ctx))
|
||||
continue;
|
||||
|
||||
nand_op_parser_trace(&ctx);
|
||||
if (best_pattern >= 0 &&
|
||||
nand_op_parser_cmp_ctx(&test_ctx, &best_ctx) <= 0)
|
||||
continue;
|
||||
|
||||
if (check_only)
|
||||
break;
|
||||
best_pattern = i;
|
||||
best_ctx = test_ctx;
|
||||
}
|
||||
|
||||
if (best_pattern < 0) {
|
||||
pr_debug("->exec_op() parser: pattern not found!\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
ctx = best_ctx;
|
||||
nand_op_parser_trace(&ctx);
|
||||
|
||||
if (!check_only) {
|
||||
pattern = &parser->patterns[best_pattern];
|
||||
ret = pattern->exec(chip, &ctx.subop);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == parser->npatterns) {
|
||||
pr_debug("->exec_op() parser: pattern not found!\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user