xref: /netbsd-src/sys/ufs/chfs/ebh.c (revision 832e988b9651e3e836291d95b2e32c8c2347de45)
1*832e988bSandvar /*	$NetBSD: ebh.c,v 1.11 2025/01/08 11:39:50 andvar Exp $	*/
2288addd0Sahoka 
3288addd0Sahoka /*-
4288addd0Sahoka  * Copyright (c) 2010 Department of Software Engineering,
5288addd0Sahoka  *		      University of Szeged, Hungary
6288addd0Sahoka  * Copyright (C) 2009 Ferenc Havasi <havasi@inf.u-szeged.hu>
7288addd0Sahoka  * Copyright (C) 2009 Zoltan Sogor <weth@inf.u-szeged.hu>
8288addd0Sahoka  * Copyright (C) 2009 David Tengeri <dtengeri@inf.u-szeged.hu>
9288addd0Sahoka  * Copyright (C) 2009 Tamas Toth <ttoth@inf.u-szeged.hu>
10288addd0Sahoka  * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
11288addd0Sahoka  * All rights reserved.
12288addd0Sahoka  *
13288addd0Sahoka  * This code is derived from software contributed to The NetBSD Foundation
14288addd0Sahoka  * by the Department of Software Engineering, University of Szeged, Hungary
15288addd0Sahoka  *
16288addd0Sahoka  * Redistribution and use in source and binary forms, with or without
17288addd0Sahoka  * modification, are permitted provided that the following conditions
18288addd0Sahoka  * are met:
19288addd0Sahoka  * 1. Redistributions of source code must retain the above copyright
20288addd0Sahoka  *    notice, this list of conditions and the following disclaimer.
21288addd0Sahoka  * 2. Redistributions in binary form must reproduce the above copyright
22288addd0Sahoka  *    notice, this list of conditions and the following disclaimer in the
23288addd0Sahoka  *    documentation and/or other materials provided with the distribution.
24288addd0Sahoka  *
25288addd0Sahoka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26288addd0Sahoka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27288addd0Sahoka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28288addd0Sahoka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29288addd0Sahoka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30288addd0Sahoka  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31288addd0Sahoka  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32288addd0Sahoka  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33288addd0Sahoka  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34288addd0Sahoka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35288addd0Sahoka  * SUCH DAMAGE.
36288addd0Sahoka  */
37288addd0Sahoka 
38288addd0Sahoka #include "ebh.h"
39288addd0Sahoka 
40288addd0Sahoka /*****************************************************************************/
41288addd0Sahoka /* Flash specific operations						     */
42288addd0Sahoka /*****************************************************************************/
43288addd0Sahoka int nor_create_eb_hdr(struct chfs_eb_hdr *ebhdr, int lnr);
44288addd0Sahoka int nand_create_eb_hdr(struct chfs_eb_hdr *ebhdr, int lnr);
45288addd0Sahoka int nor_calc_data_offs(struct chfs_ebh *ebh, int pebnr, int offset);
46288addd0Sahoka int nand_calc_data_offs(struct chfs_ebh *ebh, int pebnr, int offset);
47288addd0Sahoka int nor_read_eb_hdr(struct chfs_ebh *ebh, int pebnr, struct chfs_eb_hdr *ebhdr);
48288addd0Sahoka int nand_read_eb_hdr(struct chfs_ebh *ebh, int pebnr, struct chfs_eb_hdr *ebhdr);
49288addd0Sahoka int nor_write_eb_hdr(struct chfs_ebh *ebh, int pebnr, struct chfs_eb_hdr *ebhdr);
50288addd0Sahoka int nand_write_eb_hdr(struct chfs_ebh *ebh, int pebnr,struct chfs_eb_hdr *ebhdr);
51288addd0Sahoka int nor_check_eb_hdr(struct chfs_ebh *ebh, void *buf);
52288addd0Sahoka int nand_check_eb_hdr(struct chfs_ebh *ebh, void *buf);
53288addd0Sahoka int nor_mark_eb_hdr_dirty_flash(struct chfs_ebh *ebh, int pebnr, int lid);
54288addd0Sahoka int nor_invalidate_eb_hdr(struct chfs_ebh *ebh, int pebnr);
55288addd0Sahoka int mark_eb_hdr_free(struct chfs_ebh *ebh, int pebnr, int ec);
56288addd0Sahoka 
57288addd0Sahoka int ltree_entry_cmp(struct chfs_ltree_entry *le1, struct chfs_ltree_entry *le2);
58288addd0Sahoka int peb_in_use_cmp(struct chfs_peb *peb1, struct chfs_peb *peb2);
59288addd0Sahoka int peb_free_cmp(struct chfs_peb *peb1, struct chfs_peb *peb2);
60288addd0Sahoka int add_peb_to_erase_queue(struct chfs_ebh *ebh, int pebnr, int ec,struct peb_queue *queue);
61288addd0Sahoka struct chfs_peb * find_peb_in_use(struct chfs_ebh *ebh, int pebnr);
62288addd0Sahoka int add_peb_to_free(struct chfs_ebh *ebh, int pebnr, int ec);
63288addd0Sahoka int add_peb_to_in_use(struct chfs_ebh *ebh, int pebnr, int ec);
64288addd0Sahoka void erase_callback(struct flash_erase_instruction *ei);
65288addd0Sahoka int free_peb(struct chfs_ebh *ebh);
66288addd0Sahoka int release_peb(struct chfs_ebh *ebh, int pebnr);
67288addd0Sahoka void erase_thread(void *data);
68288addd0Sahoka static void erase_thread_start(struct chfs_ebh *ebh);
69288addd0Sahoka static void erase_thread_stop(struct chfs_ebh *ebh);
70288addd0Sahoka int scan_leb_used_cmp(struct chfs_scan_leb *sleb1, struct chfs_scan_leb *sleb2);
71288addd0Sahoka int nor_scan_add_to_used(struct chfs_ebh *ebh, struct chfs_scan_info *si,struct chfs_eb_hdr *ebhdr, int pebnr, int leb_status);
72288addd0Sahoka int nor_process_eb(struct chfs_ebh *ebh, struct chfs_scan_info *si,
73288addd0Sahoka     int pebnr, struct chfs_eb_hdr *ebhdr);
74288addd0Sahoka int nand_scan_add_to_used(struct chfs_ebh *ebh, struct chfs_scan_info *si,struct chfs_eb_hdr *ebhdr, int pebnr);
75288addd0Sahoka int nand_process_eb(struct chfs_ebh *ebh, struct chfs_scan_info *si,
76288addd0Sahoka     int pebnr, struct chfs_eb_hdr *ebhdr);
77288addd0Sahoka struct chfs_scan_info *chfs_scan(struct chfs_ebh *ebh);
78288addd0Sahoka void scan_info_destroy(struct chfs_scan_info *si);
79288addd0Sahoka int scan_media(struct chfs_ebh *ebh);
80288addd0Sahoka int get_peb(struct chfs_ebh *ebh);
81288addd0Sahoka /**
82288addd0Sahoka  * nor_create_eb_hdr - creates an eraseblock header for NOR flash
83288addd0Sahoka  * @ebhdr: ebhdr to set
84288addd0Sahoka  * @lnr: LEB number
85288addd0Sahoka  */
86288addd0Sahoka int
87288addd0Sahoka nor_create_eb_hdr(struct chfs_eb_hdr *ebhdr, int lnr)
88288addd0Sahoka {
89288addd0Sahoka 	ebhdr->u.nor_hdr.lid = htole32(lnr);
90288addd0Sahoka 	return 0;
91288addd0Sahoka }
92288addd0Sahoka 
93288addd0Sahoka /**
94288addd0Sahoka  * nand_create_eb_hdr - creates an eraseblock header for NAND flash
95288addd0Sahoka  * @ebhdr: ebhdr to set
96288addd0Sahoka  * @lnr: LEB number
97288addd0Sahoka  */
98288addd0Sahoka int
99288addd0Sahoka nand_create_eb_hdr(struct chfs_eb_hdr *ebhdr, int lnr)
100288addd0Sahoka {
101288addd0Sahoka 	ebhdr->u.nand_hdr.lid = htole32(lnr);
102288addd0Sahoka 	return 0;
103288addd0Sahoka }
104288addd0Sahoka 
105288addd0Sahoka /**
106288addd0Sahoka  * nor_calc_data_offs - calculates data offset on NOR flash
107288addd0Sahoka  * @ebh: chfs eraseblock handler
108288addd0Sahoka  * @pebnr: eraseblock number
109288addd0Sahoka  * @offset: offset within the eraseblock
110288addd0Sahoka  */
111288addd0Sahoka int
112288addd0Sahoka nor_calc_data_offs(struct chfs_ebh *ebh, int pebnr, int offset)
113288addd0Sahoka {
114288addd0Sahoka 	return pebnr * ebh->flash_if->erasesize + offset +
115288addd0Sahoka 	    CHFS_EB_EC_HDR_SIZE + CHFS_EB_HDR_NOR_SIZE;
116288addd0Sahoka }
117288addd0Sahoka 
118288addd0Sahoka /**
119288addd0Sahoka  * nand_calc_data_offs - calculates data offset on NAND flash
120288addd0Sahoka  * @ebh: chfs eraseblock handler
121288addd0Sahoka  * @pebnr: eraseblock number
122288addd0Sahoka  * @offset: offset within the eraseblock
123288addd0Sahoka  */
124288addd0Sahoka int
125288addd0Sahoka nand_calc_data_offs(struct chfs_ebh *ebh, int pebnr, int offset)
126288addd0Sahoka {
127288addd0Sahoka 	return pebnr * ebh->flash_if->erasesize + offset +
128288addd0Sahoka 	    2 * ebh->flash_if->page_size;
129288addd0Sahoka }
130288addd0Sahoka 
131288addd0Sahoka /**
132f273a7a1Sandvar  * nor_read_eb_hdr - read eraseblock header from NOR flash
133288addd0Sahoka  *
134288addd0Sahoka  * @ebh: chfs eraseblock handler
135288addd0Sahoka  * @pebnr: eraseblock number
136288addd0Sahoka  * @ebhdr: whereto store the data
137288addd0Sahoka  *
138288addd0Sahoka  * Reads the eraseblock header from media.
139288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
140288addd0Sahoka  */
141288addd0Sahoka int
142288addd0Sahoka nor_read_eb_hdr(struct chfs_ebh *ebh,
143288addd0Sahoka     int pebnr, struct chfs_eb_hdr *ebhdr)
144288addd0Sahoka {
145288addd0Sahoka 	int ret;
146288addd0Sahoka 	size_t retlen;
147288addd0Sahoka 	off_t ofs = pebnr * ebh->flash_if->erasesize;
148288addd0Sahoka 
149288addd0Sahoka 	KASSERT(pebnr >= 0 && pebnr < ebh->peb_nr);
150288addd0Sahoka 
151288addd0Sahoka 	ret = flash_read(ebh->flash_dev,
152288addd0Sahoka 	    ofs, CHFS_EB_EC_HDR_SIZE,
153288addd0Sahoka 	    &retlen, (unsigned char *) &ebhdr->ec_hdr);
154288addd0Sahoka 
155288addd0Sahoka 	if (ret || retlen != CHFS_EB_EC_HDR_SIZE)
156288addd0Sahoka 		return ret;
157288addd0Sahoka 
158288addd0Sahoka 	ofs += CHFS_EB_EC_HDR_SIZE;
159288addd0Sahoka 	ret = flash_read(ebh->flash_dev,
160288addd0Sahoka 	    ofs, CHFS_EB_HDR_NOR_SIZE,
161288addd0Sahoka 	    &retlen, (unsigned char *) &ebhdr->u.nor_hdr);
162288addd0Sahoka 
163288addd0Sahoka 	if (ret || retlen != CHFS_EB_HDR_NOR_SIZE)
164288addd0Sahoka 		return ret;
165288addd0Sahoka 
166288addd0Sahoka 	return 0;
167288addd0Sahoka }
168288addd0Sahoka 
169288addd0Sahoka /**
170f273a7a1Sandvar  * nand_read_eb_hdr - read eraseblock header from NAND flash
171288addd0Sahoka  *
172288addd0Sahoka  * @ebh: chfs eraseblock handler
173288addd0Sahoka  * @pebnr: eraseblock number
174288addd0Sahoka  * @ebhdr: whereto store the data
175288addd0Sahoka  *
176288addd0Sahoka  * Reads the eraseblock header from media. It is on the first two page.
177288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
178288addd0Sahoka  */
179288addd0Sahoka int
180288addd0Sahoka nand_read_eb_hdr(struct chfs_ebh *ebh, int pebnr,
181288addd0Sahoka     struct chfs_eb_hdr *ebhdr)
182288addd0Sahoka {
183288addd0Sahoka 	int ret;
184288addd0Sahoka 	size_t retlen;
185288addd0Sahoka 	off_t ofs;
186288addd0Sahoka 
187288addd0Sahoka 	KASSERT(pebnr >= 0 && pebnr < ebh->peb_nr);
188288addd0Sahoka 
189288addd0Sahoka 	/* Read erase counter header from the first page. */
190288addd0Sahoka 	ofs = pebnr * ebh->flash_if->erasesize;
191288addd0Sahoka 	ret = flash_read(ebh->flash_dev,
192288addd0Sahoka 	    ofs, CHFS_EB_EC_HDR_SIZE, &retlen,
193288addd0Sahoka 	    (unsigned char *) &ebhdr->ec_hdr);
194288addd0Sahoka 	if (ret || retlen != CHFS_EB_EC_HDR_SIZE)
195288addd0Sahoka 		return ret;
196288addd0Sahoka 
197288addd0Sahoka 	/* Read NAND eraseblock header from the second page */
198288addd0Sahoka 	ofs += ebh->flash_if->page_size;
199288addd0Sahoka 	ret = flash_read(ebh->flash_dev,
200288addd0Sahoka 	    ofs, CHFS_EB_HDR_NAND_SIZE, &retlen,
201288addd0Sahoka 	    (unsigned char *) &ebhdr->u.nand_hdr);
202288addd0Sahoka 	if (ret || retlen != CHFS_EB_HDR_NAND_SIZE)
203288addd0Sahoka 		return ret;
204288addd0Sahoka 
205288addd0Sahoka 	return 0;
206288addd0Sahoka }
207288addd0Sahoka 
208288addd0Sahoka /**
209f273a7a1Sandvar  * nor_write_eb_hdr - write eraseblock header to NOR flash
210288addd0Sahoka  *
211288addd0Sahoka  * @ebh: chfs eraseblock handler
212288addd0Sahoka  * @pebnr: eraseblock number whereto write
213288addd0Sahoka  * @ebh: ebh to write
214288addd0Sahoka  *
215288addd0Sahoka  * Writes the eraseblock header to media.
216288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
217288addd0Sahoka  */
218288addd0Sahoka int
219288addd0Sahoka nor_write_eb_hdr(struct chfs_ebh *ebh, int pebnr, struct chfs_eb_hdr *ebhdr)
220288addd0Sahoka {
221288addd0Sahoka 	int ret, crc;
222288addd0Sahoka 	size_t retlen;
223288addd0Sahoka 
224288addd0Sahoka 	off_t ofs = pebnr * ebh->flash_if->erasesize + CHFS_EB_EC_HDR_SIZE;
225288addd0Sahoka 
226288addd0Sahoka 	ebhdr->u.nor_hdr.lid = ebhdr->u.nor_hdr.lid
227288addd0Sahoka 	    | htole32(CHFS_LID_NOT_DIRTY_BIT);
228288addd0Sahoka 
229288addd0Sahoka 	crc = crc32(0, (uint8_t *)&ebhdr->u.nor_hdr + 4,
230288addd0Sahoka 	    CHFS_EB_HDR_NOR_SIZE - 4);
231288addd0Sahoka 	ebhdr->u.nand_hdr.crc = htole32(crc);
232288addd0Sahoka 
233288addd0Sahoka 	KASSERT(pebnr >= 0 && pebnr < ebh->peb_nr);
234288addd0Sahoka 
235288addd0Sahoka 	ret = flash_write(ebh->flash_dev,
236288addd0Sahoka 	    ofs, CHFS_EB_HDR_NOR_SIZE, &retlen,
237288addd0Sahoka 	    (unsigned char *) &ebhdr->u.nor_hdr);
238288addd0Sahoka 
239288addd0Sahoka 	if (ret || retlen != CHFS_EB_HDR_NOR_SIZE)
240288addd0Sahoka 		return ret;
241288addd0Sahoka 
242288addd0Sahoka 	return 0;
243288addd0Sahoka }
244288addd0Sahoka 
245288addd0Sahoka /**
246f273a7a1Sandvar  * nand_write_eb_hdr - write eraseblock header to NAND flash
247288addd0Sahoka  *
248288addd0Sahoka  * @ebh: chfs eraseblock handler
249288addd0Sahoka  * @pebnr: eraseblock number whereto write
250288addd0Sahoka  * @ebh: ebh to write
251288addd0Sahoka  *
252288addd0Sahoka  * Writes the eraseblock header to media.
253288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
254288addd0Sahoka  */
255288addd0Sahoka int
256288addd0Sahoka nand_write_eb_hdr(struct chfs_ebh *ebh, int pebnr,
257288addd0Sahoka     struct chfs_eb_hdr *ebhdr)
258288addd0Sahoka {
259288addd0Sahoka 	int ret, crc;
260288addd0Sahoka 	size_t retlen;
261288addd0Sahoka 	flash_off_t ofs;
262288addd0Sahoka 
263288addd0Sahoka 	KASSERT(pebnr >= 0 && pebnr < ebh->peb_nr);
264288addd0Sahoka 
265288addd0Sahoka 	ofs = pebnr * ebh->flash_if->erasesize +
266288addd0Sahoka 	    ebh->flash_if->page_size;
267288addd0Sahoka 
268288addd0Sahoka 	ebhdr->u.nand_hdr.serial = htole64(++(*ebh->max_serial));
269288addd0Sahoka 
270288addd0Sahoka 	crc = crc32(0, (uint8_t *)&ebhdr->u.nand_hdr + 4,
271288addd0Sahoka 	    CHFS_EB_HDR_NAND_SIZE - 4);
272288addd0Sahoka 	ebhdr->u.nand_hdr.crc = htole32(crc);
273288addd0Sahoka 
274288addd0Sahoka 	ret = flash_write(ebh->flash_dev, ofs,
275288addd0Sahoka 	    CHFS_EB_HDR_NAND_SIZE, &retlen,
276288addd0Sahoka 	    (unsigned char *) &ebhdr->u.nand_hdr);
277288addd0Sahoka 
278288addd0Sahoka 	if (ret || retlen != CHFS_EB_HDR_NAND_SIZE)
279288addd0Sahoka 		return ret;
280288addd0Sahoka 
281288addd0Sahoka 	return 0;
282288addd0Sahoka }
283288addd0Sahoka 
284288addd0Sahoka /**
285f273a7a1Sandvar  * nor_check_eb_hdr - check eraseblock header read from NOR flash
286288addd0Sahoka  *
287288addd0Sahoka  * @ebh: chfs eraseblock handler
288288addd0Sahoka  * @buf: eraseblock header to check
289288addd0Sahoka  *
290288addd0Sahoka  * Returns eraseblock header status.
291288addd0Sahoka  */
292288addd0Sahoka int
293288addd0Sahoka nor_check_eb_hdr(struct chfs_ebh *ebh, void *buf)
294288addd0Sahoka {
295288addd0Sahoka 	uint32_t magic, crc, hdr_crc;
296288addd0Sahoka 	struct chfs_eb_hdr *ebhdr = buf;
297288addd0Sahoka 	le32 lid_save;
298288addd0Sahoka 
299288addd0Sahoka 	//check is there a header
300288addd0Sahoka 	if (check_pattern((void *) &ebhdr->ec_hdr,
301288addd0Sahoka 		0xFF, 0, CHFS_EB_EC_HDR_SIZE)) {
302288addd0Sahoka 		dbg_ebh("no header found\n");
303288addd0Sahoka 		return EBHDR_LEB_NO_HDR;
304288addd0Sahoka 	}
305288addd0Sahoka 
306288addd0Sahoka 	// check magic
307288addd0Sahoka 	magic = le32toh(ebhdr->ec_hdr.magic);
308288addd0Sahoka 	if (magic != CHFS_MAGIC_BITMASK) {
309288addd0Sahoka 		dbg_ebh("bad magic bitmask(exp: %x found %x)\n",
310288addd0Sahoka 		    CHFS_MAGIC_BITMASK, magic);
311288addd0Sahoka 		return EBHDR_LEB_BADMAGIC;
312288addd0Sahoka 	}
313288addd0Sahoka 
314288addd0Sahoka 	// check CRC_EC
315288addd0Sahoka 	hdr_crc = le32toh(ebhdr->ec_hdr.crc_ec);
316288addd0Sahoka 	crc = crc32(0, (uint8_t *) &ebhdr->ec_hdr + 8, 4);
317288addd0Sahoka 	if (hdr_crc != crc) {
318288addd0Sahoka 		dbg_ebh("bad crc_ec found\n");
319288addd0Sahoka 		return EBHDR_LEB_BADCRC;
320288addd0Sahoka 	}
321288addd0Sahoka 
322288addd0Sahoka 	/* check if the PEB is free: magic, crc_ec and erase_cnt is good and
323288addd0Sahoka 	 * everything else is FFF..
324288addd0Sahoka 	 */
325288addd0Sahoka 	if (check_pattern((void *) &ebhdr->u.nor_hdr, 0xFF, 0,
326288addd0Sahoka 		CHFS_EB_HDR_NOR_SIZE)) {
327288addd0Sahoka 		dbg_ebh("free peb found\n");
328288addd0Sahoka 		return EBHDR_LEB_FREE;
329288addd0Sahoka 	}
330288addd0Sahoka 
331288addd0Sahoka 	// check invalidated (CRC == LID == 0)
332288addd0Sahoka 	if (ebhdr->u.nor_hdr.crc == 0 && ebhdr->u.nor_hdr.lid == 0) {
333288addd0Sahoka 		dbg_ebh("invalidated ebhdr found\n");
334288addd0Sahoka 		return EBHDR_LEB_INVALIDATED;
335288addd0Sahoka 	}
336288addd0Sahoka 
337288addd0Sahoka 	// check CRC
338288addd0Sahoka 	hdr_crc = le32toh(ebhdr->u.nor_hdr.crc);
339288addd0Sahoka 	lid_save = ebhdr->u.nor_hdr.lid;
340288addd0Sahoka 
341288addd0Sahoka 	// mark lid as not dirty for crc calc
342288addd0Sahoka 	ebhdr->u.nor_hdr.lid = ebhdr->u.nor_hdr.lid | htole32(
343288addd0Sahoka 		CHFS_LID_NOT_DIRTY_BIT);
344288addd0Sahoka 	crc = crc32(0, (uint8_t *) &ebhdr->u.nor_hdr + 4,
345288addd0Sahoka 	    CHFS_EB_HDR_NOR_SIZE - 4);
346288addd0Sahoka 	// restore the original lid value in ebh
347288addd0Sahoka 	ebhdr->u.nor_hdr.lid = lid_save;
348288addd0Sahoka 
349288addd0Sahoka 	if (crc != hdr_crc) {
350288addd0Sahoka 		dbg_ebh("bad crc found\n");
351288addd0Sahoka 		return EBHDR_LEB_BADCRC;
352288addd0Sahoka 	}
353288addd0Sahoka 
354288addd0Sahoka 	// check dirty
355288addd0Sahoka 	if (!(le32toh(lid_save) & CHFS_LID_NOT_DIRTY_BIT)) {
356288addd0Sahoka 		dbg_ebh("dirty ebhdr found\n");
357288addd0Sahoka 		return EBHDR_LEB_DIRTY;
358288addd0Sahoka 	}
359288addd0Sahoka 
360288addd0Sahoka 	return EBHDR_LEB_OK;
361288addd0Sahoka }
362288addd0Sahoka 
363288addd0Sahoka /**
364f273a7a1Sandvar  * nand_check_eb_hdr - check eraseblock header read from NAND flash
365288addd0Sahoka  *
366288addd0Sahoka  * @ebh: chfs eraseblock handler
367288addd0Sahoka  * @buf: eraseblock header to check
368288addd0Sahoka  *
369288addd0Sahoka  * Returns eraseblock header status.
370288addd0Sahoka  */
371288addd0Sahoka int
372288addd0Sahoka nand_check_eb_hdr(struct chfs_ebh *ebh, void *buf)
373288addd0Sahoka {
374288addd0Sahoka 	uint32_t magic, crc, hdr_crc;
375288addd0Sahoka 	struct chfs_eb_hdr *ebhdr = buf;
376288addd0Sahoka 
377288addd0Sahoka 	//check is there a header
378288addd0Sahoka 	if (check_pattern((void *) &ebhdr->ec_hdr,
379288addd0Sahoka 		0xFF, 0, CHFS_EB_EC_HDR_SIZE)) {
380288addd0Sahoka 		dbg_ebh("no header found\n");
381288addd0Sahoka 		return EBHDR_LEB_NO_HDR;
382288addd0Sahoka 	}
383288addd0Sahoka 
384288addd0Sahoka 	// check magic
385288addd0Sahoka 	magic = le32toh(ebhdr->ec_hdr.magic);
386288addd0Sahoka 	if (magic != CHFS_MAGIC_BITMASK) {
387288addd0Sahoka 		dbg_ebh("bad magic bitmask(exp: %x found %x)\n",
388288addd0Sahoka 		    CHFS_MAGIC_BITMASK, magic);
389288addd0Sahoka 		return EBHDR_LEB_BADMAGIC;
390288addd0Sahoka 	}
391288addd0Sahoka 
392288addd0Sahoka 	// check CRC_EC
393288addd0Sahoka 	hdr_crc = le32toh(ebhdr->ec_hdr.crc_ec);
394288addd0Sahoka 	crc = crc32(0, (uint8_t *) &ebhdr->ec_hdr + 8, 4);
395288addd0Sahoka 	if (hdr_crc != crc) {
396288addd0Sahoka 		dbg_ebh("bad crc_ec found\n");
397288addd0Sahoka 		return EBHDR_LEB_BADCRC;
398288addd0Sahoka 	}
399288addd0Sahoka 
400288addd0Sahoka 	/* check if the PEB is free: magic, crc_ec and erase_cnt is good and
401288addd0Sahoka 	 * everything else is FFF..
402288addd0Sahoka 	 */
403288addd0Sahoka 	if (check_pattern((void *) &ebhdr->u.nand_hdr, 0xFF, 0,
404288addd0Sahoka 		CHFS_EB_HDR_NAND_SIZE)) {
405288addd0Sahoka 		dbg_ebh("free peb found\n");
406288addd0Sahoka 		return EBHDR_LEB_FREE;
407288addd0Sahoka 	}
408288addd0Sahoka 
409288addd0Sahoka 	// check CRC
410288addd0Sahoka 	hdr_crc = le32toh(ebhdr->u.nand_hdr.crc);
411288addd0Sahoka 
412288addd0Sahoka 	crc = crc32(0, (uint8_t *) &ebhdr->u.nand_hdr + 4,
413288addd0Sahoka 	    CHFS_EB_HDR_NAND_SIZE - 4);
414288addd0Sahoka 
415288addd0Sahoka 	if (crc != hdr_crc) {
416288addd0Sahoka 		dbg_ebh("bad crc found\n");
417288addd0Sahoka 		return EBHDR_LEB_BADCRC;
418288addd0Sahoka 	}
419288addd0Sahoka 
420288addd0Sahoka 	return EBHDR_LEB_OK;
421288addd0Sahoka }
422288addd0Sahoka 
423288addd0Sahoka /**
424f273a7a1Sandvar  * nor_mark_eb_hdr_dirty_flash- mark eraseblock header dirty on NOR flash
425288addd0Sahoka  *
426288addd0Sahoka  * @ebh: chfs eraseblock handler
427288addd0Sahoka  * @pebnr: eraseblock number
428f0a7346dSsnj  * @lid: leb id (its bit number 31 will be set to 0)
429288addd0Sahoka  *
430288addd0Sahoka  * It pulls the CHFS_LID_NOT_DIRTY_BIT to zero on flash.
431288addd0Sahoka  *
432288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
433288addd0Sahoka  */
434288addd0Sahoka int
435288addd0Sahoka nor_mark_eb_hdr_dirty_flash(struct chfs_ebh *ebh, int pebnr, int lid)
436288addd0Sahoka {
437288addd0Sahoka 	int ret;
438288addd0Sahoka 	size_t retlen;
439288addd0Sahoka 	off_t ofs;
440288addd0Sahoka 
441288addd0Sahoka 	/* mark leb id dirty */
442288addd0Sahoka 	lid = htole32(lid & CHFS_LID_DIRTY_BIT_MASK);
443288addd0Sahoka 
444288addd0Sahoka 	/* calculate position */
445288addd0Sahoka 	ofs = pebnr * ebh->flash_if->erasesize + CHFS_EB_EC_HDR_SIZE
446288addd0Sahoka 	    + CHFS_GET_MEMBER_POS(struct chfs_nor_eb_hdr , lid);
447288addd0Sahoka 
448288addd0Sahoka 	ret = flash_write(ebh->flash_dev, ofs, sizeof(lid), &retlen,
449288addd0Sahoka 	    (unsigned char *) &lid);
450288addd0Sahoka 	if (ret || retlen != sizeof(lid)) {
451288addd0Sahoka 		chfs_err("can't mark peb dirty");
452288addd0Sahoka 		return ret;
453288addd0Sahoka 	}
454288addd0Sahoka 
455288addd0Sahoka 	return 0;
456288addd0Sahoka }
457288addd0Sahoka 
458288addd0Sahoka /**
459f273a7a1Sandvar  * nor_invalidate_eb_hdr - invalidate eraseblock header on NOR flash
460288addd0Sahoka  *
461288addd0Sahoka  * @ebh: chfs eraseblock handler
462288addd0Sahoka  * @pebnr: eraseblock number
463288addd0Sahoka  *
464288addd0Sahoka  * Sets crc and lip field to zero.
465288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
466288addd0Sahoka  */
467288addd0Sahoka int
468288addd0Sahoka nor_invalidate_eb_hdr(struct chfs_ebh *ebh, int pebnr)
469288addd0Sahoka {
470288addd0Sahoka 	int ret;
471288addd0Sahoka 	size_t retlen;
472288addd0Sahoka 	off_t ofs;
473288addd0Sahoka 	char zero_buf[CHFS_INVALIDATE_SIZE];
474288addd0Sahoka 
475288addd0Sahoka 	/* fill with zero */
476288addd0Sahoka 	memset(zero_buf, 0x0, CHFS_INVALIDATE_SIZE);
477288addd0Sahoka 
478288addd0Sahoka 	/* calculate position (!!! lid is directly behind crc !!!) */
479288addd0Sahoka 	ofs = pebnr * ebh->flash_if->erasesize + CHFS_EB_EC_HDR_SIZE
480288addd0Sahoka 	    + CHFS_GET_MEMBER_POS(struct chfs_nor_eb_hdr, crc);
481288addd0Sahoka 
482288addd0Sahoka 	ret = flash_write(ebh->flash_dev,
483288addd0Sahoka 	    ofs, CHFS_INVALIDATE_SIZE, &retlen,
484288addd0Sahoka 	    (unsigned char *) &zero_buf);
485288addd0Sahoka 	if (ret || retlen != CHFS_INVALIDATE_SIZE) {
486288addd0Sahoka 		chfs_err("can't invalidate peb");
487288addd0Sahoka 		return ret;
488288addd0Sahoka 	}
489288addd0Sahoka 
490288addd0Sahoka 	return 0;
491288addd0Sahoka }
492288addd0Sahoka 
493288addd0Sahoka /**
494f273a7a1Sandvar  * mark_eb_hdr_free - free eraseblock header on NOR or NAND flash
495288addd0Sahoka  *
496288addd0Sahoka  * @ebh: chfs eraseblock handler
497288addd0Sahoka  * @pebnr: eraseblock number
498288addd0Sahoka  * @ec: erase counter of PEB
499288addd0Sahoka  *
500288addd0Sahoka  * Write out the magic and erase counter to the physical eraseblock.
501288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
502288addd0Sahoka  */
503288addd0Sahoka int
504288addd0Sahoka mark_eb_hdr_free(struct chfs_ebh *ebh, int pebnr, int ec)
505288addd0Sahoka {
506288addd0Sahoka 	int ret, crc;
507288addd0Sahoka 	size_t retlen;
508288addd0Sahoka 	off_t ofs;
509288addd0Sahoka 	struct chfs_eb_hdr *ebhdr;
510288addd0Sahoka 	ebhdr = kmem_alloc(sizeof(struct chfs_eb_hdr), KM_SLEEP);
511288addd0Sahoka 
512288addd0Sahoka 	ebhdr->ec_hdr.magic = htole32(CHFS_MAGIC_BITMASK);
513288addd0Sahoka 	ebhdr->ec_hdr.erase_cnt = htole32(ec);
514288addd0Sahoka 	crc = crc32(0, (uint8_t *) &ebhdr->ec_hdr + 8, 4);
515288addd0Sahoka 	ebhdr->ec_hdr.crc_ec = htole32(crc);
516288addd0Sahoka 
517288addd0Sahoka 	ofs = pebnr * ebh->flash_if->erasesize;
518288addd0Sahoka 
519288addd0Sahoka 	KASSERT(sizeof(ebhdr->ec_hdr) == CHFS_EB_EC_HDR_SIZE);
520288addd0Sahoka 
521288addd0Sahoka 	ret = flash_write(ebh->flash_dev,
522288addd0Sahoka 	    ofs, CHFS_EB_EC_HDR_SIZE, &retlen,
523288addd0Sahoka 	    (unsigned char *) &ebhdr->ec_hdr);
524288addd0Sahoka 
525288addd0Sahoka 	if (ret || retlen != CHFS_EB_EC_HDR_SIZE) {
526288addd0Sahoka 		chfs_err("can't mark peb as free: %d\n", pebnr);
527288addd0Sahoka 		kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
528288addd0Sahoka 		return ret;
529288addd0Sahoka 	}
530288addd0Sahoka 
531288addd0Sahoka 	kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
532288addd0Sahoka 	return 0;
533288addd0Sahoka }
534288addd0Sahoka 
535288addd0Sahoka /*****************************************************************************/
536288addd0Sahoka /* End of Flash specific operations					     */
537288addd0Sahoka /*****************************************************************************/
538288addd0Sahoka 
539288addd0Sahoka /*****************************************************************************/
540288addd0Sahoka /* Lock Tree								     */
541288addd0Sahoka /*****************************************************************************/
542288addd0Sahoka 
543288addd0Sahoka int
544288addd0Sahoka ltree_entry_cmp(struct chfs_ltree_entry *le1,
545288addd0Sahoka     struct chfs_ltree_entry *le2)
546288addd0Sahoka {
547288addd0Sahoka 	return (le1->lnr - le2->lnr);
548288addd0Sahoka }
549288addd0Sahoka 
550288addd0Sahoka /* Generate functions for Lock tree's red-black tree */
551288addd0Sahoka RB_PROTOTYPE( ltree_rbtree, chfs_ltree_entry, rb, ltree_entry_cmp);
552288addd0Sahoka RB_GENERATE( ltree_rbtree, chfs_ltree_entry, rb, ltree_entry_cmp);
553288addd0Sahoka 
554288addd0Sahoka 
555288addd0Sahoka /**
556288addd0Sahoka  * ltree_lookup - looks up a logical eraseblock in the lock tree
557288addd0Sahoka  * @ebh: chfs eraseblock handler
558288addd0Sahoka  * @lid: identifier of the logical eraseblock
559288addd0Sahoka  *
560288addd0Sahoka  * This function returns a pointer to the wanted &struct chfs_ltree_entry
561288addd0Sahoka  * if the logical eraseblock is in the lock tree, so it is locked, NULL
562288addd0Sahoka  * otherwise.
563288addd0Sahoka  * @ebh->ltree_lock has to be locked!
564288addd0Sahoka  */
565288addd0Sahoka static struct chfs_ltree_entry *
566288addd0Sahoka ltree_lookup(struct chfs_ebh *ebh, int lnr)
567288addd0Sahoka {
568288addd0Sahoka 	struct chfs_ltree_entry le, *result;
569288addd0Sahoka 	le.lnr = lnr;
570288addd0Sahoka 	result = RB_FIND(ltree_rbtree, &ebh->ltree, &le);
571288addd0Sahoka 	return result;
572288addd0Sahoka }
573288addd0Sahoka 
574288addd0Sahoka /**
575288addd0Sahoka  * ltree_add_entry - add an entry to the lock tree
576288addd0Sahoka  * @ebh: chfs eraseblock handler
577288addd0Sahoka  * @lnr: identifier of the logical eraseblock
578288addd0Sahoka  *
579288addd0Sahoka  * This function adds a new logical eraseblock entry identified with @lnr to the
580288addd0Sahoka  * lock tree. If the entry is already in the tree, it increases the user
581288addd0Sahoka  * counter.
582288addd0Sahoka  * Returns NULL if can not allocate memory for lock tree entry, or a pointer
583288addd0Sahoka  * to the inserted entry otherwise.
584288addd0Sahoka  */
585288addd0Sahoka static struct chfs_ltree_entry *
586288addd0Sahoka ltree_add_entry(struct chfs_ebh *ebh, int lnr)
587288addd0Sahoka {
588288addd0Sahoka 	struct chfs_ltree_entry *le, *result;
589288addd0Sahoka 
590288addd0Sahoka 	le = kmem_alloc(sizeof(struct chfs_ltree_entry), KM_SLEEP);
591288addd0Sahoka 
592288addd0Sahoka 	le->lnr = lnr;
593288addd0Sahoka 	le->users = 1;
594288addd0Sahoka 	rw_init(&le->mutex);
595288addd0Sahoka 
596288addd0Sahoka 	//dbg_ebh("enter ltree lock\n");
597288addd0Sahoka 	mutex_enter(&ebh->ltree_lock);
598288addd0Sahoka 	//dbg_ebh("insert\n");
599288addd0Sahoka 	result = RB_INSERT(ltree_rbtree, &ebh->ltree, le);
600288addd0Sahoka 	//dbg_ebh("inserted\n");
601288addd0Sahoka 	if (result) {
602288addd0Sahoka 		//The entry is already in the tree
603288addd0Sahoka 		result->users++;
604288addd0Sahoka 		kmem_free(le, sizeof(struct chfs_ltree_entry));
605288addd0Sahoka 	}
606288addd0Sahoka 	else {
607288addd0Sahoka 		result = le;
608288addd0Sahoka 	}
609288addd0Sahoka 	mutex_exit(&ebh->ltree_lock);
610288addd0Sahoka 
611288addd0Sahoka 	return result;
612288addd0Sahoka }
613288addd0Sahoka 
614288addd0Sahoka /**
615288addd0Sahoka  * leb_read_lock - lock a logical eraseblock for read
616288addd0Sahoka  * @ebh: chfs eraseblock handler
617288addd0Sahoka  * @lnr: identifier of the logical eraseblock
618288addd0Sahoka  *
619288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
620288addd0Sahoka  */
621288addd0Sahoka static int
622288addd0Sahoka leb_read_lock(struct chfs_ebh *ebh, int lnr)
623288addd0Sahoka {
624288addd0Sahoka 	struct chfs_ltree_entry *le;
625288addd0Sahoka 
626288addd0Sahoka 	le = ltree_add_entry(ebh, lnr);
627288addd0Sahoka 	if (!le)
628288addd0Sahoka 		return ENOMEM;
629288addd0Sahoka 
630288addd0Sahoka 	rw_enter(&le->mutex, RW_READER);
631288addd0Sahoka 	return 0;
632288addd0Sahoka }
633288addd0Sahoka 
634288addd0Sahoka /**
635288addd0Sahoka  * leb_read_unlock - unlock a logical eraseblock from read
636288addd0Sahoka  * @ebh: chfs eraseblock handler
637288addd0Sahoka  * @lnr: identifier of the logical eraseblock
638288addd0Sahoka  *
639288addd0Sahoka  * This function unlocks a logical eraseblock from read and delete it from the
640288addd0Sahoka  * lock tree is there are no more users of it.
641288addd0Sahoka  */
642288addd0Sahoka static void
643288addd0Sahoka leb_read_unlock(struct chfs_ebh *ebh, int lnr)
644288addd0Sahoka {
645288addd0Sahoka 	struct chfs_ltree_entry *le;
646288addd0Sahoka 
647288addd0Sahoka 	mutex_enter(&ebh->ltree_lock);
648288addd0Sahoka 	//dbg_ebh("LOCK: ebh->ltree_lock spin locked in leb_read_unlock()\n");
649288addd0Sahoka 	le = ltree_lookup(ebh, lnr);
650288addd0Sahoka 	if (!le)
651288addd0Sahoka 		goto out;
652288addd0Sahoka 
653288addd0Sahoka 	le->users -= 1;
654288addd0Sahoka 	KASSERT(le->users >= 0);
655288addd0Sahoka 	rw_exit(&le->mutex);
656288addd0Sahoka 	if (le->users == 0) {
657288addd0Sahoka 		le = RB_REMOVE(ltree_rbtree, &ebh->ltree, le);
658288addd0Sahoka 		if (le) {
659288addd0Sahoka 			rw_destroy(&le->mutex);
660288addd0Sahoka 
661288addd0Sahoka 			kmem_free(le, sizeof(struct chfs_ltree_entry));
662288addd0Sahoka 		}
663288addd0Sahoka 	}
664288addd0Sahoka 
665288addd0Sahoka out:
666288addd0Sahoka 	mutex_exit(&ebh->ltree_lock);
667288addd0Sahoka 	//dbg_ebh("UNLOCK: ebh->ltree_lock spin unlocked in leb_read_unlock()\n");
668288addd0Sahoka }
669288addd0Sahoka 
670288addd0Sahoka /**
671288addd0Sahoka  * leb_write_lock - lock a logical eraseblock for write
672288addd0Sahoka  * @ebh: chfs eraseblock handler
673288addd0Sahoka  * @lnr: identifier of the logical eraseblock
674288addd0Sahoka  *
675288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
676288addd0Sahoka  */
677288addd0Sahoka static int
678288addd0Sahoka leb_write_lock(struct chfs_ebh *ebh, int lnr)
679288addd0Sahoka {
680288addd0Sahoka 	struct chfs_ltree_entry *le;
681288addd0Sahoka 
682288addd0Sahoka 	le = ltree_add_entry(ebh, lnr);
683288addd0Sahoka 	if (!le)
684288addd0Sahoka 		return ENOMEM;
685288addd0Sahoka 
686288addd0Sahoka 	rw_enter(&le->mutex, RW_WRITER);
687288addd0Sahoka 	return 0;
688288addd0Sahoka }
689288addd0Sahoka 
690288addd0Sahoka /**
691288addd0Sahoka  * leb_write_unlock - unlock a logical eraseblock from write
692288addd0Sahoka  * @ebh: chfs eraseblock handler
693288addd0Sahoka  * @lnr: identifier of the logical eraseblock
694288addd0Sahoka  *
695288addd0Sahoka  * This function unlocks a logical eraseblock from write and delete it from the
696288addd0Sahoka  * lock tree is there are no more users of it.
697288addd0Sahoka  */
698288addd0Sahoka static void
699288addd0Sahoka leb_write_unlock(struct chfs_ebh *ebh, int lnr)
700288addd0Sahoka {
701288addd0Sahoka 	struct chfs_ltree_entry *le;
702288addd0Sahoka 
703288addd0Sahoka 	mutex_enter(&ebh->ltree_lock);
704288addd0Sahoka 	//dbg_ebh("LOCK: ebh->ltree_lock spin locked in leb_write_unlock()\n");
705288addd0Sahoka 	le = ltree_lookup(ebh, lnr);
706288addd0Sahoka 	if (!le)
707288addd0Sahoka 		goto out;
708288addd0Sahoka 
709288addd0Sahoka 	le->users -= 1;
710288addd0Sahoka 	KASSERT(le->users >= 0);
711288addd0Sahoka 	rw_exit(&le->mutex);
712288addd0Sahoka 	if (le->users == 0) {
713288addd0Sahoka 		RB_REMOVE(ltree_rbtree, &ebh->ltree, le);
714288addd0Sahoka 
715288addd0Sahoka 		rw_destroy(&le->mutex);
716288addd0Sahoka 
717288addd0Sahoka 		kmem_free(le, sizeof(struct chfs_ltree_entry));
718288addd0Sahoka 	}
719288addd0Sahoka 
720288addd0Sahoka out:
721288addd0Sahoka 	mutex_exit(&ebh->ltree_lock);
722288addd0Sahoka 	//dbg_ebh("UNLOCK: ebh->ltree_lock spin unlocked in leb_write_unlock()\n");
723288addd0Sahoka }
724288addd0Sahoka 
725288addd0Sahoka /*****************************************************************************/
726288addd0Sahoka /* End of Lock Tree							     */
727288addd0Sahoka /*****************************************************************************/
728288addd0Sahoka 
729288addd0Sahoka /*****************************************************************************/
730288addd0Sahoka /* Erase related operations						     */
731288addd0Sahoka /*****************************************************************************/
732288addd0Sahoka 
733288addd0Sahoka /**
734288addd0Sahoka  * If the first argument is smaller than the second, the function
735288addd0Sahoka  * returns a value smaller than zero. If they are equal, the function re-
736288addd0Sahoka  * turns zero. Otherwise, it should return a value greater than zero.
737288addd0Sahoka  */
738288addd0Sahoka int
739288addd0Sahoka peb_in_use_cmp(struct chfs_peb *peb1, struct chfs_peb *peb2)
740288addd0Sahoka {
741288addd0Sahoka 	return (peb1->pebnr - peb2->pebnr);
742288addd0Sahoka }
743288addd0Sahoka 
744288addd0Sahoka int
745288addd0Sahoka peb_free_cmp(struct chfs_peb *peb1, struct chfs_peb *peb2)
746288addd0Sahoka {
747288addd0Sahoka 	int comp;
748288addd0Sahoka 
749288addd0Sahoka 	comp = peb1->erase_cnt - peb2->erase_cnt;
750288addd0Sahoka 	if (0 == comp)
751288addd0Sahoka 		comp = peb1->pebnr - peb2->pebnr;
752288addd0Sahoka 
753288addd0Sahoka 	return comp;
754288addd0Sahoka }
755288addd0Sahoka 
756288addd0Sahoka /* Generate functions for in use PEB's red-black tree */
757288addd0Sahoka RB_PROTOTYPE(peb_in_use_rbtree, chfs_peb, u.rb, peb_in_use_cmp);
758288addd0Sahoka RB_GENERATE(peb_in_use_rbtree, chfs_peb, u.rb, peb_in_use_cmp);
759288addd0Sahoka RB_PROTOTYPE(peb_free_rbtree, chfs_peb, u.rb, peb_free_cmp);
760288addd0Sahoka RB_GENERATE(peb_free_rbtree, chfs_peb, u.rb, peb_free_cmp);
761288addd0Sahoka 
762288addd0Sahoka /**
763288addd0Sahoka  * add_peb_to_erase_queue: adds a PEB to to_erase/fully_erased queue
764288addd0Sahoka  * @ebh - chfs eraseblock handler
765288addd0Sahoka  * @pebnr - physical eraseblock's number
766288addd0Sahoka  * @ec - erase counter of PEB
767288addd0Sahoka  * @queue: the queue to add to
768288addd0Sahoka  *
769288addd0Sahoka  * This function adds a PEB to the erase queue specified by @queue.
770288addd0Sahoka  * The @ebh->erase_lock must be locked before using this.
771288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
772288addd0Sahoka  */
773288addd0Sahoka int
774288addd0Sahoka add_peb_to_erase_queue(struct chfs_ebh *ebh, int pebnr, int ec,
775288addd0Sahoka     struct peb_queue *queue)
776288addd0Sahoka {
777288addd0Sahoka 	struct chfs_peb *peb;
778288addd0Sahoka 
779288addd0Sahoka 	peb = kmem_alloc(sizeof(struct chfs_peb), KM_SLEEP);
780288addd0Sahoka 
781288addd0Sahoka 	peb->erase_cnt = ec;
782288addd0Sahoka 	peb->pebnr = pebnr;
783288addd0Sahoka 
784288addd0Sahoka 	TAILQ_INSERT_TAIL(queue, peb, u.queue);
785288addd0Sahoka 
786288addd0Sahoka 	return 0;
787288addd0Sahoka 
788288addd0Sahoka }
789288addd0Sahoka //TODO
790288addd0Sahoka /**
791288addd0Sahoka  * find_peb_in_use - looks up a PEB in the RB-tree of used blocks
792288addd0Sahoka  * @ebh - chfs eraseblock handler
793288addd0Sahoka  *
794288addd0Sahoka  * This function returns a pointer to the PEB found in the tree,
795288addd0Sahoka  * NULL otherwise.
796288addd0Sahoka  * The @ebh->erase_lock must be locked before using this.
797288addd0Sahoka  */
798288addd0Sahoka struct chfs_peb *
799288addd0Sahoka find_peb_in_use(struct chfs_ebh *ebh, int pebnr)
800288addd0Sahoka {
801288addd0Sahoka 	struct chfs_peb peb, *result;
802288addd0Sahoka 	peb.pebnr = pebnr;
803288addd0Sahoka 	result = RB_FIND(peb_in_use_rbtree, &ebh->in_use, &peb);
804288addd0Sahoka 	return result;
805288addd0Sahoka }
806288addd0Sahoka 
807288addd0Sahoka /**
808288addd0Sahoka  * add_peb_to_free - adds a PEB to the RB-tree of free PEBs
809288addd0Sahoka  * @ebh - chfs eraseblock handler
810288addd0Sahoka  * @pebnr - physical eraseblock's number
811288addd0Sahoka  * @ec - erase counter of PEB
812288addd0Sahoka  *
813288addd0Sahoka  *
814288addd0Sahoka  * This function adds a physical eraseblock to the RB-tree of free PEBs
815288addd0Sahoka  * stored in the @ebh. The key is the erase counter and pebnr.
816288addd0Sahoka  * The @ebh->erase_lock must be locked before using this.
817288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
818288addd0Sahoka  */
819288addd0Sahoka int
820288addd0Sahoka add_peb_to_free(struct chfs_ebh *ebh, int pebnr, int ec)
821288addd0Sahoka {
822288addd0Sahoka 	struct chfs_peb *peb, *result;
823288addd0Sahoka 
824288addd0Sahoka 	peb = kmem_alloc(sizeof(struct chfs_peb), KM_SLEEP);
825288addd0Sahoka 
826288addd0Sahoka 	peb->erase_cnt = ec;
827288addd0Sahoka 	peb->pebnr = pebnr;
828288addd0Sahoka 	result = RB_INSERT(peb_free_rbtree, &ebh->free, peb);
8297fcec1e8She 	if (result) {
8307fcec1e8She 		kmem_free(peb, sizeof(struct chfs_peb));
831288addd0Sahoka 		return 1;
8327fcec1e8She 	}
833288addd0Sahoka 
834288addd0Sahoka 	return 0;
835288addd0Sahoka }
836288addd0Sahoka 
837288addd0Sahoka /**
838288addd0Sahoka  * add_peb_to_in_use - adds a PEB to the RB-tree of used PEBs
839288addd0Sahoka  * @ebh - chfs eraseblock handler
840288addd0Sahoka  * @pebnr - physical eraseblock's number
841288addd0Sahoka  * @ec - erase counter of PEB
842288addd0Sahoka  *
843288addd0Sahoka  *
844288addd0Sahoka  * This function adds a physical eraseblock to the RB-tree of used PEBs
845288addd0Sahoka  * stored in the @ebh. The key is pebnr.
846288addd0Sahoka  * The @ebh->erase_lock must be locked before using this.
847288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
848288addd0Sahoka  */
849288addd0Sahoka int
850288addd0Sahoka add_peb_to_in_use(struct chfs_ebh *ebh, int pebnr, int ec)
851288addd0Sahoka {
852288addd0Sahoka 	struct chfs_peb *peb, *result;
853288addd0Sahoka 
854288addd0Sahoka 	peb = kmem_alloc(sizeof(struct chfs_peb), KM_SLEEP);
855288addd0Sahoka 
856288addd0Sahoka 	peb->erase_cnt = ec;
857288addd0Sahoka 	peb->pebnr = pebnr;
858288addd0Sahoka 	result = RB_INSERT(peb_in_use_rbtree, &ebh->in_use, peb);
8597fcec1e8She 	if (result) {
8607fcec1e8She 		kmem_free(peb, sizeof(struct chfs_peb));
861288addd0Sahoka 		return 1;
8627fcec1e8She 	}
863288addd0Sahoka 
864288addd0Sahoka 	return 0;
865288addd0Sahoka }
866288addd0Sahoka 
867288addd0Sahoka /**
868288addd0Sahoka  * erase_callback - callback function for flash erase
869288addd0Sahoka  * @ei: erase information
870288addd0Sahoka  */
871288addd0Sahoka void
872288addd0Sahoka erase_callback(struct flash_erase_instruction *ei)
873288addd0Sahoka {
874288addd0Sahoka 	int err;
875288addd0Sahoka 	struct chfs_erase_info_priv *priv = (void *) ei->ei_priv;
876288addd0Sahoka 	//dbg_ebh("ERASE_CALLBACK() CALLED\n");
877288addd0Sahoka 	struct chfs_ebh *ebh = priv->ebh;
878288addd0Sahoka 	struct chfs_peb *peb = priv->peb;
879288addd0Sahoka 
880288addd0Sahoka 	peb->erase_cnt += 1;
881288addd0Sahoka 
882288addd0Sahoka 	if (ei->ei_state == FLASH_ERASE_DONE) {
883288addd0Sahoka 
884288addd0Sahoka 		/* Write out erase counter */
885288addd0Sahoka 		err = ebh->ops->mark_eb_hdr_free(ebh,
886288addd0Sahoka 		    peb->pebnr, peb->erase_cnt);
887288addd0Sahoka 		if (err) {
888288addd0Sahoka 			/* cannot mark PEB as free,so erase it again */
889288addd0Sahoka 			chfs_err(
890288addd0Sahoka 				"cannot mark eraseblock as free, PEB: %d\n",
891288addd0Sahoka 				peb->pebnr);
892288addd0Sahoka 			mutex_enter(&ebh->erase_lock);
893288addd0Sahoka 			/*dbg_ebh("LOCK: ebh->erase_lock spin locked in erase_callback() "
894288addd0Sahoka 			  "after mark ebhdr free\n");*/
895288addd0Sahoka 			add_peb_to_erase_queue(ebh, peb->pebnr, peb->erase_cnt,
896288addd0Sahoka 			    &ebh->to_erase);
897288addd0Sahoka 			mutex_exit(&ebh->erase_lock);
898288addd0Sahoka 			/*dbg_ebh("UNLOCK: ebh->erase_lock spin unlocked in erase_callback() "
899288addd0Sahoka 			  "after mark ebhdr free\n");*/
900288addd0Sahoka 			kmem_free(peb, sizeof(struct chfs_peb));
901288addd0Sahoka 			return;
902288addd0Sahoka 		}
903288addd0Sahoka 
904288addd0Sahoka 		mutex_enter(&ebh->erase_lock);
905288addd0Sahoka 		/*dbg_ebh("LOCK: ebh->erase_lock spin locked in erase_callback()\n");*/
906288addd0Sahoka 		err = add_peb_to_free(ebh, peb->pebnr, peb->erase_cnt);
907288addd0Sahoka 		mutex_exit(&ebh->erase_lock);
908288addd0Sahoka 		/*dbg_ebh("UNLOCK: ebh->erase_lock spin unlocked in erase_callback()\n");*/
909288addd0Sahoka 		kmem_free(peb, sizeof(struct chfs_peb));
910288addd0Sahoka 	} else {
911288addd0Sahoka 		/*
912288addd0Sahoka 		 * Erase is finished, but there was a problem,
913288addd0Sahoka 		 * so erase PEB again
914288addd0Sahoka 		 */
915288addd0Sahoka 		chfs_err("erase failed, state is: 0x%x\n", ei->ei_state);
916288addd0Sahoka 		add_peb_to_erase_queue(ebh, peb->pebnr, peb->erase_cnt, &ebh->to_erase);
917288addd0Sahoka 		kmem_free(peb, sizeof(struct chfs_peb));
918288addd0Sahoka 	}
919288addd0Sahoka }
920288addd0Sahoka 
921288addd0Sahoka /**
922288addd0Sahoka  * free_peb: free a PEB
923288addd0Sahoka  * @ebh: chfs eraseblock handler
924288addd0Sahoka  *
925288addd0Sahoka  * This function erases the first physical eraseblock from one of the erase
926288addd0Sahoka  * lists and adds to the RB-tree of free PEBs.
9273b26fa5cSandvar  * Returns zero in case of success, error code in case of fail.
928288addd0Sahoka  */
929288addd0Sahoka int
930288addd0Sahoka free_peb(struct chfs_ebh *ebh)
931288addd0Sahoka {
932288addd0Sahoka 	int err, retries = 0;
933288addd0Sahoka 	off_t ofs;
934288addd0Sahoka 	struct chfs_peb *peb = NULL;
935288addd0Sahoka 	struct flash_erase_instruction *ei;
936288addd0Sahoka 
937288addd0Sahoka 	KASSERT(mutex_owned(&ebh->erase_lock));
938288addd0Sahoka 
939288addd0Sahoka 	if (!TAILQ_EMPTY(&ebh->fully_erased)) {
940288addd0Sahoka 		//dbg_ebh("[FREE PEB] got a fully erased block\n");
941288addd0Sahoka 		peb = TAILQ_FIRST(&ebh->fully_erased);
942288addd0Sahoka 		TAILQ_REMOVE(&ebh->fully_erased, peb, u.queue);
943288addd0Sahoka 		err = ebh->ops->mark_eb_hdr_free(ebh,
944288addd0Sahoka 		    peb->pebnr, peb->erase_cnt);
945288addd0Sahoka 		if (err) {
946288addd0Sahoka 			goto out_free;
947288addd0Sahoka 		}
948288addd0Sahoka 		err = add_peb_to_free(ebh, peb->pebnr, peb->erase_cnt);
949288addd0Sahoka 		goto out_free;
950288addd0Sahoka 	}
951288addd0Sahoka 	/* Erase PEB */
952*832e988bSandvar 	//dbg_ebh("[FREE PEB] erasing a block\n");
953288addd0Sahoka 	peb = TAILQ_FIRST(&ebh->to_erase);
954288addd0Sahoka 	TAILQ_REMOVE(&ebh->to_erase, peb, u.queue);
955288addd0Sahoka 	mutex_exit(&ebh->erase_lock);
956288addd0Sahoka 	//dbg_ebh("UNLOCK: ebh->erase_lock spin unlocked in free_peb()\n");
957288addd0Sahoka 	ofs = peb->pebnr * ebh->flash_if->erasesize;
958288addd0Sahoka 
959288addd0Sahoka 	/* XXX where do we free this? */
960288addd0Sahoka 	ei = kmem_alloc(sizeof(struct flash_erase_instruction)
961288addd0Sahoka 	    + sizeof(struct chfs_erase_info_priv), KM_SLEEP);
962288addd0Sahoka retry:
963288addd0Sahoka 	memset(ei, 0, sizeof(*ei));
964288addd0Sahoka 
965288addd0Sahoka //	ei->ei_if = ebh->flash_if;
966288addd0Sahoka 	ei->ei_addr = ofs;
967288addd0Sahoka 	ei->ei_len = ebh->flash_if->erasesize;
968288addd0Sahoka 	ei->ei_callback = erase_callback;
969288addd0Sahoka 	ei->ei_priv = (unsigned long) (&ei[1]);
970288addd0Sahoka 
971288addd0Sahoka 	((struct chfs_erase_info_priv *) ei->ei_priv)->ebh = ebh;
972288addd0Sahoka 	((struct chfs_erase_info_priv *) ei->ei_priv)->peb = peb;
973288addd0Sahoka 
974288addd0Sahoka 	err = flash_erase(ebh->flash_dev, ei);
975288addd0Sahoka 	dbg_ebh("erased peb: %d\n", peb->pebnr);
976288addd0Sahoka 
977288addd0Sahoka 	/* einval would mean we did something wrong */
978288addd0Sahoka 	KASSERT(err != EINVAL);
979288addd0Sahoka 
980288addd0Sahoka 	if (err) {
981288addd0Sahoka 		dbg_ebh("errno: %d, ei->ei_state: %d\n", err, ei->ei_state);
982288addd0Sahoka 		if (CHFS_MAX_GET_PEB_RETRIES < ++retries &&
983288addd0Sahoka 		    ei->ei_state == FLASH_ERASE_FAILED) {
984288addd0Sahoka 			/* The block went bad mark it */
985288addd0Sahoka 			dbg_ebh("ebh markbad! 0x%jx\n", (uintmax_t )ofs);
986288addd0Sahoka 			err = flash_block_markbad(ebh->flash_dev, ofs);
987288addd0Sahoka 			if (!err) {
988288addd0Sahoka 				ebh->peb_nr--;
989288addd0Sahoka 			}
990288addd0Sahoka 
991288addd0Sahoka 			goto out;
992288addd0Sahoka 		}
993288addd0Sahoka 		chfs_err("can not erase PEB: %d, try again\n", peb->pebnr);
994288addd0Sahoka 		goto retry;
995288addd0Sahoka 	}
996288addd0Sahoka 
997288addd0Sahoka out:
998288addd0Sahoka 	/* lock the erase_lock, because it was locked
999288addd0Sahoka 	 * when the function was called */
1000288addd0Sahoka 	mutex_enter(&ebh->erase_lock);
1001288addd0Sahoka 	return err;
1002288addd0Sahoka 
1003288addd0Sahoka out_free:
1004288addd0Sahoka 	kmem_free(peb, sizeof(struct chfs_peb));
1005288addd0Sahoka 	return err;
1006288addd0Sahoka }
1007288addd0Sahoka 
1008288addd0Sahoka /**
1009288addd0Sahoka  * release_peb - schedule an erase for the PEB
1010288addd0Sahoka  * @ebh: chfs eraseblock handler
1011288addd0Sahoka  * @pebnr: physical eraseblock number
1012288addd0Sahoka  *
1013288addd0Sahoka  * This function get the peb identified by @pebnr from the in_use RB-tree of
1014288addd0Sahoka  * @ebh, removes it and schedule an erase for it.
1015288addd0Sahoka  *
1016288addd0Sahoka  * Returns zero on success, error code in case of fail.
1017288addd0Sahoka  */
1018288addd0Sahoka int
1019288addd0Sahoka release_peb(struct chfs_ebh *ebh, int pebnr)
1020288addd0Sahoka {
1021288addd0Sahoka 	int err = 0;
1022288addd0Sahoka 	struct chfs_peb *peb;
1023288addd0Sahoka 
1024288addd0Sahoka 	mutex_enter(&ebh->erase_lock);
1025288addd0Sahoka 
1026288addd0Sahoka 	//dbg_ebh("LOCK: ebh->erase_lock spin locked in release_peb()\n");
1027288addd0Sahoka 	peb = find_peb_in_use(ebh, pebnr);
1028288addd0Sahoka 	if (!peb) {
1029288addd0Sahoka 		chfs_err("LEB is mapped, but is not in the 'in_use' "
1030288addd0Sahoka 		    "tree of ebh\n");
1031288addd0Sahoka 		goto out_unlock;
1032288addd0Sahoka 	}
1033288addd0Sahoka 	err = add_peb_to_erase_queue(ebh, peb->pebnr, peb->erase_cnt,
1034288addd0Sahoka 	    &ebh->to_erase);
1035288addd0Sahoka 
1036288addd0Sahoka 	if (err)
1037288addd0Sahoka 		goto out_unlock;
1038288addd0Sahoka 
1039288addd0Sahoka 	RB_REMOVE(peb_in_use_rbtree, &ebh->in_use, peb);
1040288addd0Sahoka out_unlock:
1041288addd0Sahoka 	mutex_exit(&ebh->erase_lock);
1042288addd0Sahoka 	//dbg_ebh("UNLOCK: ebh->erase_lock spin unlocked in release_peb()"
1043288addd0Sahoka 	//		" at out_unlock\n");
1044288addd0Sahoka 	return err;
1045288addd0Sahoka }
1046288addd0Sahoka 
1047288addd0Sahoka /**
1048288addd0Sahoka  * erase_thread - background thread for erasing PEBs
1049288addd0Sahoka  * @data: pointer to the eraseblock handler
1050288addd0Sahoka  */
1051288addd0Sahoka /*void
1052288addd0Sahoka   erase_thread(void *data)
1053288addd0Sahoka   {
1054288addd0Sahoka   struct chfs_ebh *ebh = data;
1055288addd0Sahoka 
1056288addd0Sahoka   dbg_ebh("erase thread started\n");
1057288addd0Sahoka   while (ebh->bg_erase.eth_running) {
1058288addd0Sahoka   int err;
1059288addd0Sahoka 
1060288addd0Sahoka   mutex_enter(&ebh->erase_lock);
1061288addd0Sahoka   dbg_ebh("LOCK: ebh->erase_lock spin locked in erase_thread()\n");
1062288addd0Sahoka   if (TAILQ_EMPTY(&ebh->to_erase) && TAILQ_EMPTY(&ebh->fully_erased)) {
1063288addd0Sahoka   dbg_ebh("thread has nothing to do\n");
1064288addd0Sahoka   mutex_exit(&ebh->erase_lock);
1065288addd0Sahoka   mutex_enter(&ebh->bg_erase.eth_thread_mtx);
1066288addd0Sahoka   cv_timedwait_sig(&ebh->bg_erase.eth_wakeup,
1067288addd0Sahoka   &ebh->bg_erase.eth_thread_mtx, mstohz(100));
1068288addd0Sahoka   mutex_exit(&ebh->bg_erase.eth_thread_mtx);
1069288addd0Sahoka 
1070288addd0Sahoka   dbg_ebh("UNLOCK: ebh->erase_lock spin unlocked in erase_thread()\n");
1071288addd0Sahoka   continue;
1072288addd0Sahoka   }
1073288addd0Sahoka   mutex_exit(&ebh->erase_lock);
1074288addd0Sahoka   dbg_ebh("UNLOCK: ebh->erase_lock spin unlocked in erase_thread()\n");
1075288addd0Sahoka 
1076288addd0Sahoka   err = free_peb(ebh);
1077288addd0Sahoka   if (err)
1078288addd0Sahoka   chfs_err("freeing PEB failed in the background thread: %d\n", err);
1079288addd0Sahoka 
1080288addd0Sahoka   }
1081288addd0Sahoka   dbg_ebh("erase thread stopped\n");
1082288addd0Sahoka   kthread_exit(0);
1083288addd0Sahoka   }*/
1084288addd0Sahoka 
1085288addd0Sahoka /**
1086288addd0Sahoka  * erase_thread - background thread for erasing PEBs
1087288addd0Sahoka  * @data: pointer to the eraseblock handler
1088288addd0Sahoka  */
1089288addd0Sahoka void
1090288addd0Sahoka erase_thread(void *data) {
1091288addd0Sahoka 	dbg_ebh("[EBH THREAD] erase thread started\n");
1092288addd0Sahoka 
1093288addd0Sahoka 	struct chfs_ebh *ebh = data;
1094288addd0Sahoka 	int err;
1095288addd0Sahoka 
1096288addd0Sahoka 	mutex_enter(&ebh->erase_lock);
1097288addd0Sahoka 	while (ebh->bg_erase.eth_running) {
1098288addd0Sahoka 		if (TAILQ_EMPTY(&ebh->to_erase) &&
1099288addd0Sahoka 		    TAILQ_EMPTY(&ebh->fully_erased)) {
1100288addd0Sahoka 			cv_timedwait_sig(&ebh->bg_erase.eth_wakeup,
1101288addd0Sahoka 			    &ebh->erase_lock, mstohz(100));
1102288addd0Sahoka 		} else {
1103288addd0Sahoka 			/* XXX exiting this mutex is a bit odd here as
1104288addd0Sahoka 			 * free_peb instantly reenters it...
1105288addd0Sahoka 			 */
1106288addd0Sahoka 			err = free_peb(ebh);
1107288addd0Sahoka 			mutex_exit(&ebh->erase_lock);
1108288addd0Sahoka 			if (err) {
1109288addd0Sahoka 				chfs_err("freeing PEB failed in the"
1110288addd0Sahoka 				    " background thread: %d\n", err);
1111288addd0Sahoka 			}
1112288addd0Sahoka 			mutex_enter(&ebh->erase_lock);
1113288addd0Sahoka 		}
1114288addd0Sahoka 	}
1115288addd0Sahoka 	mutex_exit(&ebh->erase_lock);
1116288addd0Sahoka 
1117288addd0Sahoka 	dbg_ebh("[EBH THREAD] erase thread stopped\n");
1118288addd0Sahoka 	kthread_exit(0);
1119288addd0Sahoka }
1120288addd0Sahoka 
1121288addd0Sahoka /**
1122288addd0Sahoka  * erase_thread_start - init and start erase thread
1123288addd0Sahoka  * @ebh: eraseblock handler
1124288addd0Sahoka  */
1125288addd0Sahoka static void
1126288addd0Sahoka erase_thread_start(struct chfs_ebh *ebh)
1127288addd0Sahoka {
1128288addd0Sahoka 	cv_init(&ebh->bg_erase.eth_wakeup, "ebheracv");
1129288addd0Sahoka 
1130288addd0Sahoka 	ebh->bg_erase.eth_running = true;
1131288addd0Sahoka 	kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL,
1132288addd0Sahoka 	    erase_thread, ebh, &ebh->bg_erase.eth_thread, "ebherase");
1133288addd0Sahoka }
1134288addd0Sahoka 
1135288addd0Sahoka /**
1136288addd0Sahoka  * erase_thread_stop - stop background erase thread
1137288addd0Sahoka  * @ebh: eraseblock handler
1138288addd0Sahoka  */
1139288addd0Sahoka static void
1140288addd0Sahoka erase_thread_stop(struct chfs_ebh *ebh)
1141288addd0Sahoka {
1142288addd0Sahoka 	ebh->bg_erase.eth_running = false;
1143288addd0Sahoka 	cv_signal(&ebh->bg_erase.eth_wakeup);
1144288addd0Sahoka 	dbg_ebh("[EBH THREAD STOP] signaled\n");
1145288addd0Sahoka 
1146288addd0Sahoka 	kthread_join(ebh->bg_erase.eth_thread);
1147288addd0Sahoka #ifdef BROKEN_KTH_JOIN
1148288addd0Sahoka 	kpause("chfsebhjointh", false, mstohz(1000), NULL);
1149288addd0Sahoka #endif
1150288addd0Sahoka 
1151288addd0Sahoka 	cv_destroy(&ebh->bg_erase.eth_wakeup);
1152288addd0Sahoka }
1153288addd0Sahoka 
1154288addd0Sahoka /*****************************************************************************/
1155288addd0Sahoka /* End of Erase related operations					     */
1156288addd0Sahoka /*****************************************************************************/
1157288addd0Sahoka 
1158288addd0Sahoka /*****************************************************************************/
1159288addd0Sahoka /* Scan related operations						     */
1160288addd0Sahoka /*****************************************************************************/
1161288addd0Sahoka int
1162288addd0Sahoka scan_leb_used_cmp(struct chfs_scan_leb *sleb1, struct chfs_scan_leb *sleb2)
1163288addd0Sahoka {
1164288addd0Sahoka 	return (sleb1->lnr - sleb2->lnr);
1165288addd0Sahoka }
1166288addd0Sahoka 
1167288addd0Sahoka RB_PROTOTYPE(scan_leb_used_rbtree, chfs_scan_leb, u.rb, scan_leb_used_cmp);
1168288addd0Sahoka RB_GENERATE(scan_leb_used_rbtree, chfs_scan_leb, u.rb, scan_leb_used_cmp);
1169288addd0Sahoka 
1170288addd0Sahoka /**
1171288addd0Sahoka  * scan_add_to_queue - adds a physical eraseblock to one of the
1172288addd0Sahoka  *                     eraseblock queue
1173288addd0Sahoka  * @si: chfs scanning information
1174288addd0Sahoka  * @pebnr: physical eraseblock number
1175288addd0Sahoka  * @erase_cnt: erase counter of the physical eraseblock
1176288addd0Sahoka  * @list: the list to add to
1177288addd0Sahoka  *
1178288addd0Sahoka  * This function adds a physical eraseblock to one of the lists in the scanning
1179288addd0Sahoka  * information.
1180288addd0Sahoka  * Returns zero in case of success, negative error code in case of fail.
1181288addd0Sahoka  */
1182288addd0Sahoka static int
1183288addd0Sahoka scan_add_to_queue(struct chfs_scan_info *si, int pebnr, int erase_cnt,
1184288addd0Sahoka     struct scan_leb_queue *queue)
1185288addd0Sahoka {
1186288addd0Sahoka 	struct chfs_scan_leb *sleb;
1187288addd0Sahoka 
1188288addd0Sahoka 	sleb = kmem_alloc(sizeof(struct chfs_scan_leb), KM_SLEEP);
1189288addd0Sahoka 
1190288addd0Sahoka 	sleb->pebnr = pebnr;
1191288addd0Sahoka 	sleb->erase_cnt = erase_cnt;
1192288addd0Sahoka 	TAILQ_INSERT_TAIL(queue, sleb, u.queue);
1193288addd0Sahoka 	return 0;
1194288addd0Sahoka }
1195288addd0Sahoka 
1196288addd0Sahoka /*
1197288addd0Sahoka  * nor_scan_add_to_used - add a physical eraseblock to the
1198288addd0Sahoka  *                        used tree of scan info
1199288addd0Sahoka  * @ebh: chfs eraseblock handler
1200288addd0Sahoka  * @si: chfs scanning information
1201288addd0Sahoka  * @ebhdr: eraseblock header
1202288addd0Sahoka  * @pebnr: physical eraseblock number
1203288addd0Sahoka  * @leb_status: the status of the PEB's eraseblock header
1204288addd0Sahoka  *
1205288addd0Sahoka  * This function adds a PEB to the used tree of the scanning information.
1206288addd0Sahoka  * It handles the situations if there are more physical eraseblock referencing
1207288addd0Sahoka  * to the same logical eraseblock.
1208288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1209288addd0Sahoka  */
1210288addd0Sahoka int
1211288addd0Sahoka nor_scan_add_to_used(struct chfs_ebh *ebh, struct chfs_scan_info *si,
1212288addd0Sahoka     struct chfs_eb_hdr *ebhdr, int pebnr, int leb_status)
1213288addd0Sahoka {
1214288addd0Sahoka 	int err, lnr, ec;
1215288addd0Sahoka 	struct chfs_scan_leb *sleb, *old;
1216288addd0Sahoka 
1217288addd0Sahoka 	lnr = CHFS_GET_LID(ebhdr->u.nor_hdr.lid);
1218288addd0Sahoka 	ec = le32toh(ebhdr->ec_hdr.erase_cnt);
1219288addd0Sahoka 
1220288addd0Sahoka 	sleb = kmem_alloc(sizeof(struct chfs_scan_leb), KM_SLEEP);
1221288addd0Sahoka 
1222288addd0Sahoka 	sleb->erase_cnt = ec;
1223288addd0Sahoka 	sleb->lnr = lnr;
1224288addd0Sahoka 	sleb->pebnr = pebnr;
1225288addd0Sahoka 	sleb->info = leb_status;
1226288addd0Sahoka 
1227288addd0Sahoka 	old = RB_INSERT(scan_leb_used_rbtree, &si->used, sleb);
1228288addd0Sahoka 	if (old) {
1229288addd0Sahoka 		kmem_free(sleb, sizeof(struct chfs_scan_leb));
1230288addd0Sahoka 		/* There is already an eraseblock in the used tree */
1231288addd0Sahoka 		/* If the new one is bad */
1232288addd0Sahoka 		if (EBHDR_LEB_DIRTY == leb_status &&
1233288addd0Sahoka 		    EBHDR_LEB_OK == old->info) {
1234288addd0Sahoka 			return scan_add_to_queue(si, pebnr, ec, &si->erase);
1235288addd0Sahoka 		} else {
1236288addd0Sahoka 			err = scan_add_to_queue(si, old->pebnr,
1237288addd0Sahoka 			    old->erase_cnt, &si->erase);
1238288addd0Sahoka 			if (err) {
1239288addd0Sahoka 				return err;
1240288addd0Sahoka 			}
1241288addd0Sahoka 
1242288addd0Sahoka 			old->erase_cnt = ec;
1243288addd0Sahoka 			old->lnr = lnr;
1244288addd0Sahoka 			old->pebnr = pebnr;
1245288addd0Sahoka 			old->info = leb_status;
1246288addd0Sahoka 			return 0;
1247288addd0Sahoka 		}
1248288addd0Sahoka 	}
1249288addd0Sahoka 	return 0;
1250288addd0Sahoka }
1251288addd0Sahoka 
1252288addd0Sahoka /**
1253288addd0Sahoka  * nor_process eb -read the headers from NOR flash, check them and add to
1254288addd0Sahoka  * 				   the scanning information
1255288addd0Sahoka  * @ebh: chfs eraseblock handler
1256288addd0Sahoka  * @si: chfs scanning information
1257288addd0Sahoka  * @pebnr: physical eraseblock number
1258288addd0Sahoka  *
1259288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1260288addd0Sahoka  */
1261288addd0Sahoka int
1262288addd0Sahoka nor_process_eb(struct chfs_ebh *ebh, struct chfs_scan_info *si,
1263288addd0Sahoka     int pebnr, struct chfs_eb_hdr *ebhdr)
1264288addd0Sahoka {
1265288addd0Sahoka 	int err, erase_cnt, leb_status;
1266288addd0Sahoka 
1267288addd0Sahoka 	err = ebh->ops->read_eb_hdr(ebh, pebnr, ebhdr);
1268288addd0Sahoka 	if (err)
1269288addd0Sahoka 		return err;
1270288addd0Sahoka 
1271288addd0Sahoka 	erase_cnt = le32toh(ebhdr->ec_hdr.erase_cnt);
1272288addd0Sahoka 	dbg_ebh("erase_cnt: %d\n", erase_cnt);
1273288addd0Sahoka 	leb_status = ebh->ops->check_eb_hdr(ebh, ebhdr);
1274288addd0Sahoka 	if (EBHDR_LEB_BADMAGIC == leb_status ||
1275288addd0Sahoka 	    EBHDR_LEB_BADCRC == leb_status) {
1276288addd0Sahoka 		err = scan_add_to_queue(si, pebnr, erase_cnt, &si->corrupted);
1277288addd0Sahoka 		return err;
1278288addd0Sahoka 	}
1279288addd0Sahoka 	else if (EBHDR_LEB_FREE == leb_status) {
1280288addd0Sahoka 		err = scan_add_to_queue(si, pebnr, erase_cnt, &si->free);
1281288addd0Sahoka 		goto count_mean;
1282288addd0Sahoka 	}
1283288addd0Sahoka 	else if (EBHDR_LEB_NO_HDR == leb_status) {
1284288addd0Sahoka 		err = scan_add_to_queue(si, pebnr, erase_cnt, &si->erased);
1285288addd0Sahoka 		return err;
1286288addd0Sahoka 	}
1287288addd0Sahoka 	else if (EBHDR_LEB_INVALIDATED == leb_status) {
1288288addd0Sahoka 		err = scan_add_to_queue(si, pebnr, erase_cnt, &si->erase);
1289288addd0Sahoka 		return err;
1290288addd0Sahoka 	}
1291288addd0Sahoka 
1292288addd0Sahoka 	err = nor_scan_add_to_used(ebh, si, ebhdr, pebnr, leb_status);
1293288addd0Sahoka 	if (err)
1294288addd0Sahoka 		return err;
1295288addd0Sahoka 
1296288addd0Sahoka 
1297288addd0Sahoka count_mean:
1298288addd0Sahoka 	si->sum_of_ec += erase_cnt;
1299288addd0Sahoka 	si->num_of_eb++;
1300288addd0Sahoka 
1301288addd0Sahoka 	return err;
1302288addd0Sahoka }
1303288addd0Sahoka 
1304288addd0Sahoka /*
1305288addd0Sahoka  * nand_scan_add_to_used - add a physical eraseblock to the
1306288addd0Sahoka  *                         used tree of scan info
1307288addd0Sahoka  * @ebh: chfs eraseblock handler
1308288addd0Sahoka  * @si: chfs scanning information
1309288addd0Sahoka  * @ebhdr: eraseblock header
1310288addd0Sahoka  * @pebnr: physical eraseblock number
1311288addd0Sahoka  * @leb_status: the status of the PEB's eraseblock header
1312288addd0Sahoka  *
1313288addd0Sahoka  * This function adds a PEB to the used tree of the scanning information.
1314288addd0Sahoka  * It handles the situations if there are more physical eraseblock referencing
1315288addd0Sahoka  * to the same logical eraseblock.
1316288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1317288addd0Sahoka  */
1318288addd0Sahoka int
1319288addd0Sahoka nand_scan_add_to_used(struct chfs_ebh *ebh, struct chfs_scan_info *si,
1320288addd0Sahoka     struct chfs_eb_hdr *ebhdr, int pebnr)
1321288addd0Sahoka {
1322288addd0Sahoka 	int err, lnr, ec;
1323288addd0Sahoka 	struct chfs_scan_leb *sleb, *old;
1324288addd0Sahoka 	uint64_t serial = le64toh(ebhdr->u.nand_hdr.serial);
1325288addd0Sahoka 
1326288addd0Sahoka 	lnr = CHFS_GET_LID(ebhdr->u.nor_hdr.lid);
1327288addd0Sahoka 	ec = le32toh(ebhdr->ec_hdr.erase_cnt);
1328288addd0Sahoka 
1329288addd0Sahoka 	sleb = kmem_alloc(sizeof(struct chfs_scan_leb), KM_SLEEP);
1330288addd0Sahoka 
1331288addd0Sahoka 	sleb->erase_cnt = ec;
1332288addd0Sahoka 	sleb->lnr = lnr;
1333288addd0Sahoka 	sleb->pebnr = pebnr;
1334288addd0Sahoka 	sleb->info = serial;
1335288addd0Sahoka 
1336288addd0Sahoka 	old = RB_INSERT(scan_leb_used_rbtree, &si->used, sleb);
1337288addd0Sahoka 	if (old) {
1338288addd0Sahoka 		kmem_free(sleb, sizeof(struct chfs_scan_leb));
1339288addd0Sahoka 		/* There is already an eraseblock in the used tree */
1340288addd0Sahoka 		/* If the new one is bad */
1341288addd0Sahoka 		if (serial < old->info)
1342288addd0Sahoka 			return scan_add_to_queue(si, pebnr, ec, &si->erase);
1343288addd0Sahoka 		else {
1344288addd0Sahoka 			err = scan_add_to_queue(si,
1345288addd0Sahoka 			    old->pebnr, old->erase_cnt, &si->erase);
1346288addd0Sahoka 			if (err)
1347288addd0Sahoka 				return err;
1348288addd0Sahoka 
1349288addd0Sahoka 			old->erase_cnt = ec;
1350288addd0Sahoka 			old->lnr = lnr;
1351288addd0Sahoka 			old->pebnr = pebnr;
1352288addd0Sahoka 			old->info = serial;
1353288addd0Sahoka 			return 0;
1354288addd0Sahoka 		}
1355288addd0Sahoka 	}
1356288addd0Sahoka 	return 0;
1357288addd0Sahoka }
1358288addd0Sahoka 
1359288addd0Sahoka /**
1360288addd0Sahoka  * nand_process eb -read the headers from NAND flash, check them and add to the
1361288addd0Sahoka  * 					scanning information
1362288addd0Sahoka  * @ebh: chfs eraseblock handler
1363288addd0Sahoka  * @si: chfs scanning information
1364288addd0Sahoka  * @pebnr: physical eraseblock number
1365288addd0Sahoka  *
1366288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1367288addd0Sahoka  */
1368288addd0Sahoka int
1369288addd0Sahoka nand_process_eb(struct chfs_ebh *ebh, struct chfs_scan_info *si,
1370288addd0Sahoka     int pebnr, struct chfs_eb_hdr *ebhdr)
1371288addd0Sahoka {
1372288addd0Sahoka 	int err, erase_cnt, leb_status;
1373288addd0Sahoka 	uint64_t max_serial;
1374f045c4d3Sahoka 	/* isbad() is defined on some ancient platforms, heh */
1375f045c4d3Sahoka 	bool is_bad;
1376288addd0Sahoka 
1377288addd0Sahoka 	/* Check block is bad */
1378288addd0Sahoka 	err = flash_block_isbad(ebh->flash_dev,
1379f045c4d3Sahoka 	    pebnr * ebh->flash_if->erasesize, &is_bad);
1380288addd0Sahoka 	if (err) {
1381288addd0Sahoka 		chfs_err("checking block is bad failed\n");
1382288addd0Sahoka 		return err;
1383288addd0Sahoka 	}
1384f045c4d3Sahoka 	if (is_bad) {
1385288addd0Sahoka 		si->bad_peb_cnt++;
1386288addd0Sahoka 		return 0;
1387288addd0Sahoka 	}
1388288addd0Sahoka 
1389288addd0Sahoka 	err = ebh->ops->read_eb_hdr(ebh, pebnr, ebhdr);
1390288addd0Sahoka 	if (err)
1391288addd0Sahoka 		return err;
1392288addd0Sahoka 
1393288addd0Sahoka 	erase_cnt = le32toh(ebhdr->ec_hdr.erase_cnt);
1394288addd0Sahoka 	leb_status = ebh->ops->check_eb_hdr(ebh, ebhdr);
1395288addd0Sahoka 	if (EBHDR_LEB_BADMAGIC == leb_status ||
1396288addd0Sahoka 	    EBHDR_LEB_BADCRC == leb_status) {
1397288addd0Sahoka 		err = scan_add_to_queue(si, pebnr, erase_cnt, &si->corrupted);
1398288addd0Sahoka 		return err;
1399288addd0Sahoka 	}
1400288addd0Sahoka 	else if (EBHDR_LEB_FREE == leb_status) {
1401288addd0Sahoka 		err = scan_add_to_queue(si, pebnr, erase_cnt, &si->free);
1402288addd0Sahoka 		goto count_mean;
1403288addd0Sahoka 	}
1404288addd0Sahoka 	else if (EBHDR_LEB_NO_HDR == leb_status) {
1405288addd0Sahoka 		err = scan_add_to_queue(si, pebnr, erase_cnt, &si->erased);
1406288addd0Sahoka 		return err;
1407288addd0Sahoka 	}
1408288addd0Sahoka 
1409288addd0Sahoka 	err = nand_scan_add_to_used(ebh, si, ebhdr, pebnr);
1410288addd0Sahoka 	if (err)
1411288addd0Sahoka 		return err;
1412288addd0Sahoka 
1413288addd0Sahoka 	max_serial = le64toh(ebhdr->u.nand_hdr.serial);
1414288addd0Sahoka 	if (max_serial > *ebh->max_serial) {
1415288addd0Sahoka 		*ebh->max_serial = max_serial;
1416288addd0Sahoka 	}
1417288addd0Sahoka 
1418288addd0Sahoka count_mean:
1419288addd0Sahoka 	si->sum_of_ec += erase_cnt;
1420288addd0Sahoka 	si->num_of_eb++;
1421288addd0Sahoka 
1422288addd0Sahoka 	return err;
1423288addd0Sahoka }
1424288addd0Sahoka 
1425288addd0Sahoka /**
1426f273a7a1Sandvar  * chfs_scan - scans the media and returns information about it
1427288addd0Sahoka  * @ebh: chfs eraseblock handler
1428288addd0Sahoka  *
1429288addd0Sahoka  * This function scans through the media and returns information about it or if
1430288addd0Sahoka  * it fails NULL will be returned.
1431288addd0Sahoka  */
1432288addd0Sahoka struct chfs_scan_info *
1433288addd0Sahoka chfs_scan(struct chfs_ebh *ebh)
1434288addd0Sahoka {
1435288addd0Sahoka 	struct chfs_scan_info *si;
1436288addd0Sahoka 	struct chfs_eb_hdr *ebhdr;
1437288addd0Sahoka 	int pebnr, err;
1438288addd0Sahoka 
1439288addd0Sahoka 	si = kmem_alloc(sizeof(*si), KM_SLEEP);
1440288addd0Sahoka 
1441288addd0Sahoka 	TAILQ_INIT(&si->corrupted);
1442288addd0Sahoka 	TAILQ_INIT(&si->free);
1443288addd0Sahoka 	TAILQ_INIT(&si->erase);
1444288addd0Sahoka 	TAILQ_INIT(&si->erased);
1445288addd0Sahoka 	RB_INIT(&si->used);
1446288addd0Sahoka 	si->bad_peb_cnt = 0;
1447288addd0Sahoka 	si->num_of_eb = 0;
1448288addd0Sahoka 	si->sum_of_ec = 0;
1449288addd0Sahoka 
1450288addd0Sahoka 	ebhdr = kmem_alloc(sizeof(*ebhdr), KM_SLEEP);
1451288addd0Sahoka 
1452288addd0Sahoka 	for (pebnr = 0; pebnr < ebh->peb_nr; pebnr++) {
1453288addd0Sahoka 		dbg_ebh("processing PEB %d\n", pebnr);
1454288addd0Sahoka 		err = ebh->ops->process_eb(ebh, si, pebnr, ebhdr);
1455288addd0Sahoka 		if (err < 0)
1456288addd0Sahoka 			goto out_ebhdr;
1457288addd0Sahoka 	}
1458288addd0Sahoka 	kmem_free(ebhdr, sizeof(*ebhdr));
1459288addd0Sahoka 	dbg_ebh("[CHFS_SCAN] scanning information collected\n");
1460288addd0Sahoka 	return si;
1461288addd0Sahoka 
1462288addd0Sahoka out_ebhdr:
1463288addd0Sahoka 	kmem_free(ebhdr, sizeof(*ebhdr));
1464288addd0Sahoka 	kmem_free(si, sizeof(*si));
1465288addd0Sahoka 	return NULL;
1466288addd0Sahoka }
1467288addd0Sahoka 
1468288addd0Sahoka /**
1469288addd0Sahoka  * scan_info_destroy - frees all lists and trees in the scanning information
1470288addd0Sahoka  * @si: the scanning information
1471288addd0Sahoka  */
1472288addd0Sahoka void
1473288addd0Sahoka scan_info_destroy(struct chfs_scan_info *si)
1474288addd0Sahoka {
1475288addd0Sahoka 	EBH_QUEUE_DESTROY(&si->corrupted,
1476288addd0Sahoka 	    struct chfs_scan_leb, u.queue);
1477288addd0Sahoka 
1478288addd0Sahoka 	EBH_QUEUE_DESTROY(&si->erase,
1479288addd0Sahoka 	    struct chfs_scan_leb, u.queue);
1480288addd0Sahoka 
1481288addd0Sahoka 	EBH_QUEUE_DESTROY(&si->erased,
1482288addd0Sahoka 	    struct chfs_scan_leb, u.queue);
1483288addd0Sahoka 
1484288addd0Sahoka 	EBH_QUEUE_DESTROY(&si->free,
1485288addd0Sahoka 	    struct chfs_scan_leb, u.queue);
1486288addd0Sahoka 
1487288addd0Sahoka 	EBH_TREE_DESTROY(scan_leb_used_rbtree,
1488288addd0Sahoka 	    &si->used, struct chfs_scan_leb);
1489288addd0Sahoka 
1490288addd0Sahoka 	kmem_free(si, sizeof(*si));
1491288addd0Sahoka 	dbg_ebh("[SCAN_INFO_DESTROY] scanning information destroyed\n");
1492288addd0Sahoka }
1493288addd0Sahoka 
1494288addd0Sahoka /**
1495288addd0Sahoka  * scan_media - scan media
1496288addd0Sahoka  *
1497288addd0Sahoka  * @ebh - chfs eraseblock handler
1498288addd0Sahoka  *
1499288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1500288addd0Sahoka  */
1501288addd0Sahoka 
1502288addd0Sahoka int
1503288addd0Sahoka scan_media(struct chfs_ebh *ebh)
1504288addd0Sahoka {
1505288addd0Sahoka 	int err, i, avg_ec;
1506288addd0Sahoka 	struct chfs_scan_info *si;
1507288addd0Sahoka 	struct chfs_scan_leb *sleb;
1508288addd0Sahoka 
1509288addd0Sahoka 	si = chfs_scan(ebh);
1510288addd0Sahoka 	/*
1511288addd0Sahoka 	 * Process the scan info, manage the eraseblock lists
1512288addd0Sahoka 	 */
1513288addd0Sahoka 	mutex_init(&ebh->ltree_lock, MUTEX_DEFAULT, IPL_NONE);
1514288addd0Sahoka 	mutex_init(&ebh->erase_lock, MUTEX_DEFAULT, IPL_NONE);
1515288addd0Sahoka 	RB_INIT(&ebh->ltree);
1516288addd0Sahoka 	RB_INIT(&ebh->free);
1517288addd0Sahoka 	RB_INIT(&ebh->in_use);
1518288addd0Sahoka 	TAILQ_INIT(&ebh->to_erase);
1519288addd0Sahoka 	TAILQ_INIT(&ebh->fully_erased);
1520288addd0Sahoka 	mutex_init(&ebh->alc_mutex, MUTEX_DEFAULT, IPL_NONE);
1521288addd0Sahoka 
1522288addd0Sahoka 	ebh->peb_nr -= si->bad_peb_cnt;
1523288addd0Sahoka 
1524288addd0Sahoka 	/*
1525288addd0Sahoka 	 * Create background thread for erasing
1526288addd0Sahoka 	 */
1527288addd0Sahoka 	erase_thread_start(ebh);
1528288addd0Sahoka 
1529288addd0Sahoka 	ebh->lmap = kmem_alloc(ebh->peb_nr * sizeof(int), KM_SLEEP);
1530288addd0Sahoka 
1531288addd0Sahoka 	for (i = 0; i < ebh->peb_nr; i++) {
1532288addd0Sahoka 		ebh->lmap[i] = EBH_LEB_UNMAPPED;
1533288addd0Sahoka 	}
1534288addd0Sahoka 
1535288addd0Sahoka 	if (si->num_of_eb == 0) {
1536288addd0Sahoka 		/* The flash contains no data. */
1537288addd0Sahoka 		avg_ec = 0;
1538288addd0Sahoka 	}
1539288addd0Sahoka 	else {
1540288addd0Sahoka 		avg_ec = (int) (si->sum_of_ec / si->num_of_eb);
1541288addd0Sahoka 	}
1542288addd0Sahoka 	dbg_ebh("num_of_eb: %d\n", si->num_of_eb);
1543288addd0Sahoka 
1544288addd0Sahoka 	mutex_enter(&ebh->erase_lock);
1545288addd0Sahoka 
1546288addd0Sahoka 	RB_FOREACH(sleb, scan_leb_used_rbtree, &si->used) {
1547288addd0Sahoka 		ebh->lmap[sleb->lnr] = sleb->pebnr;
1548288addd0Sahoka 		err = add_peb_to_in_use(ebh, sleb->pebnr, sleb->erase_cnt);
1549288addd0Sahoka 		if (err)
1550288addd0Sahoka 			goto out_free;
1551288addd0Sahoka 	}
1552288addd0Sahoka 
1553288addd0Sahoka 	TAILQ_FOREACH(sleb, &si->erased, u.queue) {
1554288addd0Sahoka 		err = add_peb_to_erase_queue(ebh, sleb->pebnr, avg_ec,
1555288addd0Sahoka 		    &ebh->fully_erased);
1556288addd0Sahoka 		if (err)
1557288addd0Sahoka 			goto out_free;
1558288addd0Sahoka 	}
1559288addd0Sahoka 
1560288addd0Sahoka 	TAILQ_FOREACH(sleb, &si->erase, u.queue) {
1561288addd0Sahoka 		err = add_peb_to_erase_queue(ebh, sleb->pebnr, avg_ec,
1562288addd0Sahoka 		    &ebh->to_erase);
1563288addd0Sahoka 		if (err)
1564288addd0Sahoka 			goto out_free;
1565288addd0Sahoka 	}
1566288addd0Sahoka 
1567288addd0Sahoka 	TAILQ_FOREACH(sleb, &si->free, u.queue) {
1568288addd0Sahoka 		err = add_peb_to_free(ebh, sleb->pebnr, sleb->erase_cnt);
1569288addd0Sahoka 		if (err)
1570288addd0Sahoka 			goto out_free;
1571288addd0Sahoka 	}
1572288addd0Sahoka 
1573288addd0Sahoka 	TAILQ_FOREACH(sleb, &si->corrupted, u.queue) {
1574288addd0Sahoka 		err = add_peb_to_erase_queue(ebh, sleb->pebnr, avg_ec,
1575288addd0Sahoka 		    &ebh->to_erase);
1576288addd0Sahoka 		if (err)
1577288addd0Sahoka 			goto out_free;
1578288addd0Sahoka 	}
1579288addd0Sahoka 	mutex_exit(&ebh->erase_lock);
1580288addd0Sahoka 	scan_info_destroy(si);
1581288addd0Sahoka 	return 0;
1582288addd0Sahoka 
1583288addd0Sahoka out_free:
1584288addd0Sahoka 	mutex_exit(&ebh->erase_lock);
1585288addd0Sahoka 	kmem_free(ebh->lmap, ebh->peb_nr * sizeof(int));
1586288addd0Sahoka 	scan_info_destroy(si);
1587288addd0Sahoka 	dbg_ebh("[SCAN_MEDIA] returning with error: %d\n", err);
1588288addd0Sahoka 	return err;
1589288addd0Sahoka }
1590288addd0Sahoka 
1591288addd0Sahoka /*****************************************************************************/
1592288addd0Sahoka /* End of Scan related operations					     */
1593288addd0Sahoka /*****************************************************************************/
1594288addd0Sahoka 
1595288addd0Sahoka /**
1596f273a7a1Sandvar  * ebh_open - opens mtd device and init eraseblock header
1597288addd0Sahoka  * @ebh: eraseblock handler
1598288addd0Sahoka  * @flash_nr: flash device number to use
1599288addd0Sahoka  *
1600288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1601288addd0Sahoka  */
1602288addd0Sahoka int
1603288addd0Sahoka ebh_open(struct chfs_ebh *ebh, dev_t dev)
1604288addd0Sahoka {
1605288addd0Sahoka 	int err;
1606288addd0Sahoka 
1607288addd0Sahoka 	ebh->flash_dev = flash_get_device(dev);
1608288addd0Sahoka 	if (!ebh->flash_dev) {
1609288addd0Sahoka 		aprint_error("ebh_open: cant get flash device\n");
1610288addd0Sahoka 		return ENODEV;
1611288addd0Sahoka 	}
1612288addd0Sahoka 
1613288addd0Sahoka 	ebh->flash_if = flash_get_interface(dev);
1614288addd0Sahoka 	if (!ebh->flash_if) {
1615288addd0Sahoka 		aprint_error("ebh_open: cant get flash interface\n");
1616288addd0Sahoka 		return ENODEV;
1617288addd0Sahoka 	}
1618288addd0Sahoka 
1619288addd0Sahoka 	ebh->flash_size = flash_get_size(dev);
1620288addd0Sahoka 	ebh->peb_nr = ebh->flash_size / ebh->flash_if->erasesize;
1621288addd0Sahoka //	ebh->peb_nr = ebh->flash_if->size / ebh->flash_if->erasesize;
1622288addd0Sahoka 	/* Set up flash operations based on flash type */
1623288addd0Sahoka 	ebh->ops = kmem_alloc(sizeof(struct chfs_ebh_ops), KM_SLEEP);
1624288addd0Sahoka 
1625288addd0Sahoka 	switch (ebh->flash_if->type) {
1626288addd0Sahoka 	case FLASH_TYPE_NOR:
1627288addd0Sahoka 		ebh->eb_size = ebh->flash_if->erasesize -
1628288addd0Sahoka 		    CHFS_EB_EC_HDR_SIZE - CHFS_EB_HDR_NOR_SIZE;
1629288addd0Sahoka 
1630288addd0Sahoka 		ebh->ops->read_eb_hdr = nor_read_eb_hdr;
1631288addd0Sahoka 		ebh->ops->write_eb_hdr = nor_write_eb_hdr;
1632288addd0Sahoka 		ebh->ops->check_eb_hdr = nor_check_eb_hdr;
1633288addd0Sahoka 		ebh->ops->mark_eb_hdr_dirty_flash =
1634288addd0Sahoka 		    nor_mark_eb_hdr_dirty_flash;
1635288addd0Sahoka 		ebh->ops->invalidate_eb_hdr = nor_invalidate_eb_hdr;
1636288addd0Sahoka 		ebh->ops->mark_eb_hdr_free = mark_eb_hdr_free;
1637288addd0Sahoka 
1638288addd0Sahoka 		ebh->ops->process_eb = nor_process_eb;
1639288addd0Sahoka 
1640288addd0Sahoka 		ebh->ops->create_eb_hdr = nor_create_eb_hdr;
1641288addd0Sahoka 		ebh->ops->calc_data_offs = nor_calc_data_offs;
1642288addd0Sahoka 
1643288addd0Sahoka 		ebh->max_serial = NULL;
1644288addd0Sahoka 		break;
1645288addd0Sahoka 	case FLASH_TYPE_NAND:
1646288addd0Sahoka 		ebh->eb_size = ebh->flash_if->erasesize -
1647288addd0Sahoka 		    2 * ebh->flash_if->page_size;
1648288addd0Sahoka 
1649288addd0Sahoka 		ebh->ops->read_eb_hdr = nand_read_eb_hdr;
1650288addd0Sahoka 		ebh->ops->write_eb_hdr = nand_write_eb_hdr;
1651288addd0Sahoka 		ebh->ops->check_eb_hdr = nand_check_eb_hdr;
1652288addd0Sahoka 		ebh->ops->mark_eb_hdr_free = mark_eb_hdr_free;
1653288addd0Sahoka 		ebh->ops->mark_eb_hdr_dirty_flash = NULL;
1654288addd0Sahoka 		ebh->ops->invalidate_eb_hdr = NULL;
1655288addd0Sahoka 
1656288addd0Sahoka 		ebh->ops->process_eb = nand_process_eb;
1657288addd0Sahoka 
1658288addd0Sahoka 		ebh->ops->create_eb_hdr = nand_create_eb_hdr;
1659288addd0Sahoka 		ebh->ops->calc_data_offs = nand_calc_data_offs;
1660288addd0Sahoka 
1661288addd0Sahoka 		ebh->max_serial = kmem_alloc(sizeof(uint64_t), KM_SLEEP);
1662288addd0Sahoka 
1663288addd0Sahoka 		*ebh->max_serial = 0;
1664288addd0Sahoka 		break;
1665288addd0Sahoka 	default:
1666288addd0Sahoka 		return 1;
1667288addd0Sahoka 	}
1668288addd0Sahoka 	printf("opening ebh: eb_size: %zu\n", ebh->eb_size);
1669288addd0Sahoka 	err = scan_media(ebh);
1670288addd0Sahoka 	if (err) {
1671288addd0Sahoka 		dbg_ebh("Scan failed.");
1672288addd0Sahoka 		kmem_free(ebh->ops, sizeof(struct chfs_ebh_ops));
1673288addd0Sahoka 		kmem_free(ebh, sizeof(struct chfs_ebh));
1674288addd0Sahoka 		return err;
1675288addd0Sahoka 	}
1676288addd0Sahoka 	return 0;
1677288addd0Sahoka }
1678288addd0Sahoka 
1679288addd0Sahoka /**
1680288addd0Sahoka  * ebh_close - close ebh
1681288addd0Sahoka  * @ebh: eraseblock handler
1682288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1683288addd0Sahoka  */
1684288addd0Sahoka int
1685288addd0Sahoka ebh_close(struct chfs_ebh *ebh)
1686288addd0Sahoka {
1687288addd0Sahoka 	erase_thread_stop(ebh);
1688288addd0Sahoka 
1689288addd0Sahoka 	EBH_TREE_DESTROY(peb_free_rbtree, &ebh->free, struct chfs_peb);
1690288addd0Sahoka 	EBH_TREE_DESTROY(peb_in_use_rbtree, &ebh->in_use, struct chfs_peb);
1691288addd0Sahoka 
1692288addd0Sahoka 	EBH_QUEUE_DESTROY(&ebh->fully_erased, struct chfs_peb, u.queue);
1693288addd0Sahoka 	EBH_QUEUE_DESTROY(&ebh->to_erase, struct chfs_peb, u.queue);
1694288addd0Sahoka 
1695288addd0Sahoka 	/* XXX HACK, see ebh.h */
1696288addd0Sahoka 	EBH_TREE_DESTROY_MUTEX(ltree_rbtree, &ebh->ltree,
1697288addd0Sahoka 	    struct chfs_ltree_entry);
1698288addd0Sahoka 
1699288addd0Sahoka 	KASSERT(!mutex_owned(&ebh->ltree_lock));
1700288addd0Sahoka 	KASSERT(!mutex_owned(&ebh->alc_mutex));
1701288addd0Sahoka 	KASSERT(!mutex_owned(&ebh->erase_lock));
1702288addd0Sahoka 
1703288addd0Sahoka 	mutex_destroy(&ebh->ltree_lock);
1704288addd0Sahoka 	mutex_destroy(&ebh->alc_mutex);
1705288addd0Sahoka 	mutex_destroy(&ebh->erase_lock);
1706288addd0Sahoka 
1707288addd0Sahoka 	kmem_free(ebh->ops, sizeof(struct chfs_ebh_ops));
1708288addd0Sahoka 	kmem_free(ebh, sizeof(struct chfs_ebh));
1709288addd0Sahoka 
1710288addd0Sahoka 	return 0;
1711288addd0Sahoka }
1712288addd0Sahoka 
1713288addd0Sahoka /**
1714288addd0Sahoka  * ebh_read_leb - read data from leb
1715288addd0Sahoka  * @ebh: eraseblock handler
1716288addd0Sahoka  * @lnr: logical eraseblock number
1717288addd0Sahoka  * @buf: buffer to read to
1718288addd0Sahoka  * @offset: offset from where to read
1719288addd0Sahoka  * @len: bytes number to read
1720288addd0Sahoka  *
1721288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1722288addd0Sahoka  */
1723288addd0Sahoka int
1724288addd0Sahoka ebh_read_leb(struct chfs_ebh *ebh, int lnr, char *buf, uint32_t offset,
1725288addd0Sahoka     size_t len, size_t *retlen)
1726288addd0Sahoka {
1727288addd0Sahoka 	int err, pebnr;
1728288addd0Sahoka 	off_t data_offset;
1729288addd0Sahoka 
1730288addd0Sahoka 	KASSERT(offset + len <= ebh->eb_size);
1731288addd0Sahoka 
1732288addd0Sahoka 	err = leb_read_lock(ebh, lnr);
1733288addd0Sahoka 	if (err)
1734288addd0Sahoka 		return err;
17353fae61eeSttoth 
1736288addd0Sahoka 	pebnr = ebh->lmap[lnr];
1737288addd0Sahoka 	/* If PEB is not mapped the buffer is filled with 0xFF */
1738288addd0Sahoka 	if (EBH_LEB_UNMAPPED == pebnr) {
1739288addd0Sahoka 		leb_read_unlock(ebh, lnr);
1740288addd0Sahoka 		memset(buf, 0xFF, len);
1741288addd0Sahoka 		return 0;
1742288addd0Sahoka 	}
1743288addd0Sahoka 
1744288addd0Sahoka 	/* Read data */
1745288addd0Sahoka 	data_offset = ebh->ops->calc_data_offs(ebh, pebnr, offset);
1746288addd0Sahoka 	err = flash_read(ebh->flash_dev, data_offset, len, retlen,
1747288addd0Sahoka 	    (unsigned char *) buf);
1748288addd0Sahoka 	if (err)
1749288addd0Sahoka 		goto out_free;
1750288addd0Sahoka 
1751288addd0Sahoka 	KASSERT(len == *retlen);
1752288addd0Sahoka 
1753288addd0Sahoka out_free:
1754288addd0Sahoka 	leb_read_unlock(ebh, lnr);
1755288addd0Sahoka 	return err;
1756288addd0Sahoka }
1757288addd0Sahoka 
1758288addd0Sahoka /**
1759288addd0Sahoka  * get_peb: get a free physical eraseblock
1760288addd0Sahoka  * @ebh - chfs eraseblock handler
1761288addd0Sahoka  *
1762288addd0Sahoka  * This function gets a free eraseblock from the ebh->free RB-tree.
176320fff34fSandvar  * The first entry will be returned and deleted from the tree.
1764288addd0Sahoka  * The entries sorted by the erase counters, so the PEB with the smallest
1765288addd0Sahoka  * erase counter will be added back.
1766288addd0Sahoka  * If something goes bad a negative value will be returned.
1767288addd0Sahoka  */
1768288addd0Sahoka int
1769288addd0Sahoka get_peb(struct chfs_ebh *ebh)
1770288addd0Sahoka {
1771288addd0Sahoka 	int err, pebnr;
1772288addd0Sahoka 	struct chfs_peb *peb;
1773288addd0Sahoka 
1774288addd0Sahoka retry:
1775288addd0Sahoka 	mutex_enter(&ebh->erase_lock);
1776288addd0Sahoka 	//dbg_ebh("LOCK: ebh->erase_lock spin locked in get_peb()\n");
1777288addd0Sahoka 	if (RB_EMPTY(&ebh->free)) {
1778288addd0Sahoka 		/*There is no more free PEBs in the tree*/
1779288addd0Sahoka 		if (TAILQ_EMPTY(&ebh->to_erase) &&
1780288addd0Sahoka 		    TAILQ_EMPTY(&ebh->fully_erased)) {
1781288addd0Sahoka 			mutex_exit(&ebh->erase_lock);
1782288addd0Sahoka 			//dbg_ebh("UNLOCK: ebh->erase_lock spin unlocked in get_peb()\n");
1783288addd0Sahoka 			return ENOSPC;
1784288addd0Sahoka 		}
1785288addd0Sahoka 		err = free_peb(ebh);
1786288addd0Sahoka 
1787288addd0Sahoka 		mutex_exit(&ebh->erase_lock);
1788288addd0Sahoka 		//dbg_ebh("UNLOCK: ebh->erase_lock spin unlocked in get_peb()\n");
1789288addd0Sahoka 
1790288addd0Sahoka 		if (err)
1791288addd0Sahoka 			return err;
1792288addd0Sahoka 		goto retry;
1793288addd0Sahoka 	}
1794288addd0Sahoka 	peb = RB_MIN(peb_free_rbtree, &ebh->free);
1795288addd0Sahoka 	pebnr = peb->pebnr;
1796288addd0Sahoka 	RB_REMOVE(peb_free_rbtree, &ebh->free, peb);
1797288addd0Sahoka 	err = add_peb_to_in_use(ebh, peb->pebnr, peb->erase_cnt);
1798288addd0Sahoka 	if (err)
1799288addd0Sahoka 		pebnr = err;
1800288addd0Sahoka 
1801288addd0Sahoka 	kmem_free(peb, sizeof(struct chfs_peb));
1802288addd0Sahoka 
1803288addd0Sahoka 	mutex_exit(&ebh->erase_lock);
1804288addd0Sahoka 	//dbg_ebh("UNLOCK: ebh->erase_lock spin unlocked in get_peb()\n");
1805288addd0Sahoka 
1806288addd0Sahoka 	return pebnr;
1807288addd0Sahoka }
1808288addd0Sahoka 
1809288addd0Sahoka /**
1810288addd0Sahoka  * ebh_write_leb - write data to leb
1811288addd0Sahoka  * @ebh: eraseblock handler
1812288addd0Sahoka  * @lnr: logical eraseblock number
1813288addd0Sahoka  * @buf: data to write
1814288addd0Sahoka  * @offset: offset where to write
1815288addd0Sahoka  * @len: bytes number to write
1816288addd0Sahoka  *
1817288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1818288addd0Sahoka  */
1819288addd0Sahoka int
1820288addd0Sahoka ebh_write_leb(struct chfs_ebh *ebh, int lnr, char *buf, uint32_t offset,
1821288addd0Sahoka     size_t len, size_t *retlen)
1822288addd0Sahoka {
1823288addd0Sahoka 	int err, pebnr, retries = 0;
1824288addd0Sahoka 	off_t data_offset;
1825288addd0Sahoka 	struct chfs_eb_hdr *ebhdr;
1826288addd0Sahoka 
1827288addd0Sahoka 	dbg("offset: %d | len: %zu | (offset+len): %zu "
1828288addd0Sahoka 	    " | ebsize: %zu\n", offset, len, (offset+len), ebh->eb_size);
1829288addd0Sahoka 
1830288addd0Sahoka 	KASSERT(offset + len <= ebh->eb_size);
1831288addd0Sahoka 
1832288addd0Sahoka 	err = leb_write_lock(ebh, lnr);
1833288addd0Sahoka 	if (err)
1834288addd0Sahoka 		return err;
1835288addd0Sahoka 
1836288addd0Sahoka 	pebnr = ebh->lmap[lnr];
1837288addd0Sahoka 	/* If the LEB is mapped write out data */
1838288addd0Sahoka 	if (pebnr != EBH_LEB_UNMAPPED) {
1839288addd0Sahoka 		data_offset = ebh->ops->calc_data_offs(ebh, pebnr, offset);
1840288addd0Sahoka 		err = flash_write(ebh->flash_dev, data_offset, len, retlen,
1841288addd0Sahoka 		    (unsigned char *) buf);
1842288addd0Sahoka 
1843288addd0Sahoka 		if (err) {
1844288addd0Sahoka 			chfs_err("error %d while writing %zu bytes to PEB "
1845288addd0Sahoka 			    "%d:%ju, written %zu bytes\n",
1846288addd0Sahoka 			    err, len, pebnr, (uintmax_t )offset, *retlen);
1847288addd0Sahoka 		} else {
1848288addd0Sahoka 			KASSERT(len == *retlen);
1849288addd0Sahoka 		}
1850288addd0Sahoka 
1851288addd0Sahoka 		leb_write_unlock(ebh, lnr);
1852288addd0Sahoka 		return err;
1853288addd0Sahoka 	}
1854288addd0Sahoka 
1855288addd0Sahoka 	/*
1856288addd0Sahoka 	 * If the LEB is unmapped, get a free PEB and write the
1857288addd0Sahoka 	 * eraseblock header first
1858288addd0Sahoka 	 */
1859288addd0Sahoka 	ebhdr = kmem_alloc(sizeof(struct chfs_eb_hdr), KM_SLEEP);
1860288addd0Sahoka 
1861288addd0Sahoka 	/* Setting up eraseblock header properties */
1862288addd0Sahoka 	ebh->ops->create_eb_hdr(ebhdr, lnr);
1863288addd0Sahoka 
1864288addd0Sahoka retry:
1865288addd0Sahoka 	/* Getting a physical eraseblock from the wear leveling system */
1866288addd0Sahoka 	pebnr = get_peb(ebh);
1867288addd0Sahoka 	if (pebnr < 0) {
1868288addd0Sahoka 		leb_write_unlock(ebh, lnr);
1869288addd0Sahoka 		kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
1870288addd0Sahoka 		return pebnr;
1871288addd0Sahoka 	}
1872288addd0Sahoka 
1873288addd0Sahoka 	/* Write the eraseblock header to the media */
1874288addd0Sahoka 	err = ebh->ops->write_eb_hdr(ebh, pebnr, ebhdr);
1875288addd0Sahoka 	if (err) {
1876288addd0Sahoka 		chfs_warn(
1877288addd0Sahoka 			"error writing eraseblock header: LEB %d , PEB %d\n",
1878288addd0Sahoka 			lnr, pebnr);
1879288addd0Sahoka 		goto write_error;
1880288addd0Sahoka 	}
1881288addd0Sahoka 
1882288addd0Sahoka 	/* Write out data */
1883288addd0Sahoka 	if (len) {
1884288addd0Sahoka 		data_offset = ebh->ops->calc_data_offs(ebh, pebnr, offset);
1885288addd0Sahoka 		err = flash_write(ebh->flash_dev,
1886288addd0Sahoka 		    data_offset, len, retlen, (unsigned char *) buf);
1887288addd0Sahoka 		if (err) {
1888288addd0Sahoka 			chfs_err("error %d while writing %zu bytes to PEB "
1889288addd0Sahoka 			    " %d:%ju, written %zu bytes\n",
1890288addd0Sahoka 			    err, len, pebnr, (uintmax_t )offset, *retlen);
1891288addd0Sahoka 			goto write_error;
1892288addd0Sahoka 		}
1893288addd0Sahoka 	}
1894288addd0Sahoka 
1895288addd0Sahoka 	ebh->lmap[lnr] = pebnr;
1896288addd0Sahoka 	leb_write_unlock(ebh, lnr);
1897288addd0Sahoka 	kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
1898288addd0Sahoka 
1899288addd0Sahoka 	return 0;
1900288addd0Sahoka 
1901288addd0Sahoka write_error: err = release_peb(ebh, pebnr);
1902288addd0Sahoka 	// max retries (NOW: 2)
1903288addd0Sahoka 	if (err || CHFS_MAX_GET_PEB_RETRIES < ++retries) {
1904288addd0Sahoka 		leb_write_unlock(ebh, lnr);
1905288addd0Sahoka 		kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
1906288addd0Sahoka 		return err;
1907288addd0Sahoka 	}
1908288addd0Sahoka 	goto retry;
1909288addd0Sahoka }
1910288addd0Sahoka 
1911288addd0Sahoka /**
1912288addd0Sahoka  * ebh_erase_leb - erase a leb
1913288addd0Sahoka  * @ebh: eraseblock handler
1914288addd0Sahoka  * @lnr: leb number
1915288addd0Sahoka  *
1916288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
1917288addd0Sahoka  */
1918288addd0Sahoka int
1919288addd0Sahoka ebh_erase_leb(struct chfs_ebh *ebh, int lnr)
1920288addd0Sahoka {
1921288addd0Sahoka 	int err, pebnr;
1922288addd0Sahoka 
1923288addd0Sahoka 	leb_write_lock(ebh, lnr);
1924288addd0Sahoka 
1925288addd0Sahoka 	pebnr = ebh->lmap[lnr];
1926288addd0Sahoka 	if (pebnr < 0) {
1927288addd0Sahoka 		leb_write_unlock(ebh, lnr);
1928288addd0Sahoka 		return EBH_LEB_UNMAPPED;
1929288addd0Sahoka 	}
1930288addd0Sahoka 	err = release_peb(ebh, pebnr);
1931288addd0Sahoka 	if (err)
1932288addd0Sahoka 		goto out_unlock;
1933288addd0Sahoka 
1934288addd0Sahoka 	ebh->lmap[lnr] = EBH_LEB_UNMAPPED;
1935288addd0Sahoka 	cv_signal(&ebh->bg_erase.eth_wakeup);
1936288addd0Sahoka out_unlock:
1937288addd0Sahoka 	leb_write_unlock(ebh, lnr);
1938288addd0Sahoka 	return err;
1939288addd0Sahoka }
1940288addd0Sahoka 
1941288addd0Sahoka /**
1942288addd0Sahoka  * ebh_map_leb - maps a PEB to LEB
1943288addd0Sahoka  * @ebh: eraseblock handler
1944288addd0Sahoka  * @lnr: leb number
1945288addd0Sahoka  *
1946288addd0Sahoka  * Returns zero on success, error code in case of fail
1947288addd0Sahoka  */
1948288addd0Sahoka int
1949288addd0Sahoka ebh_map_leb(struct chfs_ebh *ebh, int lnr)
1950288addd0Sahoka {
1951288addd0Sahoka 	int err, pebnr, retries = 0;
1952288addd0Sahoka 	struct chfs_eb_hdr *ebhdr;
1953288addd0Sahoka 
1954288addd0Sahoka 	ebhdr = kmem_alloc(sizeof(struct chfs_eb_hdr), KM_SLEEP);
1955288addd0Sahoka 
1956288addd0Sahoka 	err = leb_write_lock(ebh, lnr);
1957aad3a792Schristos 	if (err) {
1958aad3a792Schristos 		kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
1959288addd0Sahoka 		return err;
1960aad3a792Schristos 	}
1961288addd0Sahoka 
1962288addd0Sahoka retry:
1963288addd0Sahoka 	pebnr = get_peb(ebh);
1964288addd0Sahoka 	if (pebnr < 0) {
1965288addd0Sahoka 		err = pebnr;
1966288addd0Sahoka 		goto out_unlock;
1967288addd0Sahoka 	}
1968288addd0Sahoka 
1969288addd0Sahoka 	ebh->ops->create_eb_hdr(ebhdr, lnr);
1970288addd0Sahoka 
1971288addd0Sahoka 	err = ebh->ops->write_eb_hdr(ebh, pebnr, ebhdr);
1972288addd0Sahoka 	if (err) {
1973288addd0Sahoka 		chfs_warn(
1974288addd0Sahoka 			"error writing eraseblock header: LEB %d , PEB %d\n",
1975288addd0Sahoka 			lnr, pebnr);
1976288addd0Sahoka 		goto write_error;
1977288addd0Sahoka 	}
1978288addd0Sahoka 
1979288addd0Sahoka 	ebh->lmap[lnr] = pebnr;
1980288addd0Sahoka 
1981288addd0Sahoka out_unlock:
1982288addd0Sahoka 	leb_write_unlock(ebh, lnr);
1983288addd0Sahoka 	return err;
1984288addd0Sahoka 
1985288addd0Sahoka write_error:
1986288addd0Sahoka 	err = release_peb(ebh, pebnr);
1987288addd0Sahoka 	// max retries (NOW: 2)
1988288addd0Sahoka 	if (err || CHFS_MAX_GET_PEB_RETRIES < ++retries) {
1989288addd0Sahoka 		leb_write_unlock(ebh, lnr);
1990288addd0Sahoka 		kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
1991288addd0Sahoka 		return err;
1992288addd0Sahoka 	}
1993288addd0Sahoka 	goto retry;
1994288addd0Sahoka }
1995288addd0Sahoka 
1996288addd0Sahoka /**
1997288addd0Sahoka  * ebh_unmap_leb -
1998288addd0Sahoka  * @ebh: eraseblock handler
1999288addd0Sahoka  * @lnr: leb number
2000288addd0Sahoka  *
2001f273a7a1Sandvar  * Returns zero on success, error code in case of fail.
2002288addd0Sahoka  */
2003288addd0Sahoka int
2004288addd0Sahoka ebh_unmap_leb(struct chfs_ebh *ebh, int lnr)
2005288addd0Sahoka {
2006288addd0Sahoka 	int err;
2007288addd0Sahoka 
2008288addd0Sahoka 	if (ebh_is_mapped(ebh, lnr) < 0)
2009288addd0Sahoka 		/* If the eraseblock already unmapped */
2010288addd0Sahoka 		return 0;
2011288addd0Sahoka 
2012288addd0Sahoka 	err = ebh_erase_leb(ebh, lnr);
2013288addd0Sahoka 
2014288addd0Sahoka 	return err;
2015288addd0Sahoka }
2016288addd0Sahoka 
2017288addd0Sahoka /**
2018288addd0Sahoka  * ebh_is_mapped - check if a PEB is mapped to @lnr
2019288addd0Sahoka  * @ebh: eraseblock handler
2020288addd0Sahoka  * @lnr: leb number
2021288addd0Sahoka  *
2022f273a7a1Sandvar  * Returns 0 if the logical eraseblock is mapped, negative error code otherwise.
2023288addd0Sahoka  */
2024288addd0Sahoka int
2025288addd0Sahoka ebh_is_mapped(struct chfs_ebh *ebh, int lnr)
2026288addd0Sahoka {
2027288addd0Sahoka 	int err, result;
2028288addd0Sahoka 	err = leb_read_lock(ebh, lnr);
2029288addd0Sahoka 	if (err)
2030288addd0Sahoka 		return err;
2031288addd0Sahoka 
2032288addd0Sahoka 	result = ebh->lmap[lnr];
2033288addd0Sahoka 	leb_read_unlock(ebh, lnr);
2034288addd0Sahoka 
2035288addd0Sahoka 	return result;
2036288addd0Sahoka }
2037288addd0Sahoka 
2038288addd0Sahoka /**
2039288addd0Sahoka  * ebh_change_leb - write the LEB to another PEB
2040288addd0Sahoka  * @ebh: eraseblock handler
2041288addd0Sahoka  * @lnr: leb number
2042288addd0Sahoka  * @buf: data to write
2043288addd0Sahoka  * @len: length of data
2044288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
2045288addd0Sahoka  */
2046288addd0Sahoka int
2047288addd0Sahoka ebh_change_leb(struct chfs_ebh *ebh, int lnr, char *buf, size_t len,
2048288addd0Sahoka     size_t *retlen)
2049288addd0Sahoka {
2050288addd0Sahoka 	int err, pebnr, pebnr_old, retries = 0;
2051288addd0Sahoka 	off_t data_offset;
2052288addd0Sahoka 
2053288addd0Sahoka 	struct chfs_peb *peb = NULL;
2054288addd0Sahoka 	struct chfs_eb_hdr *ebhdr;
2055288addd0Sahoka 
2056288addd0Sahoka 	if (ebh_is_mapped(ebh, lnr) < 0)
2057288addd0Sahoka 		return EBH_LEB_UNMAPPED;
2058288addd0Sahoka 
2059288addd0Sahoka 	if (len == 0) {
2060288addd0Sahoka 		err = ebh_unmap_leb(ebh, lnr);
2061288addd0Sahoka 		if (err)
2062288addd0Sahoka 			return err;
2063288addd0Sahoka 		return ebh_map_leb(ebh, lnr);
2064288addd0Sahoka 	}
2065288addd0Sahoka 
2066288addd0Sahoka 	ebhdr = kmem_alloc(sizeof(struct chfs_eb_hdr), KM_SLEEP);
2067288addd0Sahoka 
2068288addd0Sahoka 	pebnr_old = ebh->lmap[lnr];
2069288addd0Sahoka 
2070288addd0Sahoka 	mutex_enter(&ebh->alc_mutex);
2071288addd0Sahoka 	err = leb_write_lock(ebh, lnr);
2072288addd0Sahoka 	if (err)
2073288addd0Sahoka 		goto out_mutex;
2074288addd0Sahoka 
2075288addd0Sahoka 	if (ebh->ops->mark_eb_hdr_dirty_flash) {
2076288addd0Sahoka 		err = ebh->ops->mark_eb_hdr_dirty_flash(ebh, pebnr_old, lnr);
2077288addd0Sahoka 		if (err)
2078288addd0Sahoka 			goto out_unlock;
2079288addd0Sahoka 	}
2080288addd0Sahoka 
2081288addd0Sahoka 	/* Setting up eraseblock header properties */
2082288addd0Sahoka 	ebh->ops->create_eb_hdr(ebhdr, lnr);
2083288addd0Sahoka 
2084288addd0Sahoka retry:
2085288addd0Sahoka 	/* Getting a physical eraseblock from the wear leveling system */
2086288addd0Sahoka 	pebnr = get_peb(ebh);
2087288addd0Sahoka 	if (pebnr < 0) {
2088288addd0Sahoka 		leb_write_unlock(ebh, lnr);
2089288addd0Sahoka 		mutex_exit(&ebh->alc_mutex);
2090288addd0Sahoka 		kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
2091288addd0Sahoka 		return pebnr;
2092288addd0Sahoka 	}
2093288addd0Sahoka 
2094288addd0Sahoka 	err = ebh->ops->write_eb_hdr(ebh, pebnr, ebhdr);
2095288addd0Sahoka 	if (err) {
2096288addd0Sahoka 		chfs_warn(
2097288addd0Sahoka 			"error writing eraseblock header: LEB %d , PEB %d",
2098288addd0Sahoka 			lnr, pebnr);
2099288addd0Sahoka 		goto write_error;
2100288addd0Sahoka 	}
2101288addd0Sahoka 
2102288addd0Sahoka 	/* Write out data */
2103288addd0Sahoka 	data_offset = ebh->ops->calc_data_offs(ebh, pebnr, 0);
2104288addd0Sahoka 	err = flash_write(ebh->flash_dev, data_offset, len, retlen,
2105288addd0Sahoka 	    (unsigned char *) buf);
2106288addd0Sahoka 	if (err) {
2107288addd0Sahoka 		chfs_err("error %d while writing %zu bytes to PEB %d:%ju,"
2108288addd0Sahoka 		    " written %zu bytes",
2109288addd0Sahoka 		    err, len, pebnr, (uintmax_t)data_offset, *retlen);
2110288addd0Sahoka 		goto write_error;
2111288addd0Sahoka 	}
2112288addd0Sahoka 
2113288addd0Sahoka 	ebh->lmap[lnr] = pebnr;
2114288addd0Sahoka 
2115288addd0Sahoka 	if (ebh->ops->invalidate_eb_hdr) {
2116288addd0Sahoka 		err = ebh->ops->invalidate_eb_hdr(ebh, pebnr_old);
2117288addd0Sahoka 		if (err)
2118288addd0Sahoka 			goto out_unlock;
2119288addd0Sahoka 	}
2120288addd0Sahoka 	peb = find_peb_in_use(ebh, pebnr_old);
2121288addd0Sahoka 	err = release_peb(ebh, peb->pebnr);
2122288addd0Sahoka 
2123288addd0Sahoka out_unlock:
2124288addd0Sahoka 	leb_write_unlock(ebh, lnr);
2125288addd0Sahoka 
2126288addd0Sahoka out_mutex:
2127288addd0Sahoka 	mutex_exit(&ebh->alc_mutex);
2128288addd0Sahoka 	kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
2129288addd0Sahoka 	kmem_free(peb, sizeof(struct chfs_peb));
2130288addd0Sahoka 	return err;
2131288addd0Sahoka 
2132288addd0Sahoka write_error:
2133288addd0Sahoka 	err = release_peb(ebh, pebnr);
2134288addd0Sahoka 	//max retries (NOW: 2)
2135288addd0Sahoka 	if (err || CHFS_MAX_GET_PEB_RETRIES < ++retries) {
2136288addd0Sahoka 		leb_write_unlock(ebh, lnr);
2137288addd0Sahoka 		mutex_exit(&ebh->alc_mutex);
2138288addd0Sahoka 		kmem_free(ebhdr, sizeof(struct chfs_eb_hdr));
2139288addd0Sahoka 		return err;
2140288addd0Sahoka 	}
2141288addd0Sahoka 	goto retry;
2142288addd0Sahoka }
2143288addd0Sahoka 
2144