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