• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/fs/nilfs2/
1/*
2 * sufile.c - NILFS segment usage file.
3 *
4 * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 *
20 * Written by Koji Sato <koji@osrg.net>.
21 * Revised by Ryusuke Konishi <ryusuke@osrg.net>.
22 */
23
24#include <linux/kernel.h>
25#include <linux/fs.h>
26#include <linux/string.h>
27#include <linux/buffer_head.h>
28#include <linux/errno.h>
29#include <linux/nilfs2_fs.h>
30#include "mdt.h"
31#include "sufile.h"
32
33
34struct nilfs_sufile_info {
35	struct nilfs_mdt_info mi;
36	unsigned long ncleansegs;
37};
38
39static inline struct nilfs_sufile_info *NILFS_SUI(struct inode *sufile)
40{
41	return (struct nilfs_sufile_info *)NILFS_MDT(sufile);
42}
43
44static inline unsigned long
45nilfs_sufile_segment_usages_per_block(const struct inode *sufile)
46{
47	return NILFS_MDT(sufile)->mi_entries_per_block;
48}
49
50static unsigned long
51nilfs_sufile_get_blkoff(const struct inode *sufile, __u64 segnum)
52{
53	__u64 t = segnum + NILFS_MDT(sufile)->mi_first_entry_offset;
54	do_div(t, nilfs_sufile_segment_usages_per_block(sufile));
55	return (unsigned long)t;
56}
57
58static unsigned long
59nilfs_sufile_get_offset(const struct inode *sufile, __u64 segnum)
60{
61	__u64 t = segnum + NILFS_MDT(sufile)->mi_first_entry_offset;
62	return do_div(t, nilfs_sufile_segment_usages_per_block(sufile));
63}
64
65static unsigned long
66nilfs_sufile_segment_usages_in_block(const struct inode *sufile, __u64 curr,
67				     __u64 max)
68{
69	return min_t(unsigned long,
70		     nilfs_sufile_segment_usages_per_block(sufile) -
71		     nilfs_sufile_get_offset(sufile, curr),
72		     max - curr + 1);
73}
74
75static struct nilfs_segment_usage *
76nilfs_sufile_block_get_segment_usage(const struct inode *sufile, __u64 segnum,
77				     struct buffer_head *bh, void *kaddr)
78{
79	return kaddr + bh_offset(bh) +
80		nilfs_sufile_get_offset(sufile, segnum) *
81		NILFS_MDT(sufile)->mi_entry_size;
82}
83
84static inline int nilfs_sufile_get_header_block(struct inode *sufile,
85						struct buffer_head **bhp)
86{
87	return nilfs_mdt_get_block(sufile, 0, 0, NULL, bhp);
88}
89
90static inline int
91nilfs_sufile_get_segment_usage_block(struct inode *sufile, __u64 segnum,
92				     int create, struct buffer_head **bhp)
93{
94	return nilfs_mdt_get_block(sufile,
95				   nilfs_sufile_get_blkoff(sufile, segnum),
96				   create, NULL, bhp);
97}
98
99static void nilfs_sufile_mod_counter(struct buffer_head *header_bh,
100				     u64 ncleanadd, u64 ndirtyadd)
101{
102	struct nilfs_sufile_header *header;
103	void *kaddr;
104
105	kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
106	header = kaddr + bh_offset(header_bh);
107	le64_add_cpu(&header->sh_ncleansegs, ncleanadd);
108	le64_add_cpu(&header->sh_ndirtysegs, ndirtyadd);
109	kunmap_atomic(kaddr, KM_USER0);
110
111	nilfs_mdt_mark_buffer_dirty(header_bh);
112}
113
114/**
115 * nilfs_sufile_get_ncleansegs - return the number of clean segments
116 * @sufile: inode of segment usage file
117 */
118unsigned long nilfs_sufile_get_ncleansegs(struct inode *sufile)
119{
120	return NILFS_SUI(sufile)->ncleansegs;
121}
122
123/**
124 * nilfs_sufile_updatev - modify multiple segment usages at a time
125 * @sufile: inode of segment usage file
126 * @segnumv: array of segment numbers
127 * @nsegs: size of @segnumv array
128 * @create: creation flag
129 * @ndone: place to store number of modified segments on @segnumv
130 * @dofunc: primitive operation for the update
131 *
132 * Description: nilfs_sufile_updatev() repeatedly calls @dofunc
133 * against the given array of segments.  The @dofunc is called with
134 * buffers of a header block and the sufile block in which the target
135 * segment usage entry is contained.  If @ndone is given, the number
136 * of successfully modified segments from the head is stored in the
137 * place @ndone points to.
138 *
139 * Return Value: On success, zero is returned.  On error, one of the
140 * following negative error codes is returned.
141 *
142 * %-EIO - I/O error.
143 *
144 * %-ENOMEM - Insufficient amount of memory available.
145 *
146 * %-ENOENT - Given segment usage is in hole block (may be returned if
147 *            @create is zero)
148 *
149 * %-EINVAL - Invalid segment usage number
150 */
151int nilfs_sufile_updatev(struct inode *sufile, __u64 *segnumv, size_t nsegs,
152			 int create, size_t *ndone,
153			 void (*dofunc)(struct inode *, __u64,
154					struct buffer_head *,
155					struct buffer_head *))
156{
157	struct buffer_head *header_bh, *bh;
158	unsigned long blkoff, prev_blkoff;
159	__u64 *seg;
160	size_t nerr = 0, n = 0;
161	int ret = 0;
162
163	if (unlikely(nsegs == 0))
164		goto out;
165
166	down_write(&NILFS_MDT(sufile)->mi_sem);
167	for (seg = segnumv; seg < segnumv + nsegs; seg++) {
168		if (unlikely(*seg >= nilfs_sufile_get_nsegments(sufile))) {
169			printk(KERN_WARNING
170			       "%s: invalid segment number: %llu\n", __func__,
171			       (unsigned long long)*seg);
172			nerr++;
173		}
174	}
175	if (nerr > 0) {
176		ret = -EINVAL;
177		goto out_sem;
178	}
179
180	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
181	if (ret < 0)
182		goto out_sem;
183
184	seg = segnumv;
185	blkoff = nilfs_sufile_get_blkoff(sufile, *seg);
186	ret = nilfs_mdt_get_block(sufile, blkoff, create, NULL, &bh);
187	if (ret < 0)
188		goto out_header;
189
190	for (;;) {
191		dofunc(sufile, *seg, header_bh, bh);
192
193		if (++seg >= segnumv + nsegs)
194			break;
195		prev_blkoff = blkoff;
196		blkoff = nilfs_sufile_get_blkoff(sufile, *seg);
197		if (blkoff == prev_blkoff)
198			continue;
199
200		/* get different block */
201		brelse(bh);
202		ret = nilfs_mdt_get_block(sufile, blkoff, create, NULL, &bh);
203		if (unlikely(ret < 0))
204			goto out_header;
205	}
206	brelse(bh);
207
208 out_header:
209	n = seg - segnumv;
210	brelse(header_bh);
211 out_sem:
212	up_write(&NILFS_MDT(sufile)->mi_sem);
213 out:
214	if (ndone)
215		*ndone = n;
216	return ret;
217}
218
219int nilfs_sufile_update(struct inode *sufile, __u64 segnum, int create,
220			void (*dofunc)(struct inode *, __u64,
221				       struct buffer_head *,
222				       struct buffer_head *))
223{
224	struct buffer_head *header_bh, *bh;
225	int ret;
226
227	if (unlikely(segnum >= nilfs_sufile_get_nsegments(sufile))) {
228		printk(KERN_WARNING "%s: invalid segment number: %llu\n",
229		       __func__, (unsigned long long)segnum);
230		return -EINVAL;
231	}
232	down_write(&NILFS_MDT(sufile)->mi_sem);
233
234	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
235	if (ret < 0)
236		goto out_sem;
237
238	ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, create, &bh);
239	if (!ret) {
240		dofunc(sufile, segnum, header_bh, bh);
241		brelse(bh);
242	}
243	brelse(header_bh);
244
245 out_sem:
246	up_write(&NILFS_MDT(sufile)->mi_sem);
247	return ret;
248}
249
250/**
251 * nilfs_sufile_alloc - allocate a segment
252 * @sufile: inode of segment usage file
253 * @segnump: pointer to segment number
254 *
255 * Description: nilfs_sufile_alloc() allocates a clean segment.
256 *
257 * Return Value: On success, 0 is returned and the segment number of the
258 * allocated segment is stored in the place pointed by @segnump. On error, one
259 * of the following negative error codes is returned.
260 *
261 * %-EIO - I/O error.
262 *
263 * %-ENOMEM - Insufficient amount of memory available.
264 *
265 * %-ENOSPC - No clean segment left.
266 */
267int nilfs_sufile_alloc(struct inode *sufile, __u64 *segnump)
268{
269	struct buffer_head *header_bh, *su_bh;
270	struct nilfs_sufile_header *header;
271	struct nilfs_segment_usage *su;
272	size_t susz = NILFS_MDT(sufile)->mi_entry_size;
273	__u64 segnum, maxsegnum, last_alloc;
274	void *kaddr;
275	unsigned long nsegments, ncleansegs, nsus;
276	int ret, i, j;
277
278	down_write(&NILFS_MDT(sufile)->mi_sem);
279
280	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
281	if (ret < 0)
282		goto out_sem;
283	kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
284	header = kaddr + bh_offset(header_bh);
285	ncleansegs = le64_to_cpu(header->sh_ncleansegs);
286	last_alloc = le64_to_cpu(header->sh_last_alloc);
287	kunmap_atomic(kaddr, KM_USER0);
288
289	nsegments = nilfs_sufile_get_nsegments(sufile);
290	segnum = last_alloc + 1;
291	maxsegnum = nsegments - 1;
292	for (i = 0; i < nsegments; i += nsus) {
293		if (segnum >= nsegments) {
294			/* wrap around */
295			segnum = 0;
296			maxsegnum = last_alloc;
297		}
298		ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 1,
299							   &su_bh);
300		if (ret < 0)
301			goto out_header;
302		kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
303		su = nilfs_sufile_block_get_segment_usage(
304			sufile, segnum, su_bh, kaddr);
305
306		nsus = nilfs_sufile_segment_usages_in_block(
307			sufile, segnum, maxsegnum);
308		for (j = 0; j < nsus; j++, su = (void *)su + susz, segnum++) {
309			if (!nilfs_segment_usage_clean(su))
310				continue;
311			/* found a clean segment */
312			nilfs_segment_usage_set_dirty(su);
313			kunmap_atomic(kaddr, KM_USER0);
314
315			kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
316			header = kaddr + bh_offset(header_bh);
317			le64_add_cpu(&header->sh_ncleansegs, -1);
318			le64_add_cpu(&header->sh_ndirtysegs, 1);
319			header->sh_last_alloc = cpu_to_le64(segnum);
320			kunmap_atomic(kaddr, KM_USER0);
321
322			NILFS_SUI(sufile)->ncleansegs--;
323			nilfs_mdt_mark_buffer_dirty(header_bh);
324			nilfs_mdt_mark_buffer_dirty(su_bh);
325			nilfs_mdt_mark_dirty(sufile);
326			brelse(su_bh);
327			*segnump = segnum;
328			goto out_header;
329		}
330
331		kunmap_atomic(kaddr, KM_USER0);
332		brelse(su_bh);
333	}
334
335	/* no segments left */
336	ret = -ENOSPC;
337
338 out_header:
339	brelse(header_bh);
340
341 out_sem:
342	up_write(&NILFS_MDT(sufile)->mi_sem);
343	return ret;
344}
345
346void nilfs_sufile_do_cancel_free(struct inode *sufile, __u64 segnum,
347				 struct buffer_head *header_bh,
348				 struct buffer_head *su_bh)
349{
350	struct nilfs_segment_usage *su;
351	void *kaddr;
352
353	kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
354	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr);
355	if (unlikely(!nilfs_segment_usage_clean(su))) {
356		printk(KERN_WARNING "%s: segment %llu must be clean\n",
357		       __func__, (unsigned long long)segnum);
358		kunmap_atomic(kaddr, KM_USER0);
359		return;
360	}
361	nilfs_segment_usage_set_dirty(su);
362	kunmap_atomic(kaddr, KM_USER0);
363
364	nilfs_sufile_mod_counter(header_bh, -1, 1);
365	NILFS_SUI(sufile)->ncleansegs--;
366
367	nilfs_mdt_mark_buffer_dirty(su_bh);
368	nilfs_mdt_mark_dirty(sufile);
369}
370
371void nilfs_sufile_do_scrap(struct inode *sufile, __u64 segnum,
372			   struct buffer_head *header_bh,
373			   struct buffer_head *su_bh)
374{
375	struct nilfs_segment_usage *su;
376	void *kaddr;
377	int clean, dirty;
378
379	kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
380	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr);
381	if (su->su_flags == cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY) &&
382	    su->su_nblocks == cpu_to_le32(0)) {
383		kunmap_atomic(kaddr, KM_USER0);
384		return;
385	}
386	clean = nilfs_segment_usage_clean(su);
387	dirty = nilfs_segment_usage_dirty(su);
388
389	/* make the segment garbage */
390	su->su_lastmod = cpu_to_le64(0);
391	su->su_nblocks = cpu_to_le32(0);
392	su->su_flags = cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY);
393	kunmap_atomic(kaddr, KM_USER0);
394
395	nilfs_sufile_mod_counter(header_bh, clean ? (u64)-1 : 0, dirty ? 0 : 1);
396	NILFS_SUI(sufile)->ncleansegs -= clean;
397
398	nilfs_mdt_mark_buffer_dirty(su_bh);
399	nilfs_mdt_mark_dirty(sufile);
400}
401
402void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum,
403			  struct buffer_head *header_bh,
404			  struct buffer_head *su_bh)
405{
406	struct nilfs_segment_usage *su;
407	void *kaddr;
408	int sudirty;
409
410	kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
411	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr);
412	if (nilfs_segment_usage_clean(su)) {
413		printk(KERN_WARNING "%s: segment %llu is already clean\n",
414		       __func__, (unsigned long long)segnum);
415		kunmap_atomic(kaddr, KM_USER0);
416		return;
417	}
418	WARN_ON(nilfs_segment_usage_error(su));
419	WARN_ON(!nilfs_segment_usage_dirty(su));
420
421	sudirty = nilfs_segment_usage_dirty(su);
422	nilfs_segment_usage_set_clean(su);
423	kunmap_atomic(kaddr, KM_USER0);
424	nilfs_mdt_mark_buffer_dirty(su_bh);
425
426	nilfs_sufile_mod_counter(header_bh, 1, sudirty ? (u64)-1 : 0);
427	NILFS_SUI(sufile)->ncleansegs++;
428
429	nilfs_mdt_mark_dirty(sufile);
430}
431
432/**
433 * nilfs_sufile_mark_dirty - mark the buffer having a segment usage dirty
434 * @sufile: inode of segment usage file
435 * @segnum: segment number
436 */
437int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum)
438{
439	struct buffer_head *bh;
440	int ret;
441
442	ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &bh);
443	if (!ret) {
444		nilfs_mdt_mark_buffer_dirty(bh);
445		nilfs_mdt_mark_dirty(sufile);
446		brelse(bh);
447	}
448	return ret;
449}
450
451/**
452 * nilfs_sufile_set_segment_usage - set usage of a segment
453 * @sufile: inode of segment usage file
454 * @segnum: segment number
455 * @nblocks: number of live blocks in the segment
456 * @modtime: modification time (option)
457 */
458int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum,
459				   unsigned long nblocks, time_t modtime)
460{
461	struct buffer_head *bh;
462	struct nilfs_segment_usage *su;
463	void *kaddr;
464	int ret;
465
466	down_write(&NILFS_MDT(sufile)->mi_sem);
467	ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &bh);
468	if (ret < 0)
469		goto out_sem;
470
471	kaddr = kmap_atomic(bh->b_page, KM_USER0);
472	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, bh, kaddr);
473	WARN_ON(nilfs_segment_usage_error(su));
474	if (modtime)
475		su->su_lastmod = cpu_to_le64(modtime);
476	su->su_nblocks = cpu_to_le32(nblocks);
477	kunmap_atomic(kaddr, KM_USER0);
478
479	nilfs_mdt_mark_buffer_dirty(bh);
480	nilfs_mdt_mark_dirty(sufile);
481	brelse(bh);
482
483 out_sem:
484	up_write(&NILFS_MDT(sufile)->mi_sem);
485	return ret;
486}
487
488/**
489 * nilfs_sufile_get_stat - get segment usage statistics
490 * @sufile: inode of segment usage file
491 * @stat: pointer to a structure of segment usage statistics
492 *
493 * Description: nilfs_sufile_get_stat() returns information about segment
494 * usage.
495 *
496 * Return Value: On success, 0 is returned, and segment usage information is
497 * stored in the place pointed by @stat. On error, one of the following
498 * negative error codes is returned.
499 *
500 * %-EIO - I/O error.
501 *
502 * %-ENOMEM - Insufficient amount of memory available.
503 */
504int nilfs_sufile_get_stat(struct inode *sufile, struct nilfs_sustat *sustat)
505{
506	struct buffer_head *header_bh;
507	struct nilfs_sufile_header *header;
508	struct the_nilfs *nilfs = NILFS_MDT(sufile)->mi_nilfs;
509	void *kaddr;
510	int ret;
511
512	down_read(&NILFS_MDT(sufile)->mi_sem);
513
514	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
515	if (ret < 0)
516		goto out_sem;
517
518	kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
519	header = kaddr + bh_offset(header_bh);
520	sustat->ss_nsegs = nilfs_sufile_get_nsegments(sufile);
521	sustat->ss_ncleansegs = le64_to_cpu(header->sh_ncleansegs);
522	sustat->ss_ndirtysegs = le64_to_cpu(header->sh_ndirtysegs);
523	sustat->ss_ctime = nilfs->ns_ctime;
524	sustat->ss_nongc_ctime = nilfs->ns_nongc_ctime;
525	spin_lock(&nilfs->ns_last_segment_lock);
526	sustat->ss_prot_seq = nilfs->ns_prot_seq;
527	spin_unlock(&nilfs->ns_last_segment_lock);
528	kunmap_atomic(kaddr, KM_USER0);
529	brelse(header_bh);
530
531 out_sem:
532	up_read(&NILFS_MDT(sufile)->mi_sem);
533	return ret;
534}
535
536void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum,
537			       struct buffer_head *header_bh,
538			       struct buffer_head *su_bh)
539{
540	struct nilfs_segment_usage *su;
541	void *kaddr;
542	int suclean;
543
544	kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
545	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr);
546	if (nilfs_segment_usage_error(su)) {
547		kunmap_atomic(kaddr, KM_USER0);
548		return;
549	}
550	suclean = nilfs_segment_usage_clean(su);
551	nilfs_segment_usage_set_error(su);
552	kunmap_atomic(kaddr, KM_USER0);
553
554	if (suclean) {
555		nilfs_sufile_mod_counter(header_bh, -1, 0);
556		NILFS_SUI(sufile)->ncleansegs--;
557	}
558	nilfs_mdt_mark_buffer_dirty(su_bh);
559	nilfs_mdt_mark_dirty(sufile);
560}
561
562/**
563 * nilfs_sufile_get_suinfo -
564 * @sufile: inode of segment usage file
565 * @segnum: segment number to start looking
566 * @buf: array of suinfo
567 * @sisz: byte size of suinfo
568 * @nsi: size of suinfo array
569 *
570 * Description:
571 *
572 * Return Value: On success, 0 is returned and .... On error, one of the
573 * following negative error codes is returned.
574 *
575 * %-EIO - I/O error.
576 *
577 * %-ENOMEM - Insufficient amount of memory available.
578 */
579ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf,
580				unsigned sisz, size_t nsi)
581{
582	struct buffer_head *su_bh;
583	struct nilfs_segment_usage *su;
584	struct nilfs_suinfo *si = buf;
585	size_t susz = NILFS_MDT(sufile)->mi_entry_size;
586	struct the_nilfs *nilfs = NILFS_MDT(sufile)->mi_nilfs;
587	void *kaddr;
588	unsigned long nsegs, segusages_per_block;
589	ssize_t n;
590	int ret, i, j;
591
592	down_read(&NILFS_MDT(sufile)->mi_sem);
593
594	segusages_per_block = nilfs_sufile_segment_usages_per_block(sufile);
595	nsegs = min_t(unsigned long,
596		      nilfs_sufile_get_nsegments(sufile) - segnum,
597		      nsi);
598	for (i = 0; i < nsegs; i += n, segnum += n) {
599		n = min_t(unsigned long,
600			  segusages_per_block -
601				  nilfs_sufile_get_offset(sufile, segnum),
602			  nsegs - i);
603		ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0,
604							   &su_bh);
605		if (ret < 0) {
606			if (ret != -ENOENT)
607				goto out;
608			/* hole */
609			memset(si, 0, sisz * n);
610			si = (void *)si + sisz * n;
611			continue;
612		}
613
614		kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
615		su = nilfs_sufile_block_get_segment_usage(
616			sufile, segnum, su_bh, kaddr);
617		for (j = 0; j < n;
618		     j++, su = (void *)su + susz, si = (void *)si + sisz) {
619			si->sui_lastmod = le64_to_cpu(su->su_lastmod);
620			si->sui_nblocks = le32_to_cpu(su->su_nblocks);
621			si->sui_flags = le32_to_cpu(su->su_flags) &
622				~(1UL << NILFS_SEGMENT_USAGE_ACTIVE);
623			if (nilfs_segment_is_active(nilfs, segnum + j))
624				si->sui_flags |=
625					(1UL << NILFS_SEGMENT_USAGE_ACTIVE);
626		}
627		kunmap_atomic(kaddr, KM_USER0);
628		brelse(su_bh);
629	}
630	ret = nsegs;
631
632 out:
633	up_read(&NILFS_MDT(sufile)->mi_sem);
634	return ret;
635}
636
637/**
638 * nilfs_sufile_read - read sufile inode
639 * @sufile: sufile inode
640 * @raw_inode: on-disk sufile inode
641 */
642int nilfs_sufile_read(struct inode *sufile, struct nilfs_inode *raw_inode)
643{
644	struct nilfs_sufile_info *sui = NILFS_SUI(sufile);
645	struct buffer_head *header_bh;
646	struct nilfs_sufile_header *header;
647	void *kaddr;
648	int ret;
649
650	ret = nilfs_read_inode_common(sufile, raw_inode);
651	if (ret < 0)
652		return ret;
653
654	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
655	if (!ret) {
656		kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
657		header = kaddr + bh_offset(header_bh);
658		sui->ncleansegs = le64_to_cpu(header->sh_ncleansegs);
659		kunmap_atomic(kaddr, KM_USER0);
660		brelse(header_bh);
661	}
662	return ret;
663}
664
665/**
666 * nilfs_sufile_new - create sufile
667 * @nilfs: nilfs object
668 * @susize: size of a segment usage entry
669 */
670struct inode *nilfs_sufile_new(struct the_nilfs *nilfs, size_t susize)
671{
672	struct inode *sufile;
673
674	sufile = nilfs_mdt_new(nilfs, NULL, NILFS_SUFILE_INO,
675			       sizeof(struct nilfs_sufile_info));
676	if (sufile)
677		nilfs_mdt_set_entry_size(sufile, susize,
678					 sizeof(struct nilfs_sufile_header));
679	return sufile;
680}
681