Merge branch 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux...
[linux-2.6] / fs / nilfs2 / sufile.c
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 }