Numonyx NOR and chip->mutex bug?

Michael Cashwell mboards at prograde.net
Fri Feb 4 12:09:01 EST 2011


On Feb 4, 2011, at 8:05 AM, Joakim Tjernlund wrote:

> Stefan,
> 
> I think the bug lies in
> static int inval_cache_and_wait_for_operation(
> 		struct map_info *map, struct flchip *chip,
> 		unsigned long cmd_adr, unsigned long inval_adr, int inval_len,
> 		unsigned int chip_op_time, unsigned int chip_op_time_max)
> {
> 	struct cfi_private *cfi = map->fldrv_priv;
> 	map_word status, status_OK = CMD(0x80);
> 	int chip_state = chip->state;
> 	unsigned int timeo, sleep_time, reset_timeo;
> 
> 	mutex_unlock(&chip->mutex);
> 	if (inval_len)
> 		INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len);
> 	mutex_lock(&chip->mutex);
> 
> Here we drop the lock and take it again. Someone may suspend the
> erase and do a read/write instead and put the chip some other state.

That's where I get confused. It's true that another thread could do that but wouldn't it have to bracket its operation with calls to get_chip() and put_chip()? Those actually do the suspend and resume. I'm certainly seeing erases being suspended to do a buffered write and then being resumed.

A properly done suspend/resume should leave the hardware is the same state it was in before hand. So if get_chip/put_chip are not doing that then that's where the problem is.

Originally I thought a udelay(20) was needed after the resume command was sent. What I've most recently seen is that just doing one map_read() of status (and discarding the result) is enough to avoid my error:

	switch(chip->oldstate) {
	case FL_ERASING:
		chip->state = chip->oldstate;
		/* What if one interleaved chip has finished and the
		   other hasn't? The old code would leave the finished
		   one in READY mode. That's bad, and caused -EROFS
		   errors to be returned from do_erase_oneblock because
		   that's the only bit it checked for at the time.
		   As the state machine appears to explicitly allow
		   sending the 0x70 (Read Status) command to an erasing
		   chip and expecting it to be ignored, that's what we
		   do. */
		map_write(map, CMD(0xd0), adr);
		map_write(map, CMD(0x70), adr);
		(void) map_read(map, adr);	// <<<<----- added
		chip->oldstate = FL_READY;
		chip->state = FL_ERASING;
		break;

I'm still not sure why. My guess is that it's timing (since udelay(20) in the same spot also works for me) but being timing-sensitive is making it hard to pin down. Adding printk()s also often makes it not happen.

-Mike




More information about the linux-mtd mailing list