/** * struct nilfs_sufile_info - on-memory private data of sufile * @mi: on-memory private data of metadata file * @ncleansegs: number of clean segments * @allocmin: lower limit of allocatable segment range * @allocmax: upper limit of allocatable segment range
*/ struct nilfs_sufile_info { struct nilfs_mdt_info mi; unsignedlong ncleansegs;/* number of clean segments */
__u64 allocmin; /* lower limit of allocatable segment range */
__u64 allocmax; /* upper limit of allocatable segment range */
};
/** * nilfs_sufile_get_ncleansegs - return the number of clean segments * @sufile: inode of segment usage file * * Return: Number of clean segments.
*/ unsignedlong nilfs_sufile_get_ncleansegs(struct inode *sufile)
{ return NILFS_SUI(sufile)->ncleansegs;
}
/** * nilfs_sufile_updatev - modify multiple segment usages at a time * @sufile: inode of segment usage file * @segnumv: array of segment numbers * @nsegs: size of @segnumv array * @create: creation flag * @ndone: place to store number of modified segments on @segnumv * @dofunc: primitive operation for the update * * Description: nilfs_sufile_updatev() repeatedly calls @dofunc * against the given array of segments. The @dofunc is called with * buffers of a header block and the sufile block in which the target * segment usage entry is contained. If @ndone is given, the number * of successfully modified segments from the head is stored in the * place @ndone points to. * * Return: 0 on success, or one of the following negative error codes on * failure: * * %-EINVAL - Invalid segment usage number * * %-EIO - I/O error (including metadata corruption). * * %-ENOENT - Given segment usage is in hole block (may be returned if * @create is zero) * * %-ENOMEM - Insufficient memory available.
*/ int nilfs_sufile_updatev(struct inode *sufile, __u64 *segnumv, size_t nsegs, int create, size_t *ndone, void (*dofunc)(struct inode *, __u64, struct buffer_head *, struct buffer_head *))
{ struct buffer_head *header_bh, *bh; unsignedlong blkoff, prev_blkoff;
__u64 *seg;
size_t nerr = 0, n = 0; int ret = 0;
if (unlikely(nsegs == 0)) goto out;
down_write(&NILFS_MDT(sufile)->mi_sem); for (seg = segnumv; seg < segnumv + nsegs; seg++) { if (unlikely(*seg >= nilfs_sufile_get_nsegments(sufile))) {
nilfs_warn(sufile->i_sb, "%s: invalid segment number: %llu",
__func__, (unsignedlonglong)*seg);
nerr++;
}
} if (nerr > 0) {
ret = -EINVAL; goto out_sem;
}
ret = nilfs_sufile_get_header_block(sufile, &header_bh); if (ret < 0) goto out_sem;
seg = segnumv;
blkoff = nilfs_sufile_get_blkoff(sufile, *seg);
ret = nilfs_mdt_get_block(sufile, blkoff, create, NULL, &bh); if (ret < 0) goto out_header;
for (;;) {
dofunc(sufile, *seg, header_bh, bh);
if (++seg >= segnumv + nsegs) break;
prev_blkoff = blkoff;
blkoff = nilfs_sufile_get_blkoff(sufile, *seg); if (blkoff == prev_blkoff) continue;
/* get different block */
brelse(bh);
ret = nilfs_mdt_get_block(sufile, blkoff, create, NULL, &bh); if (unlikely(ret < 0)) goto out_header;
}
brelse(bh);
out_header:
n = seg - segnumv;
brelse(header_bh);
out_sem:
up_write(&NILFS_MDT(sufile)->mi_sem);
out: if (ndone)
*ndone = n; return ret;
}
int nilfs_sufile_update(struct inode *sufile, __u64 segnum, int create, void (*dofunc)(struct inode *, __u64, struct buffer_head *, struct buffer_head *))
{ struct buffer_head *header_bh, *bh; int ret;
/** * nilfs_sufile_set_alloc_range - limit range of segment to be allocated * @sufile: inode of segment usage file * @start: minimum segment number of allocatable region (inclusive) * @end: maximum segment number of allocatable region (inclusive) * * Return: 0 on success, or %-ERANGE if segment range is invalid.
*/ int nilfs_sufile_set_alloc_range(struct inode *sufile, __u64 start, __u64 end)
{ struct nilfs_sufile_info *sui = NILFS_SUI(sufile);
__u64 nsegs; int ret = -ERANGE;
/** * nilfs_sufile_mark_dirty - mark the buffer having a segment usage dirty * @sufile: inode of segment usage file * @segnum: segment number * * Return: 0 on success, or a negative error code on failure.
*/ int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum)
{ struct buffer_head *bh;
size_t offset; struct nilfs_segment_usage *su; int ret;
down_write(&NILFS_MDT(sufile)->mi_sem);
ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &bh); if (unlikely(ret)) { if (ret == -ENOENT) {
nilfs_error(sufile->i_sb, "segment usage for segment %llu is unreadable due to a hole block",
(unsignedlonglong)segnum);
ret = -EIO;
} goto out_sem;
}
offset = nilfs_sufile_segment_usage_offset(sufile, segnum, bh);
su = kmap_local_folio(bh->b_folio, offset); if (unlikely(nilfs_segment_usage_error(su))) { struct the_nilfs *nilfs = sufile->i_sb->s_fs_info;
kunmap_local(su);
brelse(bh); if (nilfs_segment_is_active(nilfs, segnum)) {
nilfs_error(sufile->i_sb, "active segment %llu is erroneous",
(unsignedlonglong)segnum);
} else { /* * Segments marked erroneous are never allocated by * nilfs_sufile_alloc(); only active segments, ie, * the segments indexed by ns_segnum or ns_nextnum, * can be erroneous here.
*/
WARN_ON_ONCE(1);
}
ret = -EIO;
} else {
nilfs_segment_usage_set_dirty(su);
kunmap_local(su);
mark_buffer_dirty(bh);
nilfs_mdt_mark_dirty(sufile);
brelse(bh);
}
out_sem:
up_write(&NILFS_MDT(sufile)->mi_sem); return ret;
}
/** * nilfs_sufile_set_segment_usage - set usage of a segment * @sufile: inode of segment usage file * @segnum: segment number * @nblocks: number of live blocks in the segment * @modtime: modification time (option) * * Return: 0 on success, or a negative error code on failure.
*/ int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum, unsignedlong nblocks, time64_t modtime)
{ struct buffer_head *bh; struct nilfs_segment_usage *su;
size_t offset; int ret;
down_write(&NILFS_MDT(sufile)->mi_sem);
ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &bh); if (ret < 0) goto out_sem;
offset = nilfs_sufile_segment_usage_offset(sufile, segnum, bh);
su = kmap_local_folio(bh->b_folio, offset); if (modtime) { /* * Check segusage error and set su_lastmod only when updating * this entry with a valid timestamp, not for cancellation.
*/
WARN_ON_ONCE(nilfs_segment_usage_error(su));
su->su_lastmod = cpu_to_le64(modtime);
}
su->su_nblocks = cpu_to_le32(nblocks);
kunmap_local(su);
/** * nilfs_sufile_get_stat - get segment usage statistics * @sufile: inode of segment usage file * @sustat: pointer to a structure of segment usage statistics * * Description: nilfs_sufile_get_stat() retrieves segment usage statistics * and stores them in the location pointed to by @sustat. * * Return: 0 on success, or one of the following negative error codes on * failure: * * %-EIO - I/O error (including metadata corruption). * * %-ENOMEM - Insufficient memory available.
*/ int nilfs_sufile_get_stat(struct inode *sufile, struct nilfs_sustat *sustat)
{ struct buffer_head *header_bh; struct nilfs_sufile_header *header; struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; int ret;
down_read(&NILFS_MDT(sufile)->mi_sem);
ret = nilfs_sufile_get_header_block(sufile, &header_bh); if (ret < 0) goto out_sem;
/** * nilfs_sufile_resize - resize segment array * @sufile: inode of segment usage file * @newnsegs: new number of segments * * Return: 0 on success, or one of the following negative error codes on * failure: * * %-EBUSY - Dirty or active segments exist in the region to be truncated. * * %-EIO - I/O error (including metadata corruption). * * %-ENOMEM - Insufficient memory available. * * %-ENOSPC - Enough free space is not left for shrinking.
*/ int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs)
{ struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; struct buffer_head *header_bh; struct nilfs_sufile_header *header; struct nilfs_sufile_info *sui = NILFS_SUI(sufile); unsignedlong nsegs, nrsvsegs; int ret = 0;
down_write(&NILFS_MDT(sufile)->mi_sem);
nsegs = nilfs_sufile_get_nsegments(sufile); if (nsegs == newnsegs) goto out;
ret = nilfs_sufile_get_header_block(sufile, &header_bh); if (ret < 0) goto out;
if (newnsegs > nsegs) {
sui->ncleansegs += newnsegs - nsegs;
} else/* newnsegs < nsegs */ {
ret = nilfs_sufile_truncate_range(sufile, newnsegs, nsegs - 1); if (ret < 0) goto out_header;
sui->ncleansegs -= nsegs - newnsegs;
/* * If the sufile is successfully truncated, immediately adjust * the segment allocation space while locking the semaphore * "mi_sem" so that nilfs_sufile_alloc() never allocates * segments in the truncated space.
*/
sui->allocmax = newnsegs - 1;
sui->allocmin = 0;
}
ret = nilfs_sufile_get_header_block(sufile, &header_bh); if (ret < 0) goto out_sem;
sup = buf;
blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum);
ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh); if (ret < 0) goto out_header;
for (;;) {
offset = nilfs_sufile_segment_usage_offset(
sufile, sup->sup_segnum, bh);
su = kmap_local_folio(bh->b_folio, offset);
if (nilfs_suinfo_update_lastmod(sup))
su->su_lastmod = cpu_to_le64(sup->sup_sui.sui_lastmod);
if (nilfs_suinfo_update_nblocks(sup))
su->su_nblocks = cpu_to_le32(sup->sup_sui.sui_nblocks);
if (nilfs_suinfo_update_flags(sup)) { /* * Active flag is a virtual flag projected by running * nilfs kernel code - drop it not to write it to * disk.
*/
sup->sup_sui.sui_flags &=
~BIT(NILFS_SEGMENT_USAGE_ACTIVE);
/** * nilfs_sufile_trim_fs() - trim ioctl handle function * @sufile: inode of segment usage file * @range: fstrim_range structure * * start: First Byte to trim * len: number of Bytes to trim from start * minlen: minimum extent length in Bytes * * Decription: nilfs_sufile_trim_fs goes through all segments containing bytes * from start to start+len. start is rounded up to the next block boundary * and start+len is rounded down. For each clean segment blkdev_issue_discard * function is invoked. * * Return: 0 on success, or a negative error code on failure.
*/ int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range)
{ struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; struct buffer_head *su_bh; struct nilfs_segment_usage *su;
size_t offset; void *kaddr;
size_t n, i, susz = NILFS_MDT(sufile)->mi_entry_size;
sector_t seg_start, seg_end, start_block, end_block;
sector_t start = 0, nblocks = 0;
u64 segnum, segnum_end, minlen, len, max_blocks, ndiscarded = 0; int ret = 0; unsignedint sects_per_block;
/* * range->len can be very large (actually, it is set to * ULLONG_MAX by default) - truncate upper end of the range * carefully so as not to overflow.
*/ if (max_blocks - start_block < len)
end_block = max_blocks - 1; else
end_block = start_block + len - 1;
while (segnum <= segnum_end) {
n = nilfs_sufile_segment_usages_in_block(sufile, segnum,
segnum_end);
ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0,
&su_bh); if (ret < 0) { if (ret != -ENOENT) goto out_sem; /* hole */
segnum += n; continue;
}
offset = nilfs_sufile_segment_usage_offset(sufile, segnum,
su_bh);
su = kaddr = kmap_local_folio(su_bh->b_folio, offset); for (i = 0; i < n; ++i, ++segnum, su = (void *)su + susz) { if (!nilfs_segment_usage_clean(su)) continue;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.