1235537Sgber/*-
2235537Sgber * Copyright (C) 2009-2012 Semihalf
3235537Sgber * All rights reserved.
4235537Sgber *
5235537Sgber * Redistribution and use in source and binary forms, with or without
6235537Sgber * modification, are permitted provided that the following conditions
7235537Sgber * are met:
8235537Sgber * 1. Redistributions of source code must retain the above copyright
9235537Sgber *    notice, this list of conditions and the following disclaimer.
10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
11235537Sgber *    notice, this list of conditions and the following disclaimer in the
12235537Sgber *    documentation and/or other materials provided with the distribution.
13235537Sgber *
14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17235537Sgber * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24235537Sgber * SUCH DAMAGE.
25235537Sgber */
26235537Sgber
27235537Sgber#include <sys/cdefs.h>
28235537Sgber__FBSDID("$FreeBSD$");
29235537Sgber
30235537Sgber#include <sys/param.h>
31235537Sgber#include <sys/types.h>
32235537Sgber#include <sys/systm.h>
33235537Sgber#include <sys/malloc.h>
34235537Sgber#include <sys/queue.h>
35235537Sgber#include <sys/fcntl.h>
36235537Sgber#include <sys/proc.h>
37235537Sgber#include <sys/namei.h>
38235537Sgber#include <sys/lock.h>
39235537Sgber#include <sys/vnode.h>
40235537Sgber#include <sys/mount.h>
41235537Sgber
42235537Sgber#include <dev/nand/nandsim_chip.h>
43235537Sgber#include <dev/nand/nandsim_swap.h>
44235537Sgber
45235537Sgberstatic int  init_block_state(struct chip_swap *);
46235537Sgberstatic void destroy_block_state(struct chip_swap *);
47235537Sgber
48235537Sgberstatic int  create_buffers(struct chip_swap *);
49235537Sgberstatic void destroy_buffers(struct chip_swap *);
50235537Sgber
51235537Sgberstatic int  swap_file_open(struct chip_swap *, const char *);
52235537Sgberstatic void swap_file_close(struct chip_swap *);
53235537Sgberstatic int  swap_file_write(struct chip_swap *, struct block_state *);
54235537Sgberstatic int  swap_file_read(struct chip_swap *, struct block_state *);
55235537Sgber
56235537Sgber#define	CHIP_SWAP_CMODE		0600
57235537Sgber#define	CHIP_SWAP_BLOCKSPACES	2
58235537Sgber
59235537Sgberstatic int
60235537Sgberinit_block_state(struct chip_swap *swap)
61235537Sgber{
62235537Sgber	struct block_state *blk_state;
63235537Sgber	int i;
64235537Sgber
65235537Sgber	if (swap == NULL)
66235537Sgber		return (-1);
67235537Sgber
68235537Sgber	blk_state = malloc(swap->nof_blks * sizeof(struct block_state),
69235537Sgber	    M_NANDSIM, M_WAITOK | M_ZERO);
70235537Sgber
71235537Sgber	for (i = 0; i < swap->nof_blks; i++)
72235537Sgber		blk_state[i].offset = 0xffffffff;
73235537Sgber
74235537Sgber	swap->blk_state = blk_state;
75235537Sgber
76235537Sgber	return (0);
77235537Sgber}
78235537Sgber
79235537Sgberstatic void
80235537Sgberdestroy_block_state(struct chip_swap *swap)
81235537Sgber{
82235537Sgber
83235537Sgber	if (swap == NULL)
84235537Sgber		return;
85235537Sgber
86235537Sgber	if (swap->blk_state != NULL)
87235537Sgber		free(swap->blk_state, M_NANDSIM);
88235537Sgber}
89235537Sgber
90235537Sgberstatic int
91235537Sgbercreate_buffers(struct chip_swap *swap)
92235537Sgber{
93235537Sgber	struct block_space *block_space;
94235537Sgber	void *block;
95235537Sgber	int i;
96235537Sgber
97235537Sgber	for (i = 0; i < CHIP_SWAP_BLOCKSPACES; i++) {
98235537Sgber		block_space = malloc(sizeof(*block_space), M_NANDSIM, M_WAITOK);
99235537Sgber		block = malloc(swap->blk_size, M_NANDSIM, M_WAITOK);
100235537Sgber		block_space->blk_ptr = block;
101235537Sgber		SLIST_INSERT_HEAD(&swap->free_bs, block_space, free_link);
102235537Sgber		nand_debug(NDBG_SIM,"created blk_space %p[%p]\n", block_space,
103235537Sgber		    block);
104235537Sgber	}
105235537Sgber
106235537Sgber	if (i == 0)
107235537Sgber		return (-1);
108235537Sgber
109235537Sgber	return (0);
110235537Sgber}
111235537Sgber
112235537Sgberstatic void
113235537Sgberdestroy_buffers(struct chip_swap *swap)
114235537Sgber{
115235537Sgber	struct block_space *blk_space;
116235537Sgber
117235537Sgber	if (swap == NULL)
118235537Sgber		return;
119235537Sgber
120235537Sgber	blk_space = SLIST_FIRST(&swap->free_bs);
121235537Sgber	while (blk_space) {
122235537Sgber		SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
123235537Sgber		nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
124235537Sgber		    blk_space, blk_space->blk_ptr);
125235537Sgber		free(blk_space->blk_ptr, M_NANDSIM);
126235537Sgber		free(blk_space, M_NANDSIM);
127235537Sgber		blk_space = SLIST_FIRST(&swap->free_bs);
128235537Sgber	}
129235537Sgber
130235537Sgber	blk_space = STAILQ_FIRST(&swap->used_bs);
131235537Sgber	while (blk_space) {
132235537Sgber		STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
133235537Sgber		nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
134235537Sgber		    blk_space, blk_space->blk_ptr);
135235537Sgber		free(blk_space->blk_ptr, M_NANDSIM);
136235537Sgber		free(blk_space, M_NANDSIM);
137235537Sgber		blk_space = STAILQ_FIRST(&swap->used_bs);
138235537Sgber	}
139235537Sgber}
140235537Sgber
141235537Sgberstatic int
142235537Sgberswap_file_open(struct chip_swap *swap, const char *swap_file)
143235537Sgber{
144235537Sgber	struct nameidata nd;
145241896Skib	int flags, error;
146235537Sgber
147241896Skib	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, swap_file,
148235537Sgber	    curthread);
149235537Sgber
150235537Sgber	flags = FWRITE | FREAD | O_NOFOLLOW | O_CREAT | O_TRUNC;
151235537Sgber
152235537Sgber	error = vn_open(&nd, &flags, CHIP_SWAP_CMODE, NULL);
153235537Sgber	if (error) {
154235537Sgber		nand_debug(NDBG_SIM,"Cannot create swap file %s", swap_file);
155235537Sgber		NDFREE(&nd, NDF_ONLY_PNBUF);
156235537Sgber		return (error);
157235537Sgber	}
158235537Sgber
159235537Sgber	swap->swap_cred = crhold(curthread->td_ucred);
160235537Sgber	NDFREE(&nd, NDF_ONLY_PNBUF);
161235537Sgber
162235537Sgber	/* We just unlock so we hold a reference */
163235537Sgber	VOP_UNLOCK(nd.ni_vp, 0);
164235537Sgber
165235537Sgber	swap->swap_vp = nd.ni_vp;
166235537Sgber
167235537Sgber	return (0);
168235537Sgber}
169235537Sgber
170235537Sgberstatic void
171235537Sgberswap_file_close(struct chip_swap *swap)
172235537Sgber{
173235537Sgber
174235537Sgber	if (swap == NULL)
175235537Sgber		return;
176235537Sgber
177235537Sgber	if (swap->swap_vp == NULL)
178235537Sgber		return;
179235537Sgber
180235537Sgber	vn_close(swap->swap_vp, FWRITE, swap->swap_cred, curthread);
181235537Sgber	crfree(swap->swap_cred);
182235537Sgber}
183235537Sgber
184235537Sgberstatic int
185235537Sgberswap_file_write(struct chip_swap *swap, struct block_state *blk_state)
186235537Sgber{
187235537Sgber	struct block_space *blk_space;
188235537Sgber	struct thread *td;
189235537Sgber	struct mount *mp;
190235537Sgber	struct vnode *vp;
191235537Sgber	struct uio auio;
192235537Sgber	struct iovec aiov;
193235537Sgber
194235537Sgber	if (swap == NULL || blk_state == NULL)
195235537Sgber		return (-1);
196235537Sgber
197235537Sgber	blk_space = blk_state->blk_sp;
198235537Sgber	if (blk_state->offset == -1) {
199235537Sgber		blk_state->offset = swap->swap_offset;
200235537Sgber		swap->swap_offset += swap->blk_size;
201235537Sgber	}
202235537Sgber
203235537Sgber	nand_debug(NDBG_SIM,"saving %p[%p] at %x\n",
204235537Sgber	    blk_space, blk_space->blk_ptr, blk_state->offset);
205235537Sgber
206235537Sgber	bzero(&aiov, sizeof(aiov));
207235537Sgber	bzero(&auio, sizeof(auio));
208235537Sgber
209235537Sgber	aiov.iov_base = blk_space->blk_ptr;
210235537Sgber	aiov.iov_len = swap->blk_size;
211235537Sgber	td = curthread;
212235537Sgber	vp = swap->swap_vp;
213235537Sgber
214235537Sgber	auio.uio_iov = &aiov;
215235537Sgber	auio.uio_offset = blk_state->offset;
216235537Sgber	auio.uio_segflg = UIO_SYSSPACE;
217235537Sgber	auio.uio_rw = UIO_WRITE;
218235537Sgber	auio.uio_iovcnt = 1;
219235537Sgber	auio.uio_resid = swap->blk_size;
220235537Sgber	auio.uio_td = td;
221235537Sgber
222235537Sgber	vn_start_write(vp, &mp, V_WAIT);
223235537Sgber	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
224235537Sgber	VOP_WRITE(vp, &auio, IO_UNIT, swap->swap_cred);
225235537Sgber	VOP_UNLOCK(vp, 0);
226235537Sgber	vn_finished_write(mp);
227235537Sgber
228235537Sgber	return (0);
229235537Sgber}
230235537Sgber
231235537Sgberstatic int
232235537Sgberswap_file_read(struct chip_swap *swap, struct block_state *blk_state)
233235537Sgber{
234235537Sgber	struct block_space *blk_space;
235235537Sgber	struct thread *td;
236235537Sgber	struct vnode *vp;
237235537Sgber	struct uio auio;
238235537Sgber	struct iovec aiov;
239235537Sgber
240235537Sgber	if (swap == NULL || blk_state == NULL)
241235537Sgber		return (-1);
242235537Sgber
243235537Sgber	blk_space = blk_state->blk_sp;
244235537Sgber
245235537Sgber	nand_debug(NDBG_SIM,"restore %p[%p] at %x\n",
246235537Sgber	    blk_space, blk_space->blk_ptr, blk_state->offset);
247235537Sgber
248235537Sgber	bzero(&aiov, sizeof(aiov));
249235537Sgber	bzero(&auio, sizeof(auio));
250235537Sgber
251235537Sgber	aiov.iov_base = blk_space->blk_ptr;
252235537Sgber	aiov.iov_len = swap->blk_size;
253235537Sgber	td = curthread;
254235537Sgber	vp = swap->swap_vp;
255235537Sgber
256235537Sgber	auio.uio_iov = &aiov;
257235537Sgber	auio.uio_offset = blk_state->offset;
258235537Sgber	auio.uio_segflg = UIO_SYSSPACE;
259235537Sgber	auio.uio_rw = UIO_READ;
260235537Sgber	auio.uio_iovcnt = 1;
261235537Sgber	auio.uio_resid = swap->blk_size;
262235537Sgber	auio.uio_td = td;
263235537Sgber
264235537Sgber	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
265235537Sgber	VOP_READ(vp, &auio, 0, swap->swap_cred);
266235537Sgber	VOP_UNLOCK(vp, 0);
267235537Sgber
268235537Sgber	return (0);
269235537Sgber}
270235537Sgber
271235537Sgberstruct chip_swap *
272235537Sgbernandsim_swap_init(const char *swap_file, uint32_t nof_blks, uint32_t blk_size)
273235537Sgber{
274235537Sgber	struct chip_swap *swap;
275235537Sgber	int err = 0;
276235537Sgber
277235537Sgber	if ((swap_file == NULL) || (nof_blks == 0) || (blk_size == 0))
278235537Sgber		return (NULL);
279235537Sgber
280235537Sgber	swap = malloc(sizeof(*swap), M_NANDSIM, M_WAITOK | M_ZERO);
281235537Sgber
282235537Sgber	SLIST_INIT(&swap->free_bs);
283235537Sgber	STAILQ_INIT(&swap->used_bs);
284235537Sgber	swap->blk_size = blk_size;
285235537Sgber	swap->nof_blks = nof_blks;
286235537Sgber
287235537Sgber	err = init_block_state(swap);
288235537Sgber	if (err) {
289235537Sgber		nandsim_swap_destroy(swap);
290235537Sgber		return (NULL);
291235537Sgber	}
292235537Sgber
293235537Sgber	err = create_buffers(swap);
294235537Sgber	if (err) {
295235537Sgber		nandsim_swap_destroy(swap);
296235537Sgber		return (NULL);
297235537Sgber	}
298235537Sgber
299235537Sgber	err = swap_file_open(swap, swap_file);
300235537Sgber	if (err) {
301235537Sgber		nandsim_swap_destroy(swap);
302235537Sgber		return (NULL);
303235537Sgber	}
304235537Sgber
305235537Sgber	return (swap);
306235537Sgber}
307235537Sgber
308235537Sgbervoid
309235537Sgbernandsim_swap_destroy(struct chip_swap *swap)
310235537Sgber{
311235537Sgber
312235537Sgber	if (swap == NULL)
313235537Sgber		return;
314235537Sgber
315235537Sgber	destroy_block_state(swap);
316235537Sgber	destroy_buffers(swap);
317235537Sgber	swap_file_close(swap);
318235537Sgber	free(swap, M_NANDSIM);
319235537Sgber}
320235537Sgber
321235537Sgberstruct block_space *
322235537Sgberget_bs(struct chip_swap *swap, uint32_t block, uint8_t writing)
323235537Sgber{
324235537Sgber	struct block_state *blk_state, *old_blk_state = NULL;
325235537Sgber	struct block_space *blk_space;
326235537Sgber
327235537Sgber	if (swap == NULL || (block >= swap->nof_blks))
328235537Sgber		return (NULL);
329235537Sgber
330235537Sgber	blk_state = &swap->blk_state[block];
331235537Sgber	nand_debug(NDBG_SIM,"blk_state %x\n", blk_state->status);
332235537Sgber
333235537Sgber	if (blk_state->status & BLOCK_ALLOCATED) {
334235537Sgber		blk_space = blk_state->blk_sp;
335235537Sgber	} else {
336235537Sgber		blk_space = SLIST_FIRST(&swap->free_bs);
337235537Sgber		if (blk_space) {
338235537Sgber			SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
339235537Sgber			STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
340235537Sgber			    used_link);
341235537Sgber		} else {
342235537Sgber			blk_space = STAILQ_FIRST(&swap->used_bs);
343235537Sgber			old_blk_state = blk_space->blk_state;
344235537Sgber			STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
345235537Sgber			STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
346235537Sgber			    used_link);
347235537Sgber			if (old_blk_state->status & BLOCK_DIRTY) {
348235537Sgber				swap_file_write(swap, old_blk_state);
349235537Sgber				old_blk_state->status &= ~BLOCK_DIRTY;
350235537Sgber				old_blk_state->status |= BLOCK_SWAPPED;
351235537Sgber			}
352235537Sgber		}
353235537Sgber	}
354235537Sgber
355235537Sgber	if (blk_space == NULL)
356235537Sgber		return (NULL);
357235537Sgber
358235537Sgber	if (old_blk_state != NULL) {
359235537Sgber		old_blk_state->status &= ~BLOCK_ALLOCATED;
360235537Sgber		old_blk_state->blk_sp = NULL;
361235537Sgber	}
362235537Sgber
363235537Sgber	blk_state->blk_sp = blk_space;
364235537Sgber	blk_space->blk_state = blk_state;
365235537Sgber
366235537Sgber	if (!(blk_state->status & BLOCK_ALLOCATED)) {
367235537Sgber		if (blk_state->status & BLOCK_SWAPPED)
368235537Sgber			swap_file_read(swap, blk_state);
369235537Sgber		else
370235537Sgber			memset(blk_space->blk_ptr, 0xff, swap->blk_size);
371235537Sgber		blk_state->status |= BLOCK_ALLOCATED;
372235537Sgber	}
373235537Sgber
374235537Sgber	if (writing)
375235537Sgber		blk_state->status |= BLOCK_DIRTY;
376235537Sgber
377235537Sgber	nand_debug(NDBG_SIM,"get_bs returned %p[%p] state %x\n", blk_space,
378235537Sgber	    blk_space->blk_ptr, blk_state->status);
379235537Sgber
380235537Sgber	return (blk_space);
381235537Sgber}
382