1/*-
2 * Copyright (c) 2010-2012 Semihalf.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/conf.h>
33#include <sys/kernel.h>
34#include <sys/lock.h>
35#include <sys/malloc.h>
36#include <sys/mount.h>
37#include <sys/mutex.h>
38#include <sys/namei.h>
39#include <sys/sysctl.h>
40#include <sys/vnode.h>
41#include <sys/buf.h>
42#include <sys/bio.h>
43
44#include <vm/vm.h>
45#include <vm/vm_param.h>
46#include <vm/vm_kern.h>
47#include <vm/vm_page.h>
48
49#include <geom/geom.h>
50#include <geom/geom_vfs.h>
51
52#include <fs/nandfs/nandfs_mount.h>
53#include <fs/nandfs/nandfs.h>
54#include <fs/nandfs/nandfs_subr.h>
55
56#define	SU_USAGE_OFF(bp, offset) \
57	((struct nandfs_segment_usage *)((bp)->b_data + offset))
58
59static int
60nandfs_seg_usage_blk_offset(struct nandfs_device *fsdev, uint64_t seg,
61    uint64_t *blk, uint64_t *offset)
62{
63	uint64_t off;
64	uint16_t seg_size;
65
66	seg_size = fsdev->nd_fsdata.f_segment_usage_size;
67
68	off = roundup(sizeof(struct nandfs_sufile_header), seg_size);
69	off += (seg * seg_size);
70
71	*blk = off / fsdev->nd_blocksize;
72	*offset = off % fsdev->nd_blocksize;
73	return (0);
74}
75
76/* Alloc new segment */
77int
78nandfs_alloc_segment(struct nandfs_device *fsdev, uint64_t *seg)
79{
80	struct nandfs_node *su_node;
81	struct nandfs_sufile_header *su_header;
82	struct nandfs_segment_usage *su_usage;
83	struct buf *bp_header, *bp;
84	uint64_t blk, vblk, offset, i, rest, nsegments;
85	uint16_t seg_size;
86	int error, found;
87
88	seg_size = fsdev->nd_fsdata.f_segment_usage_size;
89	nsegments = fsdev->nd_fsdata.f_nsegments;
90
91	su_node = fsdev->nd_su_node;
92	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
93
94	/* Read header buffer */
95	error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
96	if (error) {
97		brelse(bp_header);
98		return (error);
99	}
100
101	su_header = (struct nandfs_sufile_header *)bp_header->b_data;
102
103	/* Get last allocated segment */
104	i = su_header->sh_last_alloc + 1;
105
106	found = 0;
107	bp = NULL;
108	while (!found) {
109		nandfs_seg_usage_blk_offset(fsdev, i, &blk, &offset);
110		if(blk != 0) {
111			error = nandfs_bmap_lookup(su_node, blk, &vblk);
112			if (error) {
113				nandfs_error("%s: cannot find vblk for blk "
114				    "blk:%jx\n", __func__, blk);
115				return (error);
116			}
117			if (vblk)
118				error = nandfs_bread(su_node, blk, NOCRED, 0,
119				    &bp);
120			else
121				error = nandfs_bcreate(su_node, blk, NOCRED, 0,
122				    &bp);
123			if (error) {
124				nandfs_error("%s: cannot create/read "
125				    "vblk:%jx\n", __func__, vblk);
126				if (bp)
127					brelse(bp);
128				return (error);
129			}
130
131			su_usage = SU_USAGE_OFF(bp, offset);
132		} else {
133			su_usage = SU_USAGE_OFF(bp_header, offset);
134			bp = bp_header;
135		}
136
137		rest = (fsdev->nd_blocksize - offset) / seg_size;
138		/* Go through all su usage in block */
139		while (rest) {
140			/* When last check start from beginning */
141			if (i == nsegments)
142				break;
143
144			if (!su_usage->su_flags) {
145				su_usage->su_flags = 1;
146				found = 1;
147				break;
148			}
149			su_usage++;
150			i++;
151
152			/* If all checked return error */
153			if (i == su_header->sh_last_alloc) {
154				DPRINTF(SEG, ("%s: cannot allocate segment \n",
155				    __func__));
156				brelse(bp_header);
157				if (blk != 0)
158					brelse(bp);
159				return (1);
160			}
161			rest--;
162		}
163		if (!found) {
164			/* Otherwise read another block */
165			if (blk != 0)
166				brelse(bp);
167			if (i == nsegments) {
168				blk = 0;
169				i = 0;
170			} else
171				blk++;
172			offset = 0;
173		}
174	}
175
176	if (found) {
177		*seg = i;
178		su_header->sh_last_alloc = i;
179		su_header->sh_ncleansegs--;
180		su_header->sh_ndirtysegs++;
181
182		fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
183		    fsdev->nd_fsdata.f_blocks_per_segment;
184		fsdev->nd_clean_segs--;
185
186		/*
187		 * It is mostly called from syncer() so we want to force
188		 * making buf dirty.
189		 */
190		error = nandfs_dirty_buf(bp_header, 1);
191		if (error) {
192			if (bp && bp != bp_header)
193				brelse(bp);
194			return (error);
195		}
196		if (bp && bp != bp_header)
197			nandfs_dirty_buf(bp, 1);
198
199		DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)i));
200
201		return (0);
202	}
203
204	DPRINTF(SEG, ("%s: failed\n", __func__));
205
206	return (1);
207}
208
209/*
210 * Make buffer dirty, it will be updated soon but first it need to be
211 * gathered by syncer.
212 */
213int
214nandfs_touch_segment(struct nandfs_device *fsdev, uint64_t seg)
215{
216	struct nandfs_node *su_node;
217	struct buf *bp;
218	uint64_t blk, offset;
219	int error;
220
221	su_node = fsdev->nd_su_node;
222	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
223
224	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
225
226	error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
227	if (error) {
228		brelse(bp);
229		nandfs_error("%s: cannot preallocate new segment\n", __func__);
230		return (error);
231	} else
232		nandfs_dirty_buf(bp, 1);
233
234	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
235	return (error);
236}
237
238/* Update block count of segment */
239int
240nandfs_update_segment(struct nandfs_device *fsdev, uint64_t seg, uint32_t nblks)
241{
242	struct nandfs_node *su_node;
243	struct nandfs_segment_usage *su_usage;
244	struct buf *bp;
245	uint64_t blk, offset;
246	int error;
247
248	su_node = fsdev->nd_su_node;
249	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
250
251	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
252
253	error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
254	if (error) {
255		nandfs_error("%s: read block:%jx to update\n",
256		    __func__, blk);
257		brelse(bp);
258		return (error);
259	}
260
261	su_usage = SU_USAGE_OFF(bp, offset);
262	su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
263	su_usage->su_flags = NANDFS_SEGMENT_USAGE_DIRTY;
264	su_usage->su_nblocks += nblks;
265
266	DPRINTF(SEG, ("%s: seg:%#jx inc:%#x cur:%#x\n",  __func__,
267	    (uintmax_t)seg, nblks, su_usage->su_nblocks));
268
269	nandfs_dirty_buf(bp, 1);
270
271	return (0);
272}
273
274/* Make segment free */
275int
276nandfs_free_segment(struct nandfs_device *fsdev, uint64_t seg)
277{
278	struct nandfs_node *su_node;
279	struct nandfs_sufile_header *su_header;
280	struct nandfs_segment_usage *su_usage;
281	struct buf *bp_header, *bp;
282	uint64_t blk, offset;
283	int error;
284
285	su_node = fsdev->nd_su_node;
286	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
287
288	/* Read su header */
289	error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
290	if (error) {
291		brelse(bp_header);
292		return (error);
293	}
294
295	su_header = (struct nandfs_sufile_header *)bp_header->b_data;
296	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
297
298	/* Read su usage block if other than su header block */
299	if (blk != 0) {
300		error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
301		if (error) {
302			brelse(bp);
303			brelse(bp_header);
304			return (error);
305		}
306	} else
307		bp = bp_header;
308
309	/* Reset su usage data */
310	su_usage = SU_USAGE_OFF(bp, offset);
311	su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
312	su_usage->su_nblocks = 0;
313	su_usage->su_flags = 0;
314
315	/* Update clean/dirty counter in header */
316	su_header->sh_ncleansegs++;
317	su_header->sh_ndirtysegs--;
318
319	/*
320	 *  Make buffers dirty, called by cleaner
321	 *  so force dirty even if no much space left
322	 *  on device
323	 */
324	nandfs_dirty_buf(bp_header, 1);
325	if (bp != bp_header)
326		nandfs_dirty_buf(bp, 1);
327
328	/* Update free block count */
329	fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
330	    fsdev->nd_fsdata.f_blocks_per_segment;
331	fsdev->nd_clean_segs++;
332
333	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
334
335	return (0);
336}
337
338static int
339nandfs_bad_segment(struct nandfs_device *fsdev, uint64_t seg)
340{
341	struct nandfs_node *su_node;
342	struct nandfs_segment_usage *su_usage;
343	struct buf *bp;
344	uint64_t blk, offset;
345	int error;
346
347	su_node = fsdev->nd_su_node;
348	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
349
350	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
351
352	error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
353	if (error) {
354		brelse(bp);
355		return (error);
356	}
357
358	su_usage = SU_USAGE_OFF(bp, offset);
359	su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
360	su_usage->su_flags = NANDFS_SEGMENT_USAGE_ERROR;
361
362	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
363
364	nandfs_dirty_buf(bp, 1);
365
366	return (0);
367}
368
369int
370nandfs_markgc_segment(struct nandfs_device *fsdev, uint64_t seg)
371{
372	struct nandfs_node *su_node;
373	struct nandfs_segment_usage *su_usage;
374	struct buf *bp;
375	uint64_t blk, offset;
376	int error;
377
378	su_node = fsdev->nd_su_node;
379
380	VOP_LOCK(NTOV(su_node), LK_EXCLUSIVE);
381
382	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
383
384	error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
385	if (error) {
386		brelse(bp);
387		VOP_UNLOCK(NTOV(su_node), 0);
388		return (error);
389	}
390
391	su_usage = SU_USAGE_OFF(bp, offset);
392	MPASS((su_usage->su_flags & NANDFS_SEGMENT_USAGE_GC) == 0);
393	su_usage->su_flags |= NANDFS_SEGMENT_USAGE_GC;
394
395	brelse(bp);
396	VOP_UNLOCK(NTOV(su_node), 0);
397
398	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
399
400	return (0);
401}
402
403int
404nandfs_clear_segment(struct nandfs_device *fsdev, uint64_t seg)
405{
406	uint64_t offset, segsize;
407	uint32_t bps, bsize;
408	int error = 0;
409
410	bps = fsdev->nd_fsdata.f_blocks_per_segment;
411	bsize = fsdev->nd_blocksize;
412	segsize = bsize * bps;
413	nandfs_get_segment_range(fsdev, seg, &offset, NULL);
414	offset *= bsize;
415
416	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
417
418	/* Erase it and mark it bad when fail */
419	if (nandfs_erase(fsdev, offset, segsize))
420		error = nandfs_bad_segment(fsdev, seg);
421
422	if (error)
423		return (error);
424
425	/* Mark it free */
426	error = nandfs_free_segment(fsdev, seg);
427
428	return (error);
429}
430
431int
432nandfs_get_seg_stat(struct nandfs_device *nandfsdev,
433    struct nandfs_seg_stat *nss)
434{
435	struct nandfs_sufile_header *suhdr;
436	struct nandfs_node *su_node;
437	struct buf *bp;
438	int err;
439
440	su_node = nandfsdev->nd_su_node;
441
442	NANDFS_WRITELOCK(nandfsdev);
443	VOP_LOCK(NTOV(su_node), LK_SHARED);
444	err = nandfs_bread(nandfsdev->nd_su_node, 0, NOCRED, 0, &bp);
445	if (err) {
446		brelse(bp);
447		VOP_UNLOCK(NTOV(su_node), 0);
448		NANDFS_WRITEUNLOCK(nandfsdev);
449		return (-1);
450	}
451
452	suhdr = (struct nandfs_sufile_header *)bp->b_data;
453	nss->nss_nsegs = nandfsdev->nd_fsdata.f_nsegments;
454	nss->nss_ncleansegs = suhdr->sh_ncleansegs;
455	nss->nss_ndirtysegs = suhdr->sh_ndirtysegs;
456	nss->nss_ctime = 0;
457	nss->nss_nongc_ctime = nandfsdev->nd_ts.tv_sec;
458	nss->nss_prot_seq = nandfsdev->nd_seg_sequence;
459
460	brelse(bp);
461	VOP_UNLOCK(NTOV(su_node), 0);
462
463	NANDFS_WRITEUNLOCK(nandfsdev);
464
465	return (0);
466}
467
468int
469nandfs_get_segment_info_ioctl(struct nandfs_device *fsdev,
470    struct nandfs_argv *nargv)
471{
472	struct nandfs_suinfo *nsi;
473	int error;
474
475	if (nargv->nv_nmembs > NANDFS_SEGMENTS_MAX)
476		return (EINVAL);
477
478	nsi = malloc(sizeof(struct nandfs_suinfo) * nargv->nv_nmembs,
479	    M_NANDFSTEMP, M_WAITOK | M_ZERO);
480
481	error = nandfs_get_segment_info(fsdev, nsi, nargv->nv_nmembs,
482	    nargv->nv_index);
483
484	if (error == 0)
485		error = copyout(nsi, (void *)(uintptr_t)nargv->nv_base,
486		    sizeof(struct nandfs_suinfo) * nargv->nv_nmembs);
487
488	free(nsi, M_NANDFSTEMP);
489	return (error);
490}
491
492int
493nandfs_get_segment_info(struct nandfs_device *fsdev, struct nandfs_suinfo *nsi,
494    uint32_t nmembs, uint64_t segment)
495{
496
497	return (nandfs_get_segment_info_filter(fsdev, nsi, nmembs, segment,
498	    NULL, 0, 0));
499}
500
501int
502nandfs_get_segment_info_filter(struct nandfs_device *fsdev,
503    struct nandfs_suinfo *nsi, uint32_t nmembs, uint64_t segment,
504    uint64_t *nsegs, uint32_t filter, uint32_t nfilter)
505{
506	struct nandfs_segment_usage *su;
507	struct nandfs_node *su_node;
508	struct buf *bp;
509	uint64_t curr, blocknr, blockoff, i;
510	uint32_t flags;
511	int err = 0;
512
513	curr = ~(0);
514
515	lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL);
516	su_node = fsdev->nd_su_node;
517
518	VOP_LOCK(NTOV(su_node), LK_SHARED);
519
520	bp = NULL;
521	if (nsegs !=  NULL)
522		*nsegs = 0;
523	for (i = 0; i < nmembs; segment++) {
524		if (segment == fsdev->nd_fsdata.f_nsegments)
525			break;
526
527		nandfs_seg_usage_blk_offset(fsdev, segment, &blocknr,
528		    &blockoff);
529
530		if (i == 0 || curr != blocknr) {
531			if (bp != NULL)
532				brelse(bp);
533			err = nandfs_bread(su_node, blocknr, NOCRED,
534			    0, &bp);
535			if (err) {
536				goto out;
537			}
538			curr = blocknr;
539		}
540
541		su = SU_USAGE_OFF(bp, blockoff);
542		flags = su->su_flags;
543		if (segment == fsdev->nd_seg_num ||
544		    segment == fsdev->nd_next_seg_num)
545			flags |= NANDFS_SEGMENT_USAGE_ACTIVE;
546
547		if (nfilter != 0 && (flags & nfilter) != 0)
548			continue;
549		if (filter != 0 && (flags & filter) == 0)
550			continue;
551
552		nsi->nsi_num = segment;
553		nsi->nsi_lastmod = su->su_lastmod;
554		nsi->nsi_blocks = su->su_nblocks;
555		nsi->nsi_flags = flags;
556		nsi++;
557		i++;
558		if (nsegs != NULL)
559			(*nsegs)++;
560	}
561
562out:
563	if (bp != NULL)
564		brelse(bp);
565	VOP_UNLOCK(NTOV(su_node), 0);
566	lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL);
567
568	return (err);
569}
570