Blame view

fs/jffs2/wbuf.c 36.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
c00c310ea   David Woodhouse   [JFFS2] Tidy up l...
4
5
   * Copyright © 2001-2007 Red Hat, Inc.
   * Copyright © 2004 Thomas Gleixner <tglx@linutronix.de>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
   *
   * Created by David Woodhouse <dwmw2@infradead.org>
   * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
   *
   * For licensing information, see the file 'LICENCE' in this directory.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
   */
5a528957e   Joe Perches   jffs2: Use pr_fmt...
13
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/mtd/mtd.h>
  #include <linux/crc32.h>
d4092d76a   Boris Brezillon   mtd: nand: Rename...
18
  #include <linux/mtd/rawnand.h>
4e57b6817   Tim Schmielau   [PATCH] fix missi...
19
  #include <linux/jiffies.h>
914e26379   Al Viro   [PATCH] severing ...
20
  #include <linux/sched.h>
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
21
  #include <linux/writeback.h>
4e57b6817   Tim Schmielau   [PATCH] fix missi...
22

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
29
30
31
  #include "nodelist.h"
  
  /* For testing write failures */
  #undef BREAKME
  #undef BREAKMEHEADER
  
  #ifdef BREAKME
  static unsigned char *brokenbuf;
  #endif
daba5cc4b   Artem B. Bityutskiy   [JFFS2] Fix dataf...
32
33
  #define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
  #define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
  /* max. erase failures before we mark a block bad */
  #define MAX_ERASE_FAILURES 	2
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  struct jffs2_inodirty {
  	uint32_t ino;
  	struct jffs2_inodirty *next;
  };
  
  static struct jffs2_inodirty inodirty_nomem;
  
  static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
  {
  	struct jffs2_inodirty *this = c->wbuf_inodes;
  
  	/* If a malloc failed, consider _everything_ dirty */
  	if (this == &inodirty_nomem)
  		return 1;
  
  	/* If ino == 0, _any_ non-GC writes mean 'yes' */
  	if (this && !ino)
  		return 1;
  
  	/* Look to see if the inode in question is pending in the wbuf */
  	while (this) {
  		if (this->ino == ino)
  			return 1;
  		this = this->next;
  	}
  	return 0;
  }
  
  static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
  {
  	struct jffs2_inodirty *this;
  
  	this = c->wbuf_inodes;
  
  	if (this != &inodirty_nomem) {
  		while (this) {
  			struct jffs2_inodirty *next = this->next;
  			kfree(this);
  			this = next;
  		}
  	}
  	c->wbuf_inodes = NULL;
  }
  
  static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
  {
  	struct jffs2_inodirty *new;
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
83
  	/* Schedule delayed write-buffer write-out */
64a5c2eb8   Joakim Tjernlund   jffs2: Rename jff...
84
  	jffs2_dirty_trigger(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
89
90
  
  	if (jffs2_wbuf_pending_for_ino(c, ino))
  		return;
  
  	new = kmalloc(sizeof(*new), GFP_KERNEL);
  	if (!new) {
9c261b33a   Joe Perches   jffs2: Convert mo...
91
92
  		jffs2_dbg(1, "No memory to allocate inodirty. Fallback to all considered dirty
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  		jffs2_clear_wbuf_ino_list(c);
  		c->wbuf_inodes = &inodirty_nomem;
  		return;
  	}
  	new->ino = ino;
  	new->next = c->wbuf_inodes;
  	c->wbuf_inodes = new;
  	return;
  }
  
  static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
  {
  	struct list_head *this, *next;
  	static int n;
  
  	if (list_empty(&c->erasable_pending_wbuf_list))
  		return;
  
  	list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
  		struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
9c261b33a   Joe Perches   jffs2: Convert mo...
113
114
115
  		jffs2_dbg(1, "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...
  ",
  			  jeb->offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
  		list_del(this);
  		if ((jiffies + (n++)) & 127) {
  			/* Most of the time, we just erase it immediately. Otherwise we
  			   spend ages scanning it on mount, etc. */
9c261b33a   Joe Perches   jffs2: Convert mo...
120
121
  			jffs2_dbg(1, "...and adding to erase_pending_list
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
123
  			list_add_tail(&jeb->list, &c->erase_pending_list);
  			c->nr_erasing_blocks++;
ae3b6ba06   David Woodhouse   jffs2: Use jffs2_...
124
  			jffs2_garbage_collect_trigger(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
  		} else {
  			/* Sometimes, however, we leave it elsewhere so it doesn't get
  			   immediately reused, and we spread the load a bit. */
9c261b33a   Joe Perches   jffs2: Convert mo...
128
129
  			jffs2_dbg(1, "...and adding to erasable_list
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
  			list_add_tail(&jeb->list, &c->erasable_list);
  		}
  	}
  }
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
134
135
136
137
  #define REFILE_NOTEMPTY 0
  #define REFILE_ANYWAY   1
  
  static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  {
9c261b33a   Joe Perches   jffs2: Convert mo...
139
140
  	jffs2_dbg(1, "About to refile bad block at %08x
  ", jeb->offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
146
147
  	/* File the existing block on the bad_used_list.... */
  	if (c->nextblock == jeb)
  		c->nextblock = NULL;
  	else /* Not sure this should ever happen... need more coffee */
  		list_del(&jeb->list);
  	if (jeb->first_node) {
9c261b33a   Joe Perches   jffs2: Convert mo...
148
149
150
  		jffs2_dbg(1, "Refiling block at %08x to bad_used_list
  ",
  			  jeb->offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
  		list_add(&jeb->list, &c->bad_used_list);
  	} else {
9b88f4739   Estelle Hammache   [JFFS2] Code clea...
153
  		BUG_ON(allow_empty == REFILE_NOTEMPTY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  		/* It has to have had some nodes or we couldn't be here */
9c261b33a   Joe Perches   jffs2: Convert mo...
155
156
157
  		jffs2_dbg(1, "Refiling block at %08x to erase_pending_list
  ",
  			  jeb->offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
  		list_add(&jeb->list, &c->erase_pending_list);
  		c->nr_erasing_blocks++;
ae3b6ba06   David Woodhouse   jffs2: Use jffs2_...
160
  		jffs2_garbage_collect_trigger(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162

9bfeb691e   David Woodhouse   [JFFS2] Switch to...
163
164
165
166
167
168
169
170
171
172
173
174
  	if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) {
  		uint32_t oldfree = jeb->free_size;
  
  		jffs2_link_node_ref(c, jeb, 
  				    (jeb->offset+c->sector_size-oldfree) | REF_OBSOLETE,
  				    oldfree, NULL);
  		/* convert to wasted */
  		c->wasted_size += oldfree;
  		jeb->wasted_size += oldfree;
  		c->dirty_size -= oldfree;
  		jeb->dirty_size -= oldfree;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175

e0c8e42f8   Artem B. Bityutskiy   [JFFS2] Debug cod...
176
177
178
  	jffs2_dbg_dump_block_lists_nolock(c);
  	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
  	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  }
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info *c,
  							    struct jffs2_inode_info *f,
  							    struct jffs2_raw_node_ref *raw,
  							    union jffs2_node_union *node)
  {
  	struct jffs2_node_frag *frag;
  	struct jffs2_full_dirent *fd;
  
  	dbg_noderef("incore_replace_raw: node at %p is {%04x,%04x}
  ",
  		    node, je16_to_cpu(node->u.magic), je16_to_cpu(node->u.nodetype));
  
  	BUG_ON(je16_to_cpu(node->u.magic) != 0x1985 &&
  	       je16_to_cpu(node->u.magic) != 0);
  
  	switch (je16_to_cpu(node->u.nodetype)) {
  	case JFFS2_NODETYPE_INODE:
ddc58bd65   David Woodhouse   [JFFS2] Fix wbuf ...
197
198
199
200
201
  		if (f->metadata && f->metadata->raw == raw) {
  			dbg_noderef("Will replace ->raw in f->metadata at %p
  ", f->metadata);
  			return &f->metadata->raw;
  		}
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
202
203
204
205
206
207
208
209
210
211
  		frag = jffs2_lookup_node_frag(&f->fragtree, je32_to_cpu(node->i.offset));
  		BUG_ON(!frag);
  		/* Find a frag which refers to the full_dnode we want to modify */
  		while (!frag->node || frag->node->raw != raw) {
  			frag = frag_next(frag);
  			BUG_ON(!frag);
  		}
  		dbg_noderef("Will replace ->raw in full_dnode at %p
  ", frag->node);
  		return &frag->node->raw;
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
212
213
214
215
216
217
218
219
220
221
  
  	case JFFS2_NODETYPE_DIRENT:
  		for (fd = f->dents; fd; fd = fd->next) {
  			if (fd->raw == raw) {
  				dbg_noderef("Will replace ->raw in full_dirent at %p
  ", fd);
  				return &fd->raw;
  			}
  		}
  		BUG();
ddc58bd65   David Woodhouse   [JFFS2] Fix wbuf ...
222

9bfeb691e   David Woodhouse   [JFFS2] Switch to...
223
224
225
226
227
228
229
230
  	default:
  		dbg_noderef("Don't care about replacing raw for nodetype %x
  ",
  			    je16_to_cpu(node->u.nodetype));
  		break;
  	}
  	return NULL;
  }
a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
231
232
233
234
235
236
237
  #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
  static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
  			      uint32_t ofs)
  {
  	int ret;
  	size_t retlen;
  	char *eccstr;
329ad399a   Artem Bityutskiy   mtd: introduce mt...
238
  	ret = mtd_read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify);
a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
239
  	if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
da320f055   Joe Perches   jffs2: Convert pr...
240
241
242
  		pr_warn("%s(): Read back of page at %08x failed: %d
  ",
  			__func__, c->wbuf_ofs, ret);
a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
243
244
  		return ret;
  	} else if (retlen != c->wbuf_pagesize) {
da320f055   Joe Perches   jffs2: Convert pr...
245
246
247
  		pr_warn("%s(): Read back of page at %08x gave short read: %zd not %d
  ",
  			__func__, ofs, retlen, c->wbuf_pagesize);
a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
248
249
250
251
252
253
254
255
256
257
258
  		return -EIO;
  	}
  	if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize))
  		return 0;
  
  	if (ret == -EUCLEAN)
  		eccstr = "corrected";
  	else if (ret == -EBADMSG)
  		eccstr = "correction failed";
  	else
  		eccstr = "OK or unused";
da320f055   Joe Perches   jffs2: Convert pr...
259
260
261
  	pr_warn("Write verify error (ECC %s) at %08x. Wrote:
  ",
  		eccstr, c->wbuf_ofs);
a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
262
263
  	print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
  		       c->wbuf, c->wbuf_pagesize, 0);
da320f055   Joe Perches   jffs2: Convert pr...
264
265
  	pr_warn("Read back:
  ");
a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
266
267
268
269
270
271
272
273
  	print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
  		       c->wbuf_verify, c->wbuf_pagesize, 0);
  
  	return -EIO;
  }
  #else
  #define jffs2_verify_write(c,b,o) (0)
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
279
  /* Recover from failure to write wbuf. Recover the nodes up to the
   * wbuf, not the one which we were starting to try to write. */
  
  static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
  {
  	struct jffs2_eraseblock *jeb, *new_jeb;
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
280
  	struct jffs2_raw_node_ref *raw, *next, *first_raw = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
  	size_t retlen;
  	int ret;
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
283
  	int nr_refile = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
  	unsigned char *buf;
  	uint32_t start, end, ofs, len;
046b8b980   David Woodhouse   [JFFS2] Add 'jeb'...
286
  	jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
  	spin_lock(&c->erase_completion_lock);
180bfb31f   Vitaly Wool   [JFFS2] Fix write...
288
289
290
291
  	if (c->wbuf_ofs % c->mtd->erasesize)
  		jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
  	else
  		jffs2_block_refile(c, jeb, REFILE_ANYWAY);
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
292
293
294
  	spin_unlock(&c->erase_completion_lock);
  
  	BUG_ON(!ref_obsolete(jeb->last_node));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
  
  	/* Find the first node to be recovered, by skipping over every
  	   node which ends before the wbuf starts, or which is obsolete. */
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  	for (next = raw = jeb->first_node; next; raw = next) {
  		next = ref_next(raw);
  
  		if (ref_obsolete(raw) || 
  		    (next && ref_offset(next) <= c->wbuf_ofs)) {
  			dbg_noderef("Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete
  ",
  				    ref_offset(raw), ref_flags(raw),
  				    (ref_offset(raw) + ref_totlen(c, jeb, raw)),
  				    c->wbuf_ofs);
  			continue;
  		}
  		dbg_noderef("First node to be recovered is at 0x%08x(%d)-0x%08x
  ",
  			    ref_offset(raw), ref_flags(raw),
  			    (ref_offset(raw) + ref_totlen(c, jeb, raw)));
  
  		first_raw = raw;
  		break;
  	}
  
  	if (!first_raw) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
  		/* All nodes were obsolete. Nothing to recover. */
9c261b33a   Joe Perches   jffs2: Convert mo...
321
322
  		jffs2_dbg(1, "No non-obsolete nodes to be recovered. Just filing block bad
  ");
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
323
  		c->wbuf_len = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
  		return;
  	}
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
326
327
328
  	start = ref_offset(first_raw);
  	end = ref_offset(jeb->last_node);
  	nr_refile = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329

9bfeb691e   David Woodhouse   [JFFS2] Switch to...
330
331
332
  	/* Count the number of refs which need to be copied */
  	while ((raw = ref_next(raw)) != jeb->last_node)
  		nr_refile++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333

9bfeb691e   David Woodhouse   [JFFS2] Switch to...
334
335
336
  	dbg_noderef("wbuf recover %08x-%08x (%d bytes in %d nodes)
  ",
  		    start, end, end - start, nr_refile);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
339
340
341
342
343
344
  
  	buf = NULL;
  	if (start < c->wbuf_ofs) {
  		/* First affected node was already partially written.
  		 * Attempt to reread the old data into our buffer. */
  
  		buf = kmalloc(end - start, GFP_KERNEL);
  		if (!buf) {
da320f055   Joe Perches   jffs2: Convert pr...
345
346
  			pr_crit("Malloc failure in wbuf recovery. Data loss ensues.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
  
  			goto read_failed;
  		}
  
  		/* Do the read... */
329ad399a   Artem Bityutskiy   mtd: introduce mt...
352
353
  		ret = mtd_read(c->mtd, start, c->wbuf_ofs - start, &retlen,
  			       buf);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
354

9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
355
356
357
  		/* ECC recovered ? */
  		if ((ret == -EUCLEAN || ret == -EBADMSG) &&
  		    (retlen == c->wbuf_ofs - start))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  			ret = 0;
9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
359

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
  		if (ret || retlen != c->wbuf_ofs - start) {
da320f055   Joe Perches   jffs2: Convert pr...
361
362
  			pr_crit("Old data are already lost in wbuf recovery. Data loss ensues.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
366
  
  			kfree(buf);
  			buf = NULL;
  		read_failed:
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
367
368
369
370
371
372
  			first_raw = ref_next(first_raw);
  			nr_refile--;
  			while (first_raw && ref_obsolete(first_raw)) {
  				first_raw = ref_next(first_raw);
  				nr_refile--;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
  			/* If this was the only node to be recovered, give up */
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
374
375
  			if (!first_raw) {
  				c->wbuf_len = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  				return;
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
377
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
  
  			/* It wasn't. Go on and try to recover nodes complete in the wbuf */
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
380
381
382
383
  			start = ref_offset(first_raw);
  			dbg_noderef("wbuf now recover %08x-%08x (%d bytes in %d nodes)
  ",
  				    start, end, end - start, nr_refile);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
388
389
390
  		} else {
  			/* Read succeeded. Copy the remaining data from the wbuf */
  			memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
  		}
  	}
  	/* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
  	   Either 'buf' contains the data, or we find it in the wbuf */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
  	/* ... and get an allocation of space from a shiny new block instead */
9fe4854cd   David Woodhouse   [JFFS2] Remove fl...
392
  	ret = jffs2_reserve_space_gc(c, end-start, &len, JFFS2_SUMMARY_NOSUM_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
  	if (ret) {
da320f055   Joe Perches   jffs2: Convert pr...
394
395
  		pr_warn("Failed to allocate space for wbuf recovery. Data loss ensues.
  ");
9b88f4739   Estelle Hammache   [JFFS2] Code clea...
396
  		kfree(buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
  		return;
  	}
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
399

7f762ab24   Adrian Hunter   [JFFS2] Disable s...
400
401
  	/* The summary is not recovered, so it must be disabled for this erase block */
  	jffs2_sum_disable_collecting(c->summary);
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
402
403
  	ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile);
  	if (ret) {
da320f055   Joe Perches   jffs2: Convert pr...
404
405
  		pr_warn("Failed to allocate node refs for wbuf recovery. Data loss ensues.
  ");
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
406
407
408
  		kfree(buf);
  		return;
  	}
9fe4854cd   David Woodhouse   [JFFS2] Remove fl...
409
  	ofs = write_ofs(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
  	if (end-start >= c->wbuf_pagesize) {
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
411
  		/* Need to do another write immediately, but it's possible
9b88f4739   Estelle Hammache   [JFFS2] Code clea...
412
  		   that this is just because the wbuf itself is completely
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
413
414
  		   full, and there's nothing earlier read back from the
  		   flash. Hence 'buf' isn't necessarily what we're writing
9b88f4739   Estelle Hammache   [JFFS2] Code clea...
415
  		   from. */
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
416
  		unsigned char *rewrite_buf = buf?:c->wbuf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
  		uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
9c261b33a   Joe Perches   jffs2: Convert mo...
418
419
420
  		jffs2_dbg(1, "Write 0x%x bytes at 0x%08x in wbuf recover
  ",
  			  towrite, ofs);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
421

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
  #ifdef BREAKMEHEADER
  		static int breakme;
  		if (breakme++ == 20) {
da320f055   Joe Perches   jffs2: Convert pr...
425
426
  			pr_notice("Faking write error at 0x%08x
  ", ofs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
  			breakme = 0;
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
428
  			mtd_write(c->mtd, ofs, towrite, &retlen, brokenbuf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
  			ret = -EIO;
  		} else
  #endif
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
432
433
  			ret = mtd_write(c->mtd, ofs, towrite, &retlen,
  					rewrite_buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434

a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
435
  		if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
  			/* Argh. We tried. Really we did. */
da320f055   Joe Perches   jffs2: Convert pr...
437
438
  			pr_crit("Recovery of wbuf failed due to a second write error
  ");
9b88f4739   Estelle Hammache   [JFFS2] Code clea...
439
  			kfree(buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440

2f785402f   David Woodhouse   [JFFS2] Reduce vi...
441
  			if (retlen)
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
442
  				jffs2_add_physical_node_ref(c, ofs | REF_OBSOLETE, ref_totlen(c, jeb, first_raw), NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
445
  			return;
  		}
da320f055   Joe Perches   jffs2: Convert pr...
446
447
  		pr_notice("Recovery of wbuf succeeded to %08x
  ", ofs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
  
  		c->wbuf_len = (end - start) - towrite;
  		c->wbuf_ofs = ofs + towrite;
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
451
  		memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
  		/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
454
455
456
  	} else {
  		/* OK, now we're left with the dregs in whichever buffer we're using */
  		if (buf) {
  			memcpy(c->wbuf, buf, end-start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
460
461
462
463
464
465
466
467
  		} else {
  			memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
  		}
  		c->wbuf_ofs = ofs;
  		c->wbuf_len = end - start;
  	}
  
  	/* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
  	new_jeb = &c->blocks[ofs / c->sector_size];
  
  	spin_lock(&c->erase_completion_lock);
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
468
469
470
471
472
473
  	for (raw = first_raw; raw != jeb->last_node; raw = ref_next(raw)) {
  		uint32_t rawlen = ref_totlen(c, jeb, raw);
  		struct jffs2_inode_cache *ic;
  		struct jffs2_raw_node_ref *new_ref;
  		struct jffs2_raw_node_ref **adjust_ref = NULL;
  		struct jffs2_inode_info *f = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474

9c261b33a   Joe Perches   jffs2: Convert mo...
475
476
477
  		jffs2_dbg(1, "Refiling block of %08x at %08x(%d) to %08x
  ",
  			  rawlen, ref_offset(raw), ref_flags(raw), ofs);
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
  
  		ic = jffs2_raw_ref_to_ic(raw);
  
  		/* Ick. This XATTR mess should be fixed shortly... */
  		if (ic && ic->class == RAWNODE_CLASS_XATTR_DATUM) {
  			struct jffs2_xattr_datum *xd = (void *)ic;
  			BUG_ON(xd->node != raw);
  			adjust_ref = &xd->node;
  			raw->next_in_ino = NULL;
  			ic = NULL;
  		} else if (ic && ic->class == RAWNODE_CLASS_XATTR_REF) {
  			struct jffs2_xattr_datum *xr = (void *)ic;
  			BUG_ON(xr->node != raw);
  			adjust_ref = &xr->node;
  			raw->next_in_ino = NULL;
  			ic = NULL;
  		} else if (ic && ic->class == RAWNODE_CLASS_INODE_CACHE) {
  			struct jffs2_raw_node_ref **p = &ic->nodes;
  
  			/* Remove the old node from the per-inode list */
  			while (*p && *p != (void *)ic) {
  				if (*p == raw) {
  					(*p) = (raw->next_in_ino);
  					raw->next_in_ino = NULL;
  					break;
  				}
  				p = &((*p)->next_in_ino);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506

9bfeb691e   David Woodhouse   [JFFS2] Switch to...
507
508
509
510
  			if (ic->state == INO_STATE_PRESENT && !ref_obsolete(raw)) {
  				/* If it's an in-core inode, then we have to adjust any
  				   full_dirent or full_dnode structure to point to the
  				   new version instead of the old */
27c72b040   David Woodhouse   [JFFS2] Track par...
511
  				f = jffs2_gc_fetch_inode(c, ic->ino, !ic->pino_nlink);
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  				if (IS_ERR(f)) {
  					/* Should never happen; it _must_ be present */
  					JFFS2_ERROR("Failed to iget() ino #%u, err %ld
  ",
  						    ic->ino, PTR_ERR(f));
  					BUG();
  				}
  				/* We don't lock f->sem. There's a number of ways we could
  				   end up in here with it already being locked, and nobody's
  				   going to modify it on us anyway because we hold the
  				   alloc_sem. We're only changing one ->raw pointer too,
  				   which we can get away with without upsetting readers. */
  				adjust_ref = jffs2_incore_replace_raw(c, f, raw,
  								      (void *)(buf?:c->wbuf) + (ref_offset(raw) - start));
  			} else if (unlikely(ic->state != INO_STATE_PRESENT &&
  					    ic->state != INO_STATE_CHECKEDABSENT &&
  					    ic->state != INO_STATE_GC)) {
  				JFFS2_ERROR("Inode #%u is in strange state %d!
  ", ic->ino, ic->state);
  				BUG();
  			}
  		}
  
  		new_ref = jffs2_link_node_ref(c, new_jeb, ofs | ref_flags(raw), rawlen, ic);
  
  		if (adjust_ref) {
  			BUG_ON(*adjust_ref != raw);
  			*adjust_ref = new_ref;
  		}
  		if (f)
  			jffs2_gc_release_inode(c, f);
  
  		if (!ref_obsolete(raw)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
547
  			jeb->dirty_size += rawlen;
  			jeb->used_size  -= rawlen;
  			c->dirty_size += rawlen;
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
548
549
550
  			c->used_size -= rawlen;
  			raw->flash_offset = ref_offset(raw) | REF_OBSOLETE;
  			BUG_ON(raw->next_in_ino);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
  		ofs += rawlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
  	}
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
554
  	kfree(buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  	/* Fix up the original jeb now it's on the bad_list */
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
556
  	if (first_raw == jeb->first_node) {
9c261b33a   Joe Perches   jffs2: Convert mo...
557
558
559
  		jffs2_dbg(1, "Failing block at %08x is now empty. Moving to erase_pending_list
  ",
  			  jeb->offset);
f116629d0   Akinobu Mita   [PATCH] fs: use l...
560
  		list_move(&jeb->list, &c->erase_pending_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  		c->nr_erasing_blocks++;
ae3b6ba06   David Woodhouse   jffs2: Use jffs2_...
562
  		jffs2_garbage_collect_trigger(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564

e0c8e42f8   Artem B. Bityutskiy   [JFFS2] Debug cod...
565
  	jffs2_dbg_acct_sanity_check_nolock(c, jeb);
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
566
  	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567

e0c8e42f8   Artem B. Bityutskiy   [JFFS2] Debug cod...
568
  	jffs2_dbg_acct_sanity_check_nolock(c, new_jeb);
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
569
  	jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
  
  	spin_unlock(&c->erase_completion_lock);
9c261b33a   Joe Perches   jffs2: Convert mo...
572
573
574
  	jffs2_dbg(1, "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x
  ",
  		  c->wbuf_ofs, c->wbuf_len);
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
575

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
580
581
582
583
584
585
586
587
588
  }
  
  /* Meaning of pad argument:
     0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway.
     1: Pad, do not adjust nextblock free_size
     2: Pad, adjust nextblock free_size
  */
  #define NOPAD		0
  #define PAD_NOACCOUNT	1
  #define PAD_ACCOUNTING	2
  
  static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
  {
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
589
  	struct jffs2_eraseblock *wbuf_jeb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
  	int ret;
  	size_t retlen;
3be36675d   Andrew Victor   [JFFS2] Core chan...
592
  	/* Nothing to do if not write-buffering the flash. In particular, we shouldn't
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
  	   del_timer() the timer we never initialised. */
3be36675d   Andrew Victor   [JFFS2] Core chan...
594
  	if (!jffs2_is_writebuffered(c))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
  		return 0;
51b11e363   Alexey Khoroshilov   jffs2: use mutex_...
596
  	if (!mutex_is_locked(&c->alloc_sem)) {
da320f055   Joe Perches   jffs2: Convert pr...
597
598
  		pr_crit("jffs2_flush_wbuf() called with alloc_sem not locked!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
600
  		BUG();
  	}
3be36675d   Andrew Victor   [JFFS2] Core chan...
601
  	if (!c->wbuf_len)	/* already checked c->wbuf above */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
  		return 0;
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
603
604
  	wbuf_jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
  	if (jffs2_prealloc_raw_node_refs(c, wbuf_jeb, c->nextblock->allocated_refs + 1))
2f785402f   David Woodhouse   [JFFS2] Reduce vi...
605
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
607
608
609
  	/* claim remaining space on the page
  	   this happens, if we have a change to a new block,
  	   or if fsync forces us to flush the writebuffer.
  	   if we have a switch to next page, we will not have
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
610
  	   enough remaining space for this.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
  	*/
daba5cc4b   Artem B. Bityutskiy   [JFFS2] Fix dataf...
612
  	if (pad ) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
614
615
616
617
  		c->wbuf_len = PAD(c->wbuf_len);
  
  		/* Pad with JFFS2_DIRTY_BITMASK initially.  this helps out ECC'd NOR
  		   with 8 byte page size */
  		memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
618

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
621
622
623
624
625
626
627
628
  		if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
  			struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
  			padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
  			padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
  			padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
  			padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
  		}
  	}
  	/* else jffs2_flash_writev has actually filled in the rest of the
  	   buffer for us, and will deal with the node refs etc. later. */
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
629

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
632
  #ifdef BREAKME
  	static int breakme;
  	if (breakme++ == 20) {
da320f055   Joe Perches   jffs2: Convert pr...
633
634
  		pr_notice("Faking write error at 0x%08x
  ", c->wbuf_ofs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
  		breakme = 0;
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
636
637
  		mtd_write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen,
  			  brokenbuf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
  		ret = -EIO;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
639
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
  #endif
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
641

eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
642
643
  		ret = mtd_write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize,
  				&retlen, c->wbuf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644

a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
645
  	if (ret) {
da320f055   Joe Perches   jffs2: Convert pr...
646
647
  		pr_warn("jffs2_flush_wbuf(): Write failed with %d
  ", ret);
a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
648
649
  		goto wfail;
  	} else if (retlen != c->wbuf_pagesize) {
da320f055   Joe Perches   jffs2: Convert pr...
650
651
652
  		pr_warn("jffs2_flush_wbuf(): Write was short: %zd instead of %d
  ",
  			retlen, c->wbuf_pagesize);
a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
653
654
655
656
  		ret = -EIO;
  		goto wfail;
  	} else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) {
  	wfail:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
658
659
660
  		jffs2_wbuf_recover(c);
  
  		return ret;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661
  	/* Adjust free size of the block if we padded. */
daba5cc4b   Artem B. Bityutskiy   [JFFS2] Fix dataf...
662
  	if (pad) {
0bcc099d6   David Woodhouse   [JFFS2] File node...
663
  		uint32_t waste = c->wbuf_pagesize - c->wbuf_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664

9c261b33a   Joe Perches   jffs2: Convert mo...
665
666
667
668
  		jffs2_dbg(1, "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x
  ",
  			  (wbuf_jeb == c->nextblock) ? "next" : "",
  			  wbuf_jeb->offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669

182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
670
  		/* wbuf_pagesize - wbuf_len is the amount of space that's to be
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671
672
  		   padded. If there is less free space in the block than that,
  		   something screwed up */
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
673
  		if (wbuf_jeb->free_size < waste) {
da320f055   Joe Perches   jffs2: Convert pr...
674
675
676
677
678
679
  			pr_crit("jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.
  ",
  				c->wbuf_ofs, c->wbuf_len, waste);
  			pr_crit("jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x
  ",
  				wbuf_jeb->offset, wbuf_jeb->free_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
681
  			BUG();
  		}
0bcc099d6   David Woodhouse   [JFFS2] File node...
682
683
  
  		spin_lock(&c->erase_completion_lock);
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
684
  		jffs2_link_node_ref(c, wbuf_jeb, (c->wbuf_ofs + c->wbuf_len) | REF_OBSOLETE, waste, NULL);
0bcc099d6   David Woodhouse   [JFFS2] File node...
685
  		/* FIXME: that made it count as dirty. Convert to wasted */
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
686
  		wbuf_jeb->dirty_size -= waste;
0bcc099d6   David Woodhouse   [JFFS2] File node...
687
  		c->dirty_size -= waste;
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
688
  		wbuf_jeb->wasted_size += waste;
0bcc099d6   David Woodhouse   [JFFS2] File node...
689
690
691
  		c->wasted_size += waste;
  	} else
  		spin_lock(&c->erase_completion_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
694
695
696
697
698
699
  
  	/* Stick any now-obsoleted blocks on the erase_pending_list */
  	jffs2_refile_wbuf_blocks(c);
  	jffs2_clear_wbuf_ino_list(c);
  	spin_unlock(&c->erase_completion_lock);
  
  	memset(c->wbuf,0xff,c->wbuf_pagesize);
  	/* adjust write buffer offset, else we get a non contiguous write bug */
5bf172372   Alexander Belyakov   [JFFS2] Write buf...
700
  	c->wbuf_ofs += c->wbuf_pagesize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
703
  	c->wbuf_len = 0;
  	return 0;
  }
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
704
  /* Trigger garbage collection to flush the write-buffer.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
     If ino arg is zero, do it if _any_ real (i.e. not GC) writes are
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
706
     outstanding. If ino arg non-zero, do it only if a write for the
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
709
710
711
712
     given inode is outstanding. */
  int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
  {
  	uint32_t old_wbuf_ofs;
  	uint32_t old_wbuf_len;
  	int ret = 0;
9c261b33a   Joe Perches   jffs2: Convert mo...
713
714
  	jffs2_dbg(1, "jffs2_flush_wbuf_gc() called for ino #%u...
  ", ino);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715

8aee6ac14   David Woodhouse   [JFFS2] Remove NA...
716
717
  	if (!c->wbuf)
  		return 0;
ced220703   David Woodhouse   [JFFS2] semaphore...
718
  	mutex_lock(&c->alloc_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
  	if (!jffs2_wbuf_pending_for_ino(c, ino)) {
9c261b33a   Joe Perches   jffs2: Convert mo...
720
721
  		jffs2_dbg(1, "Ino #%d not pending in wbuf. Returning
  ", ino);
ced220703   David Woodhouse   [JFFS2] semaphore...
722
  		mutex_unlock(&c->alloc_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
725
726
727
728
729
730
  		return 0;
  	}
  
  	old_wbuf_ofs = c->wbuf_ofs;
  	old_wbuf_len = c->wbuf_len;
  
  	if (c->unchecked_size) {
  		/* GC won't make any progress for a while */
9c261b33a   Joe Perches   jffs2: Convert mo...
731
732
733
  		jffs2_dbg(1, "%s(): padding. Not finished checking
  ",
  			  __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
735
  		down_write(&c->wbuf_sem);
  		ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
736
737
738
  		/* retry flushing wbuf in case jffs2_wbuf_recover
  		   left some data in the wbuf */
  		if (ret)
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
739
  			ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
742
  		up_write(&c->wbuf_sem);
  	} else while (old_wbuf_len &&
  		      old_wbuf_ofs == c->wbuf_ofs) {
ced220703   David Woodhouse   [JFFS2] semaphore...
743
  		mutex_unlock(&c->alloc_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744

9c261b33a   Joe Perches   jffs2: Convert mo...
745
746
  		jffs2_dbg(1, "%s(): calls gc pass
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
750
  
  		ret = jffs2_garbage_collect_pass(c);
  		if (ret) {
  			/* GC failed. Flush it with padding instead */
ced220703   David Woodhouse   [JFFS2] semaphore...
751
  			mutex_lock(&c->alloc_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
  			down_write(&c->wbuf_sem);
  			ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
754
755
756
  			/* retry flushing wbuf in case jffs2_wbuf_recover
  			   left some data in the wbuf */
  			if (ret)
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
757
  				ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
759
760
  			up_write(&c->wbuf_sem);
  			break;
  		}
ced220703   David Woodhouse   [JFFS2] semaphore...
761
  		mutex_lock(&c->alloc_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
  	}
9c261b33a   Joe Perches   jffs2: Convert mo...
763
764
  	jffs2_dbg(1, "%s(): ends...
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765

ced220703   David Woodhouse   [JFFS2] semaphore...
766
  	mutex_unlock(&c->alloc_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
768
769
770
771
772
773
  	return ret;
  }
  
  /* Pad write-buffer to end and write it, wasting space. */
  int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
  {
  	int ret;
8aee6ac14   David Woodhouse   [JFFS2] Remove NA...
774
775
  	if (!c->wbuf)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
777
  	down_write(&c->wbuf_sem);
  	ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
778
779
780
  	/* retry - maybe wbuf recover left some data in wbuf. */
  	if (ret)
  		ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
782
783
784
  	up_write(&c->wbuf_sem);
  
  	return ret;
  }
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
785
786
787
  
  static size_t jffs2_fill_wbuf(struct jffs2_sb_info *c, const uint8_t *buf,
  			      size_t len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
  {
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
  	if (len && !c->wbuf_len && (len >= c->wbuf_pagesize))
  		return 0;
  
  	if (len > (c->wbuf_pagesize - c->wbuf_len))
  		len = c->wbuf_pagesize - c->wbuf_len;
  	memcpy(c->wbuf + c->wbuf_len, buf, len);
  	c->wbuf_len += (uint32_t) len;
  	return len;
  }
  
  int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs,
  		       unsigned long count, loff_t to, size_t *retlen,
  		       uint32_t ino)
  {
  	struct jffs2_eraseblock *jeb;
  	size_t wbuf_retlen, donelen = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
  	uint32_t outvec_to = to;
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
806
  	int ret, invec;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807

dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
808
  	/* If not writebuffered flash, don't bother */
3be36675d   Andrew Victor   [JFFS2] Core chan...
809
  	if (!jffs2_is_writebuffered(c))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  		return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
811

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
814
815
816
  	down_write(&c->wbuf_sem);
  
  	/* If wbuf_ofs is not initialized, set it to target address */
  	if (c->wbuf_ofs == 0xFFFFFFFF) {
  		c->wbuf_ofs = PAGE_DIV(to);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
817
  		c->wbuf_len = PAGE_MOD(to);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
819
  		memset(c->wbuf,0xff,c->wbuf_pagesize);
  	}
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
820
  	/*
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
821
822
823
824
825
826
  	 * Sanity checks on target address.  It's permitted to write
  	 * at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to
  	 * write at the beginning of a new erase block. Anything else,
  	 * and you die.  New block starts at xxx000c (0-b = block
  	 * header)
  	 */
3be36675d   Andrew Victor   [JFFS2] Core chan...
827
  	if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
  		/* It's a write to a new block */
  		if (c->wbuf_len) {
9c261b33a   Joe Perches   jffs2: Convert mo...
830
831
832
  			jffs2_dbg(1, "%s(): to 0x%lx causes flush of wbuf at 0x%08x
  ",
  				  __func__, (unsigned long)to, c->wbuf_ofs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
833
  			ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
834
835
  			if (ret)
  				goto outerr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
836
837
838
  		}
  		/* set pointer to new block */
  		c->wbuf_ofs = PAGE_DIV(to);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
839
840
  		c->wbuf_len = PAGE_MOD(to);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
842
843
  
  	if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
  		/* We're not writing immediately after the writebuffer. Bad. */
da320f055   Joe Perches   jffs2: Convert pr...
844
845
846
  		pr_crit("%s(): Non-contiguous write to %08lx
  ",
  			__func__, (unsigned long)to);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
847
  		if (c->wbuf_len)
da320f055   Joe Perches   jffs2: Convert pr...
848
849
850
  			pr_crit("wbuf was previously %08x-%08x
  ",
  				c->wbuf_ofs, c->wbuf_ofs + c->wbuf_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
852
  		BUG();
  	}
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
853
854
855
856
857
858
859
860
861
  	/* adjust alignment offset */
  	if (c->wbuf_len != PAGE_MOD(to)) {
  		c->wbuf_len = PAGE_MOD(to);
  		/* take care of alignment to next page */
  		if (!c->wbuf_len) {
  			c->wbuf_len = c->wbuf_pagesize;
  			ret = __jffs2_flush_wbuf(c, NOPAD);
  			if (ret)
  				goto outerr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
  		}
  	}
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
864
865
866
  	for (invec = 0; invec < count; invec++) {
  		int vlen = invecs[invec].iov_len;
  		uint8_t *v = invecs[invec].iov_base;
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
867

dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
868
  		wbuf_retlen = jffs2_fill_wbuf(c, v, vlen);
7f716cf3f   Estelle Hammache   [JFFS2] Fix block...
869

dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
870
871
872
873
  		if (c->wbuf_len == c->wbuf_pagesize) {
  			ret = __jffs2_flush_wbuf(c, NOPAD);
  			if (ret)
  				goto outerr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
  		}
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
875
876
  		vlen -= wbuf_retlen;
  		outvec_to += wbuf_retlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
  		donelen += wbuf_retlen;
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
878
879
880
  		v += wbuf_retlen;
  
  		if (vlen >= c->wbuf_pagesize) {
eda95cbf7   Artem Bityutskiy   mtd: introduce mt...
881
882
  			ret = mtd_write(c->mtd, outvec_to, PAGE_DIV(vlen),
  					&wbuf_retlen, v);
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
883
884
885
886
887
888
889
890
  			if (ret < 0 || wbuf_retlen != PAGE_DIV(vlen))
  				goto outfile;
  
  			vlen -= wbuf_retlen;
  			outvec_to += wbuf_retlen;
  			c->wbuf_ofs = outvec_to;
  			donelen += wbuf_retlen;
  			v += wbuf_retlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
891
  		}
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
892
893
894
895
896
897
  		wbuf_retlen = jffs2_fill_wbuf(c, v, vlen);
  		if (c->wbuf_len == c->wbuf_pagesize) {
  			ret = __jffs2_flush_wbuf(c, NOPAD);
  			if (ret)
  				goto outerr;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898

dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
899
900
  		outvec_to += wbuf_retlen;
  		donelen += wbuf_retlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902

dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
903
904
905
906
  	/*
  	 * If there's a remainder in the wbuf and it's a non-GC write,
  	 * remember that the wbuf affects this ino
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
  	*retlen = donelen;
e631ddba5   Ferenc Havasi   [JFFS2] Add erase...
908
909
910
911
912
  	if (jffs2_sum_active()) {
  		int res = jffs2_sum_add_kvec(c, invecs, count, (uint32_t) to);
  		if (res)
  			return res;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
913
914
915
916
  	if (c->wbuf_len && ino)
  		jffs2_wbuf_dirties_inode(c, ino);
  
  	ret = 0;
dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
  	up_write(&c->wbuf_sem);
  	return ret;
  
  outfile:
  	/*
  	 * At this point we have no problem, c->wbuf is empty. However
  	 * refile nextblock to avoid writing again to same address.
  	 */
  
  	spin_lock(&c->erase_completion_lock);
  
  	jeb = &c->blocks[outvec_to / c->sector_size];
  	jffs2_block_refile(c, jeb, REFILE_ANYWAY);
  
  	spin_unlock(&c->erase_completion_lock);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
932

dcb093288   Thomas Gleixner   [JFFS2] Simplify ...
933
934
  outerr:
  	*retlen = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
936
937
938
939
940
941
942
  	up_write(&c->wbuf_sem);
  	return ret;
  }
  
  /*
   *	This is the entry for flash write.
   *	Check, if we work on NAND FLASH, if so build an kvec and write it via vritev
  */
9bfeb691e   David Woodhouse   [JFFS2] Switch to...
943
944
  int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
  		      size_t *retlen, const u_char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
945
946
  {
  	struct kvec vecs[1];
3be36675d   Andrew Victor   [JFFS2] Core chan...
947
  	if (!jffs2_is_writebuffered(c))
e631ddba5   Ferenc Havasi   [JFFS2] Add erase...
948
  		return jffs2_flash_direct_write(c, ofs, len, retlen, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
950
951
952
953
954
955
956
957
958
959
960
961
  
  	vecs[0].iov_base = (unsigned char *) buf;
  	vecs[0].iov_len = len;
  	return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0);
  }
  
  /*
  	Handle readback from writebuffer and ECC failure return
  */
  int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
  {
  	loff_t	orbf = 0, owbf = 0, lwbf = 0;
  	int	ret;
3be36675d   Andrew Victor   [JFFS2] Core chan...
962
  	if (!jffs2_is_writebuffered(c))
329ad399a   Artem Bityutskiy   mtd: introduce mt...
963
  		return mtd_read(c->mtd, ofs, len, retlen, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
964

3be36675d   Andrew Victor   [JFFS2] Core chan...
965
  	/* Read flash */
894214d1a   Artem B. Bityuckiy   [JFFS2] Fix race ...
966
  	down_read(&c->wbuf_sem);
329ad399a   Artem Bityutskiy   mtd: introduce mt...
967
  	ret = mtd_read(c->mtd, ofs, len, retlen, buf);
3be36675d   Andrew Victor   [JFFS2] Core chan...
968

9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
969
970
  	if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) {
  		if (ret == -EBADMSG)
da320f055   Joe Perches   jffs2: Convert pr...
971
972
973
  			pr_warn("mtd->read(0x%zx bytes from 0x%llx) returned ECC error
  ",
  				len, ofs);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
974
  		/*
9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
975
976
977
978
979
980
981
  		 * We have the raw data without ECC correction in the buffer,
  		 * maybe we are lucky and all data or parts are correct. We
  		 * check the node.  If data are corrupted node check will sort
  		 * it out.  We keep this block, it will fail on write or erase
  		 * and the we mark it bad. Or should we do that now? But we
  		 * should give him a chance.  Maybe we had a system crash or
  		 * power loss before the ecc write or a erase was completed.
3be36675d   Andrew Victor   [JFFS2] Core chan...
982
983
  		 * So we return success. :)
  		 */
9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
984
  		ret = 0;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
985
  	}
3be36675d   Andrew Victor   [JFFS2] Core chan...
986

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
987
988
  	/* if no writebuffer available or write buffer empty, return */
  	if (!c->wbuf_pagesize || !c->wbuf_len)
894214d1a   Artem B. Bityuckiy   [JFFS2] Fix race ...
989
  		goto exit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
991
  
  	/* if we read in a different block, return */
3be36675d   Andrew Victor   [JFFS2] Core chan...
992
  	if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
894214d1a   Artem B. Bityuckiy   [JFFS2] Fix race ...
993
  		goto exit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
994
995
996
997
998
999
  
  	if (ofs >= c->wbuf_ofs) {
  		owbf = (ofs - c->wbuf_ofs);	/* offset in write buffer */
  		if (owbf > c->wbuf_len)		/* is read beyond write buffer ? */
  			goto exit;
  		lwbf = c->wbuf_len - owbf;	/* number of bytes to copy */
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1000
  		if (lwbf > len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001
  			lwbf = len;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1002
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
1004
1005
  		orbf = (c->wbuf_ofs - ofs);	/* offset in read buffer */
  		if (orbf > len)			/* is write beyond write buffer ? */
  			goto exit;
9a1fcdfd4   Thomas Gleixner   [MTD] NAND Signal...
1006
  		lwbf = len - orbf;		/* number of bytes to copy */
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1007
  		if (lwbf > c->wbuf_len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
  			lwbf = c->wbuf_len;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1009
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1010
1011
1012
1013
1014
1015
1016
  	if (lwbf > 0)
  		memcpy(buf+orbf,c->wbuf+owbf,lwbf);
  
  exit:
  	up_read(&c->wbuf_sem);
  	return ret;
  }
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1017
  #define NR_OOB_SCAN_PAGES 4
09b3fba56   David Woodhouse   [JFFS2] Correct c...
1018
1019
  /* For historical reasons we use only 8 bytes for OOB clean marker */
  #define OOB_CM_SIZE 8
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1020
1021
1022
  
  static const struct jffs2_unknown_node oob_cleanmarker =
  {
566865a2a   David Woodhouse   [JFFS2] Fix cross...
1023
1024
1025
  	.magic = constant_cpu_to_je16(JFFS2_MAGIC_BITMASK),
  	.nodetype = constant_cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
  	.totlen = constant_cpu_to_je32(8)
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1026
  };
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1027

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1028
  /*
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1029
1030
   * Check, if the out of band area is empty. This function knows about the clean
   * marker and if it is present in OOB, treats the OOB as empty anyway.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
   */
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1032
1033
  int jffs2_check_oob_empty(struct jffs2_sb_info *c,
  			  struct jffs2_eraseblock *jeb, int mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1034
  {
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1035
1036
  	int i, ret;
  	int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1037
  	struct mtd_oob_ops ops;
0612b9ddc   Brian Norris   mtd: rename MTD_O...
1038
  	ops.mode = MTD_OPS_AUTO_OOB;
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1039
  	ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1040
  	ops.oobbuf = c->oobbuf;
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1041
  	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1042
  	ops.datbuf = NULL;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1043

fd2819bbc   Artem Bityutskiy   mtd: introduce mt...
1044
  	ret = mtd_read_oob(c->mtd, jeb->offset, &ops);
74d83beaa   Brian Norris   JFFS2: don't fail...
1045
  	if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != ops.ooblen) {
da320f055   Joe Perches   jffs2: Convert pr...
1046
1047
1048
  		pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d
  ",
  		       jeb->offset, ops.ooblen, ops.oobretlen, ret);
74d83beaa   Brian Norris   JFFS2: don't fail...
1049
  		if (!ret || mtd_is_bitflip(ret))
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1050
  			ret = -EIO;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1051
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1052
  	}
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1053

a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1054
1055
1056
  	for(i = 0; i < ops.ooblen; i++) {
  		if (mode && i < cmlen)
  			/* Yeah, we know about the cleanmarker */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
  			continue;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1058
  		if (ops.oobbuf[i] != 0xFF) {
9c261b33a   Joe Perches   jffs2: Convert mo...
1059
1060
1061
  			jffs2_dbg(2, "Found %02x at %x in OOB for "
  				  "%08x
  ", ops.oobbuf[i], i, jeb->offset);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1062
  			return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1063
1064
  		}
  	}
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1065
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1066
1067
1068
  }
  
  /*
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1069
1070
   * Check for a valid cleanmarker.
   * Returns: 0 if a valid cleanmarker was found
ef53cb02f   David Woodhouse   [JFFS2] Whitespac...
1071
1072
   *	    1 if no cleanmarker was found
   *	    negative error code if an error occurred
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1073
   */
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1074
1075
  int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
  				 struct jffs2_eraseblock *jeb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
  {
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1077
  	struct mtd_oob_ops ops;
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1078
  	int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1079

0612b9ddc   Brian Norris   mtd: rename MTD_O...
1080
  	ops.mode = MTD_OPS_AUTO_OOB;
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1081
  	ops.ooblen = cmlen;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1082
  	ops.oobbuf = c->oobbuf;
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1083
  	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1084
  	ops.datbuf = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085

fd2819bbc   Artem Bityutskiy   mtd: introduce mt...
1086
  	ret = mtd_read_oob(c->mtd, jeb->offset, &ops);
74d83beaa   Brian Norris   JFFS2: don't fail...
1087
  	if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != ops.ooblen) {
da320f055   Joe Perches   jffs2: Convert pr...
1088
1089
1090
  		pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d
  ",
  		       jeb->offset, ops.ooblen, ops.oobretlen, ret);
74d83beaa   Brian Norris   JFFS2: don't fail...
1091
  		if (!ret || mtd_is_bitflip(ret))
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1092
  			ret = -EIO;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1093
1094
  		return ret;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095

a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1096
  	return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1097
  }
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1098
1099
  int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
  				 struct jffs2_eraseblock *jeb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1100
  {
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1101
  	int ret;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1102
  	struct mtd_oob_ops ops;
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1103
  	int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1104

0612b9ddc   Brian Norris   mtd: rename MTD_O...
1105
  	ops.mode = MTD_OPS_AUTO_OOB;
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1106
1107
1108
  	ops.ooblen = cmlen;
  	ops.oobbuf = (uint8_t *)&oob_cleanmarker;
  	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1109
  	ops.datbuf = NULL;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1110

a2cc5ba07   Artem Bityutskiy   mtd: introduce mt...
1111
  	ret = mtd_write_oob(c->mtd, jeb->offset, &ops);
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1112
  	if (ret || ops.oobretlen != ops.ooblen) {
da320f055   Joe Perches   jffs2: Convert pr...
1113
1114
1115
  		pr_err("cannot write OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d
  ",
  		       jeb->offset, ops.ooblen, ops.oobretlen, ret);
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1116
1117
  		if (!ret)
  			ret = -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1118
1119
  		return ret;
  	}
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1120

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1121
1122
  	return 0;
  }
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1123
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1124
   * On NAND we try to mark this block bad. If the block was erased more
25985edce   Lucas De Marchi   Fix common misspe...
1125
   * than MAX_ERASE_FAILURES we mark it finally bad.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
   * Don't care about failures. This block remains on the erase-pending
   * or badblock list as long as nobody manipulates the flash with
   * a bootloader or something like that.
   */
  
  int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
  {
  	int 	ret;
  
  	/* if the count is < max, we try to write the counter to the 2nd page oob area */
  	if( ++jeb->bad_count < MAX_ERASE_FAILURES)
  		return 0;
5a528957e   Joe Perches   jffs2: Use pr_fmt...
1138
1139
  	pr_warn("marking eraseblock at %08x as bad
  ", bad_offset);
5942ddbc5   Artem Bityutskiy   mtd: introduce mt...
1140
  	ret = mtd_block_markbad(c->mtd, bad_offset);
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1141

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1142
  	if (ret) {
9c261b33a   Joe Perches   jffs2: Convert mo...
1143
1144
1145
  		jffs2_dbg(1, "%s(): Write failed for block at %08x: error %d
  ",
  			  __func__, jeb->offset, ret);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1146
1147
1148
1149
  		return ret;
  	}
  	return 1;
  }
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1150
1151
1152
  static struct jffs2_sb_info *work_to_sb(struct work_struct *work)
  {
  	struct delayed_work *dwork;
43584c1d4   Geliang Tang   jffs2: use to_del...
1153
  	dwork = to_delayed_work(work);
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1154
1155
1156
1157
1158
1159
1160
  	return container_of(dwork, struct jffs2_sb_info, wbuf_dwork);
  }
  
  static void delayed_wbuf_sync(struct work_struct *work)
  {
  	struct jffs2_sb_info *c = work_to_sb(work);
  	struct super_block *sb = OFNI_BS_2SFFJ(c);
bc98a42c1   David Howells   VFS: Convert sb->...
1161
  	if (!sb_rdonly(sb)) {
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
  		jffs2_dbg(1, "%s()
  ", __func__);
  		jffs2_flush_wbuf_gc(c, 0);
  	}
  }
  
  void jffs2_dirty_trigger(struct jffs2_sb_info *c)
  {
  	struct super_block *sb = OFNI_BS_2SFFJ(c);
  	unsigned long delay;
bc98a42c1   David Howells   VFS: Convert sb->...
1172
  	if (sb_rdonly(sb))
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1173
  		return;
99358a1ca   Al Viro   [jffs2] kill wbuf...
1174
1175
  	delay = msecs_to_jiffies(dirty_writeback_interval * 10);
  	if (queue_delayed_work(system_long_wq, &c->wbuf_dwork, delay))
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1176
1177
  		jffs2_dbg(1, "%s()
  ", __func__);
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1178
  }
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1179
  int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1180
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1181
1182
  	if (!c->mtd->oobsize)
  		return 0;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1183

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1184
1185
  	/* Cleanmarker is out-of-band, so inline size zero */
  	c->cleanmarker_size = 0;
f5b8aa78e   Boris BREZILLON   mtd: kill the ecc...
1186
  	if (c->mtd->oobavail == 0) {
da320f055   Joe Perches   jffs2: Convert pr...
1187
1188
  		pr_err("inconsistent device description
  ");
5bd34c091   Thomas Gleixner   [MTD] NAND Replac...
1189
1190
  		return -EINVAL;
  	}
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1191

5a528957e   Joe Perches   jffs2: Use pr_fmt...
1192
1193
  	jffs2_dbg(1, "using OOB on NAND
  ");
5bd34c091   Thomas Gleixner   [MTD] NAND Replac...
1194

f5b8aa78e   Boris BREZILLON   mtd: kill the ecc...
1195
  	c->oobavail = c->mtd->oobavail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1196
1197
1198
  
  	/* Initialise write buffer */
  	init_rwsem(&c->wbuf_sem);
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1199
  	INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
28318776a   Joern Engel   [MTD] Introduce w...
1200
  	c->wbuf_pagesize = c->mtd->writesize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1201
  	c->wbuf_ofs = 0xFFFFFFFF;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1202

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1203
1204
1205
  	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
  	if (!c->wbuf)
  		return -ENOMEM;
6da2ec560   Kees Cook   treewide: kmalloc...
1206
  	c->oobbuf = kmalloc_array(NR_OOB_SCAN_PAGES, c->oobavail, GFP_KERNEL);
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1207
  	if (!c->oobbuf) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1208
1209
1210
  		kfree(c->wbuf);
  		return -ENOMEM;
  	}
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1211

a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
1212
1213
1214
1215
1216
1217
1218
1219
  #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
  	c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
  	if (!c->wbuf_verify) {
  		kfree(c->oobbuf);
  		kfree(c->wbuf);
  		return -ENOMEM;
  	}
  #endif
a7a6ace14   Artem Bityutskiy   [JFFS2] Use MTD_O...
1220
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1221
1222
1223
1224
  }
  
  void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
  {
a6bc432e2   David Woodhouse   [JFFS2] Add suppo...
1225
1226
1227
  #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
  	kfree(c->wbuf_verify);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1228
  	kfree(c->wbuf);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
1229
  	kfree(c->oobbuf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1230
  }
8f15fd55f   Andrew Victor   [JFFS2] Add suppo...
1231
1232
  int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
  	c->cleanmarker_size = 0;		/* No cleanmarkers needed */
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1233

8f15fd55f   Andrew Victor   [JFFS2] Add suppo...
1234
1235
  	/* Initialize write buffer */
  	init_rwsem(&c->wbuf_sem);
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1236
  	INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
daba5cc4b   Artem B. Bityutskiy   [JFFS2] Fix dataf...
1237
  	c->wbuf_pagesize =  c->mtd->erasesize;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1238

daba5cc4b   Artem B. Bityutskiy   [JFFS2] Fix dataf...
1239
1240
1241
1242
1243
1244
1245
1246
1247
  	/* Find a suitable c->sector_size
  	 * - Not too much sectors
  	 * - Sectors have to be at least 4 K + some bytes
  	 * - All known dataflashes have erase sizes of 528 or 1056
  	 * - we take at least 8 eraseblocks and want to have at least 8K size
  	 * - The concatenation should be a power of 2
  	*/
  
  	c->sector_size = 8 * c->mtd->erasesize;
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1248

daba5cc4b   Artem B. Bityutskiy   [JFFS2] Fix dataf...
1249
1250
1251
  	while (c->sector_size < 8192) {
  		c->sector_size *= 2;
  	}
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1252

daba5cc4b   Artem B. Bityutskiy   [JFFS2] Fix dataf...
1253
1254
  	/* It may be necessary to adjust the flash size */
  	c->flash_size = c->mtd->size;
8f15fd55f   Andrew Victor   [JFFS2] Add suppo...
1255

daba5cc4b   Artem B. Bityutskiy   [JFFS2] Fix dataf...
1256
1257
  	if ((c->flash_size % c->sector_size) != 0) {
  		c->flash_size = (c->flash_size / c->sector_size) * c->sector_size;
5a528957e   Joe Perches   jffs2: Use pr_fmt...
1258
1259
  		pr_warn("flash size adjusted to %dKiB
  ", c->flash_size);
eac44a5e0   Andrew Morton   fs/jffs2/wbuf.c: ...
1260
  	}
182ec4eee   Thomas Gleixner   [JFFS2] Clean up ...
1261

daba5cc4b   Artem B. Bityutskiy   [JFFS2] Fix dataf...
1262
  	c->wbuf_ofs = 0xFFFFFFFF;
8f15fd55f   Andrew Victor   [JFFS2] Add suppo...
1263
1264
1265
  	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
  	if (!c->wbuf)
  		return -ENOMEM;
cca158417   michael   [JFFS2] add write...
1266
1267
1268
  #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
  	c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
  	if (!c->wbuf_verify) {
cca158417   michael   [JFFS2] add write...
1269
1270
1271
1272
  		kfree(c->wbuf);
  		return -ENOMEM;
  	}
  #endif
5a528957e   Joe Perches   jffs2: Use pr_fmt...
1273
1274
  	pr_info("write-buffering enabled buffer (%d) erasesize (%d)
  ",
da320f055   Joe Perches   jffs2: Convert pr...
1275
  		c->wbuf_pagesize, c->sector_size);
8f15fd55f   Andrew Victor   [JFFS2] Add suppo...
1276
1277
1278
1279
1280
  
  	return 0;
  }
  
  void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
cca158417   michael   [JFFS2] add write...
1281
1282
1283
  #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
  	kfree(c->wbuf_verify);
  #endif
8f15fd55f   Andrew Victor   [JFFS2] Add suppo...
1284
1285
  	kfree(c->wbuf);
  }
8f15fd55f   Andrew Victor   [JFFS2] Add suppo...
1286

59da721a2   Nicolas Pitre   [JFFS2] Teach JFF...
1287
  int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) {
c8b229de2   Joern Engel   [MTD] Merge STMic...
1288
1289
1290
  	/* Cleanmarker currently occupies whole programming regions,
  	 * either one or 2 for 8Byte STMicro flashes. */
  	c->cleanmarker_size = max(16u, c->mtd->writesize);
59da721a2   Nicolas Pitre   [JFFS2] Teach JFF...
1291
1292
1293
  
  	/* Initialize write buffer */
  	init_rwsem(&c->wbuf_sem);
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1294
  	INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
28318776a   Joern Engel   [MTD] Introduce w...
1295
  	c->wbuf_pagesize = c->mtd->writesize;
59da721a2   Nicolas Pitre   [JFFS2] Teach JFF...
1296
1297
1298
1299
1300
  	c->wbuf_ofs = 0xFFFFFFFF;
  
  	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
  	if (!c->wbuf)
  		return -ENOMEM;
bc8cec0df   Massimo Cirillo   JFFS2: add missin...
1301
1302
1303
1304
1305
1306
1307
  #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
  	c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
  	if (!c->wbuf_verify) {
  		kfree(c->wbuf);
  		return -ENOMEM;
  	}
  #endif
59da721a2   Nicolas Pitre   [JFFS2] Teach JFF...
1308
1309
1310
1311
  	return 0;
  }
  
  void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) {
bc8cec0df   Massimo Cirillo   JFFS2: add missin...
1312
1313
1314
  #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
  	kfree(c->wbuf_verify);
  #endif
59da721a2   Nicolas Pitre   [JFFS2] Teach JFF...
1315
1316
  	kfree(c->wbuf);
  }
0029da3bf   Artem Bityutskiy   JFFS2: add UBI su...
1317
1318
1319
1320
1321
1322
1323
1324
1325
  
  int jffs2_ubivol_setup(struct jffs2_sb_info *c) {
  	c->cleanmarker_size = 0;
  
  	if (c->mtd->writesize == 1)
  		/* We do not need write-buffer */
  		return 0;
  
  	init_rwsem(&c->wbuf_sem);
8bdc81c50   Artem Bityutskiy   jffs2: get rid of...
1326
  	INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
0029da3bf   Artem Bityutskiy   JFFS2: add UBI su...
1327
1328
1329
1330
1331
1332
  
  	c->wbuf_pagesize =  c->mtd->writesize;
  	c->wbuf_ofs = 0xFFFFFFFF;
  	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
  	if (!c->wbuf)
  		return -ENOMEM;
5a528957e   Joe Perches   jffs2: Use pr_fmt...
1333
1334
  	pr_info("write-buffering enabled buffer (%d) erasesize (%d)
  ",
da320f055   Joe Perches   jffs2: Convert pr...
1335
  		c->wbuf_pagesize, c->sector_size);
0029da3bf   Artem Bityutskiy   JFFS2: add UBI su...
1336
1337
1338
1339
1340
1341
1342
  
  	return 0;
  }
  
  void jffs2_ubivol_cleanup(struct jffs2_sb_info *c) {
  	kfree(c->wbuf);
  }