xref: /minix/sys/ufs/chfs/chfs_wbuf.c (revision 84d9c625)
1 /*	$NetBSD: chfs_wbuf.c,v 1.5 2012/10/19 12:44:39 ttoth Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010 Department of Software Engineering,
5  *		      University of Szeged, Hungary
6  * Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
7  * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to The NetBSD Foundation
11  * by the Department of Software Engineering, University of Szeged, Hungary
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <dev/flash/flash.h>
36 #include <sys/uio.h>
37 #include "chfs.h"
38 
39 #define DBG_WBUF 1		/* XXX unused, but should be */
40 
41 #define PAD(x) (((x)+3)&~3)
42 
43 #define EB_ADDRESS(x) ( rounddown((x), chmp->chm_ebh->eb_size) )
44 
45 #define PAGE_DIV(x) ( rounddown((x), chmp->chm_wbuf_pagesize) )
46 #define PAGE_MOD(x) ( (x) % (chmp->chm_wbuf_pagesize) )
47 
48 /* writebuffer options */
49 enum {
50 	WBUF_NOPAD,
51 	WBUF_SETPAD
52 };
53 
54 /*
55  * chfs_flush_wbuf - write wbuf to the flash
56  * Returns zero in case of success.
57  */
58 static int
59 chfs_flush_wbuf(struct chfs_mount *chmp, int pad)
60 {
61 	int ret;
62 	size_t retlen;
63 	struct chfs_node_ref *nref;
64 	struct chfs_flash_padding_node* padnode;
65 
66 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
67 	KASSERT(mutex_owned(&chmp->chm_lock_sizes));
68 	KASSERT(rw_write_held(&chmp->chm_lock_wbuf));
69 	KASSERT(pad == WBUF_SETPAD || pad == WBUF_NOPAD);
70 
71 	/* check padding option */
72 	if (pad == WBUF_SETPAD) {
73 		chmp->chm_wbuf_len = PAD(chmp->chm_wbuf_len);
74 		memset(chmp->chm_wbuf + chmp->chm_wbuf_len, 0,
75 		    chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len);
76 
77 		/* add a padding node */
78 		padnode = (void *)(chmp->chm_wbuf + chmp->chm_wbuf_len);
79 		padnode->magic = htole16(CHFS_FS_MAGIC_BITMASK);
80 		padnode->type = htole16(CHFS_NODETYPE_PADDING);
81 		padnode->length = htole32(chmp->chm_wbuf_pagesize
82 		    - chmp->chm_wbuf_len);
83 		padnode->hdr_crc = htole32(crc32(0, (uint8_t *)padnode,
84 			sizeof(*padnode)-4));
85 
86 		nref = chfs_alloc_node_ref(chmp->chm_nextblock);
87 		nref->nref_offset = chmp->chm_wbuf_ofs + chmp->chm_wbuf_len;
88 		nref->nref_offset = CHFS_GET_OFS(nref->nref_offset) |
89 		    CHFS_OBSOLETE_NODE_MASK;
90 		chmp->chm_wbuf_len = chmp->chm_wbuf_pagesize;
91 
92 		/* change sizes after padding node */
93 		chfs_change_size_free(chmp, chmp->chm_nextblock,
94 		    -padnode->length);
95 		chfs_change_size_wasted(chmp, chmp->chm_nextblock,
96 		    padnode->length);
97 	}
98 
99 	/* write out the buffer */
100 	ret = chfs_write_leb(chmp, chmp->chm_nextblock->lnr, chmp->chm_wbuf,
101 	    chmp->chm_wbuf_ofs, chmp->chm_wbuf_len, &retlen);
102 	if (ret) {
103 		return ret;
104 	}
105 
106 	/* reset the buffer */
107 	memset(chmp->chm_wbuf, 0xff, chmp->chm_wbuf_pagesize);
108 	chmp->chm_wbuf_ofs += chmp->chm_wbuf_pagesize;
109 	chmp->chm_wbuf_len = 0;
110 
111 	return 0;
112 }
113 
114 
115 /*
116  * chfs_fill_wbuf - write data to wbuf
117  * Return the len of the buf what we didn't write to the wbuf.
118  */
119 static size_t
120 chfs_fill_wbuf(struct chfs_mount *chmp, const u_char *buf, size_t len)
121 {
122 	/* check available space */
123 	if (len && !chmp->chm_wbuf_len && (len >= chmp->chm_wbuf_pagesize)) {
124 		return 0;
125 	}
126 	/* check buffer's length */
127 	if (len > (chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len)) {
128 		len = chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len;
129 	}
130 	/* write into the wbuf */
131 	memcpy(chmp->chm_wbuf + chmp->chm_wbuf_len, buf, len);
132 
133 	/* update the actual length of writebuffer */
134 	chmp->chm_wbuf_len += (int) len;
135 	return len;
136 }
137 
138 /*
139  * chfs_write_wbuf - write to wbuf and then the flash
140  * Returns zero in case of success.
141  */
142 int
143 chfs_write_wbuf(struct chfs_mount* chmp, const struct iovec *invecs, long count,
144     off_t to, size_t *retlen)
145 {
146 	int invec, ret = 0;
147 	size_t wbuf_retlen, donelen = 0;
148 	int outvec_to = to;
149 
150 	int lnr = chmp->chm_nextblock->lnr;
151 
152 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
153 	KASSERT(mutex_owned(&chmp->chm_lock_sizes));
154 	KASSERT(!rw_write_held(&chmp->chm_lock_wbuf));
155 
156 	rw_enter(&chmp->chm_lock_wbuf, RW_WRITER);
157 
158 	if (chmp->chm_wbuf_ofs == 0xffffffff) {
159 		chmp->chm_wbuf_ofs = PAGE_DIV(to);
160 		chmp->chm_wbuf_len = PAGE_MOD(to);
161 		memset(chmp->chm_wbuf, 0xff, chmp->chm_wbuf_pagesize);
162 	}
163 
164 	if (EB_ADDRESS(to) != EB_ADDRESS(chmp->chm_wbuf_ofs)) {
165 		if (chmp->chm_wbuf_len) {
166 			ret = chfs_flush_wbuf(chmp, WBUF_SETPAD);
167 			if (ret)
168 				goto outerr;
169 		}
170 		chmp->chm_wbuf_ofs = PAGE_DIV(to);
171 		chmp->chm_wbuf_len = PAGE_MOD(to);
172 	}
173 
174 	if (to != PAD(chmp->chm_wbuf_ofs + chmp->chm_wbuf_len)) {
175 		dbg("to: %llu != %zu\n", (unsigned long long)to,
176 			PAD(chmp->chm_wbuf_ofs + chmp->chm_wbuf_len));
177 		dbg("Non-contiguous write\n");
178 		panic("BUG\n");
179 	}
180 
181 	/* adjust alignment offset */
182 	if (chmp->chm_wbuf_len != PAGE_MOD(to)) {
183 		chmp->chm_wbuf_len = PAGE_MOD(to);
184 		/* take care of alignement to next page */
185 		if (!chmp->chm_wbuf_len) {
186 			chmp->chm_wbuf_len += chmp->chm_wbuf_pagesize;
187 			ret = chfs_flush_wbuf(chmp, WBUF_NOPAD);
188 			if (ret)
189 				goto outerr;
190 		}
191 	}
192 
193 	for (invec = 0; invec < count; invec++) {
194 		int vlen = invecs[invec].iov_len;
195 		u_char* v = invecs[invec].iov_base;
196 
197 		/* fill the whole wbuf */
198 		wbuf_retlen = chfs_fill_wbuf(chmp, v, vlen);
199 		if (chmp->chm_wbuf_len == chmp->chm_wbuf_pagesize) {
200 			ret = chfs_flush_wbuf(chmp, WBUF_NOPAD);
201 			if (ret) {
202 				goto outerr;
203 			}
204 		}
205 
206 		vlen -= wbuf_retlen;
207 		outvec_to += wbuf_retlen;
208 		v += wbuf_retlen;
209 		donelen += wbuf_retlen;
210 
211 		/* if there is more residual data than the length of the wbuf
212 		 * write it out directly until it's fit in the wbuf */
213 		if (vlen >= chmp->chm_wbuf_pagesize) {
214 			ret = chfs_write_leb(chmp, lnr, v, outvec_to, PAGE_DIV(vlen), &wbuf_retlen);
215 			vlen -= wbuf_retlen;
216 			outvec_to += wbuf_retlen;
217 			chmp->chm_wbuf_ofs = outvec_to;
218 			v += wbuf_retlen;
219 			donelen += wbuf_retlen;
220 		}
221 
222 		/* write the residual data to the wbuf */
223 		wbuf_retlen = chfs_fill_wbuf(chmp, v, vlen);
224 		if (chmp->chm_wbuf_len == chmp->chm_wbuf_pagesize) {
225 			ret = chfs_flush_wbuf(chmp, WBUF_NOPAD);
226 			if (ret)
227 				goto outerr;
228 		}
229 
230 		outvec_to += wbuf_retlen;
231 		donelen += wbuf_retlen;
232 	}
233 	*retlen = donelen;
234 	rw_exit(&chmp->chm_lock_wbuf);
235 	return ret;
236 
237 outerr:
238 	*retlen = 0;
239 	return ret;
240 }
241 
242 /*
243  * chfs_flush_peding_wbuf - write wbuf to the flash
244  * Used when we must flush wbuf right now.
245  * If wbuf has free space, pad it to the size of wbuf and write out.
246  */
247 int chfs_flush_pending_wbuf(struct chfs_mount *chmp)
248 {
249 	int err;
250 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
251 	mutex_enter(&chmp->chm_lock_sizes);
252 	rw_enter(&chmp->chm_lock_wbuf, RW_WRITER);
253 	err = chfs_flush_wbuf(chmp, WBUF_SETPAD);
254 	rw_exit(&chmp->chm_lock_wbuf);
255 	mutex_exit(&chmp->chm_lock_sizes);
256 	return err;
257 }
258