1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/fs/sysv/balloc.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * minix/bitmap.c
61da177e4SLinus Torvalds * Copyright (C) 1991, 1992 Linus Torvalds
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * ext/freelists.c
91da177e4SLinus Torvalds * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds * xenix/alloc.c
121da177e4SLinus Torvalds * Copyright (C) 1992 Doug Evans
131da177e4SLinus Torvalds *
141da177e4SLinus Torvalds * coh/alloc.c
151da177e4SLinus Torvalds * Copyright (C) 1993 Pascal Haible, Bruno Haible
161da177e4SLinus Torvalds *
171da177e4SLinus Torvalds * sysv/balloc.c
181da177e4SLinus Torvalds * Copyright (C) 1993 Bruno Haible
191da177e4SLinus Torvalds *
201da177e4SLinus Torvalds * This file contains code for allocating/freeing blocks.
211da177e4SLinus Torvalds */
221da177e4SLinus Torvalds
231da177e4SLinus Torvalds #include <linux/buffer_head.h>
241da177e4SLinus Torvalds #include <linux/string.h>
251da177e4SLinus Torvalds #include "sysv.h"
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds /* We don't trust the value of
281da177e4SLinus Torvalds sb->sv_sbd2->s_tfree = *sb->sv_free_blocks
291da177e4SLinus Torvalds but we nevertheless keep it up to date. */
301da177e4SLinus Torvalds
get_chunk(struct super_block * sb,struct buffer_head * bh)311da177e4SLinus Torvalds static inline sysv_zone_t *get_chunk(struct super_block *sb, struct buffer_head *bh)
321da177e4SLinus Torvalds {
331da177e4SLinus Torvalds char *bh_data = bh->b_data;
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds if (SYSV_SB(sb)->s_type == FSTYPE_SYSV4)
361da177e4SLinus Torvalds return (sysv_zone_t*)(bh_data+4);
371da177e4SLinus Torvalds else
381da177e4SLinus Torvalds return (sysv_zone_t*)(bh_data+2);
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds
411da177e4SLinus Torvalds /* NOTE NOTE NOTE: nr is a block number _as_ _stored_ _on_ _disk_ */
421da177e4SLinus Torvalds
sysv_free_block(struct super_block * sb,sysv_zone_t nr)431da177e4SLinus Torvalds void sysv_free_block(struct super_block * sb, sysv_zone_t nr)
441da177e4SLinus Torvalds {
451da177e4SLinus Torvalds struct sysv_sb_info * sbi = SYSV_SB(sb);
461da177e4SLinus Torvalds struct buffer_head * bh;
471da177e4SLinus Torvalds sysv_zone_t *blocks = sbi->s_bcache;
481da177e4SLinus Torvalds unsigned count;
491da177e4SLinus Torvalds unsigned block = fs32_to_cpu(sbi, nr);
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds /*
521da177e4SLinus Torvalds * This code does not work at all for AFS (it has a bitmap
531da177e4SLinus Torvalds * free list). As AFS is supposed to be read-only no one
541da177e4SLinus Torvalds * should call this for an AFS filesystem anyway...
551da177e4SLinus Torvalds */
561da177e4SLinus Torvalds if (sbi->s_type == FSTYPE_AFS)
571da177e4SLinus Torvalds return;
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) {
601da177e4SLinus Torvalds printk("sysv_free_block: trying to free block not in datazone\n");
611da177e4SLinus Torvalds return;
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds
64c07cb01cSMarco Stornelli mutex_lock(&sbi->s_lock);
651da177e4SLinus Torvalds count = fs16_to_cpu(sbi, *sbi->s_bcache_count);
661da177e4SLinus Torvalds
671da177e4SLinus Torvalds if (count > sbi->s_flc_size) {
681da177e4SLinus Torvalds printk("sysv_free_block: flc_count > flc_size\n");
69c07cb01cSMarco Stornelli mutex_unlock(&sbi->s_lock);
701da177e4SLinus Torvalds return;
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds /* If the free list head in super-block is full, it is copied
731da177e4SLinus Torvalds * into this block being freed, ditto if it's completely empty
741da177e4SLinus Torvalds * (applies only on Coherent).
751da177e4SLinus Torvalds */
761da177e4SLinus Torvalds if (count == sbi->s_flc_size || count == 0) {
771da177e4SLinus Torvalds block += sbi->s_block_base;
781da177e4SLinus Torvalds bh = sb_getblk(sb, block);
791da177e4SLinus Torvalds if (!bh) {
801da177e4SLinus Torvalds printk("sysv_free_block: getblk() failed\n");
81c07cb01cSMarco Stornelli mutex_unlock(&sbi->s_lock);
821da177e4SLinus Torvalds return;
831da177e4SLinus Torvalds }
841da177e4SLinus Torvalds memset(bh->b_data, 0, sb->s_blocksize);
851da177e4SLinus Torvalds *(__fs16*)bh->b_data = cpu_to_fs16(sbi, count);
861da177e4SLinus Torvalds memcpy(get_chunk(sb,bh), blocks, count * sizeof(sysv_zone_t));
871da177e4SLinus Torvalds mark_buffer_dirty(bh);
881da177e4SLinus Torvalds set_buffer_uptodate(bh);
891da177e4SLinus Torvalds brelse(bh);
901da177e4SLinus Torvalds count = 0;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds sbi->s_bcache[count++] = nr;
931da177e4SLinus Torvalds
941da177e4SLinus Torvalds *sbi->s_bcache_count = cpu_to_fs16(sbi, count);
951da177e4SLinus Torvalds fs32_add(sbi, sbi->s_free_blocks, 1);
961da177e4SLinus Torvalds dirty_sb(sb);
97c07cb01cSMarco Stornelli mutex_unlock(&sbi->s_lock);
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds
sysv_new_block(struct super_block * sb)1001da177e4SLinus Torvalds sysv_zone_t sysv_new_block(struct super_block * sb)
1011da177e4SLinus Torvalds {
1021da177e4SLinus Torvalds struct sysv_sb_info *sbi = SYSV_SB(sb);
1031da177e4SLinus Torvalds unsigned int block;
1041da177e4SLinus Torvalds sysv_zone_t nr;
1051da177e4SLinus Torvalds struct buffer_head * bh;
1061da177e4SLinus Torvalds unsigned count;
1071da177e4SLinus Torvalds
108c07cb01cSMarco Stornelli mutex_lock(&sbi->s_lock);
1091da177e4SLinus Torvalds count = fs16_to_cpu(sbi, *sbi->s_bcache_count);
1101da177e4SLinus Torvalds
1111da177e4SLinus Torvalds if (count == 0) /* Applies only to Coherent FS */
1121da177e4SLinus Torvalds goto Enospc;
1131da177e4SLinus Torvalds nr = sbi->s_bcache[--count];
1141da177e4SLinus Torvalds if (nr == 0) /* Applies only to Xenix FS, SystemV FS */
1151da177e4SLinus Torvalds goto Enospc;
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds block = fs32_to_cpu(sbi, nr);
1181da177e4SLinus Torvalds
1191da177e4SLinus Torvalds *sbi->s_bcache_count = cpu_to_fs16(sbi, count);
1201da177e4SLinus Torvalds
1211da177e4SLinus Torvalds if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) {
1221da177e4SLinus Torvalds printk("sysv_new_block: new block %d is not in data zone\n",
1231da177e4SLinus Torvalds block);
1241da177e4SLinus Torvalds goto Enospc;
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds
1271da177e4SLinus Torvalds if (count == 0) { /* the last block continues the free list */
1281da177e4SLinus Torvalds unsigned count;
1291da177e4SLinus Torvalds
1301da177e4SLinus Torvalds block += sbi->s_block_base;
1311da177e4SLinus Torvalds if (!(bh = sb_bread(sb, block))) {
1321da177e4SLinus Torvalds printk("sysv_new_block: cannot read free-list block\n");
1331da177e4SLinus Torvalds /* retry this same block next time */
1341da177e4SLinus Torvalds *sbi->s_bcache_count = cpu_to_fs16(sbi, 1);
1351da177e4SLinus Torvalds goto Enospc;
1361da177e4SLinus Torvalds }
1371da177e4SLinus Torvalds count = fs16_to_cpu(sbi, *(__fs16*)bh->b_data);
1381da177e4SLinus Torvalds if (count > sbi->s_flc_size) {
1391da177e4SLinus Torvalds printk("sysv_new_block: free-list block with >flc_size entries\n");
1401da177e4SLinus Torvalds brelse(bh);
1411da177e4SLinus Torvalds goto Enospc;
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds *sbi->s_bcache_count = cpu_to_fs16(sbi, count);
1441da177e4SLinus Torvalds memcpy(sbi->s_bcache, get_chunk(sb, bh),
1451da177e4SLinus Torvalds count * sizeof(sysv_zone_t));
1461da177e4SLinus Torvalds brelse(bh);
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds /* Now the free list head in the superblock is valid again. */
1491da177e4SLinus Torvalds fs32_add(sbi, sbi->s_free_blocks, -1);
1501da177e4SLinus Torvalds dirty_sb(sb);
151c07cb01cSMarco Stornelli mutex_unlock(&sbi->s_lock);
1521da177e4SLinus Torvalds return nr;
1531da177e4SLinus Torvalds
1541da177e4SLinus Torvalds Enospc:
155c07cb01cSMarco Stornelli mutex_unlock(&sbi->s_lock);
1561da177e4SLinus Torvalds return 0;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds
sysv_count_free_blocks(struct super_block * sb)1591da177e4SLinus Torvalds unsigned long sysv_count_free_blocks(struct super_block * sb)
1601da177e4SLinus Torvalds {
1611da177e4SLinus Torvalds struct sysv_sb_info * sbi = SYSV_SB(sb);
1621da177e4SLinus Torvalds int sb_count;
1631da177e4SLinus Torvalds int count;
1641da177e4SLinus Torvalds struct buffer_head * bh = NULL;
1651da177e4SLinus Torvalds sysv_zone_t *blocks;
1661da177e4SLinus Torvalds unsigned block;
1671da177e4SLinus Torvalds int n;
1681da177e4SLinus Torvalds
1691da177e4SLinus Torvalds /*
1701da177e4SLinus Torvalds * This code does not work at all for AFS (it has a bitmap
1711da177e4SLinus Torvalds * free list). As AFS is supposed to be read-only we just
1721da177e4SLinus Torvalds * lie and say it has no free block at all.
1731da177e4SLinus Torvalds */
1741da177e4SLinus Torvalds if (sbi->s_type == FSTYPE_AFS)
1751da177e4SLinus Torvalds return 0;
1761da177e4SLinus Torvalds
177c07cb01cSMarco Stornelli mutex_lock(&sbi->s_lock);
1781da177e4SLinus Torvalds sb_count = fs32_to_cpu(sbi, *sbi->s_free_blocks);
1791da177e4SLinus Torvalds
1801da177e4SLinus Torvalds if (0)
1811da177e4SLinus Torvalds goto trust_sb;
1821da177e4SLinus Torvalds
1831da177e4SLinus Torvalds /* this causes a lot of disk traffic ... */
1841da177e4SLinus Torvalds count = 0;
1851da177e4SLinus Torvalds n = fs16_to_cpu(sbi, *sbi->s_bcache_count);
1861da177e4SLinus Torvalds blocks = sbi->s_bcache;
1871da177e4SLinus Torvalds while (1) {
1881da177e4SLinus Torvalds sysv_zone_t zone;
1891da177e4SLinus Torvalds if (n > sbi->s_flc_size)
1901da177e4SLinus Torvalds goto E2big;
1911da177e4SLinus Torvalds zone = 0;
1921da177e4SLinus Torvalds while (n && (zone = blocks[--n]) != 0)
1931da177e4SLinus Torvalds count++;
1941da177e4SLinus Torvalds if (zone == 0)
1951da177e4SLinus Torvalds break;
1961da177e4SLinus Torvalds
1971da177e4SLinus Torvalds block = fs32_to_cpu(sbi, zone);
1981da177e4SLinus Torvalds if (bh)
1991da177e4SLinus Torvalds brelse(bh);
2001da177e4SLinus Torvalds
2011da177e4SLinus Torvalds if (block < sbi->s_firstdatazone || block >= sbi->s_nzones)
2021da177e4SLinus Torvalds goto Einval;
2031da177e4SLinus Torvalds block += sbi->s_block_base;
2041da177e4SLinus Torvalds bh = sb_bread(sb, block);
2051da177e4SLinus Torvalds if (!bh)
2061da177e4SLinus Torvalds goto Eio;
2071da177e4SLinus Torvalds n = fs16_to_cpu(sbi, *(__fs16*)bh->b_data);
2081da177e4SLinus Torvalds blocks = get_chunk(sb, bh);
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds if (bh)
2111da177e4SLinus Torvalds brelse(bh);
2121da177e4SLinus Torvalds if (count != sb_count)
2131da177e4SLinus Torvalds goto Ecount;
2141da177e4SLinus Torvalds done:
215c07cb01cSMarco Stornelli mutex_unlock(&sbi->s_lock);
2161da177e4SLinus Torvalds return count;
2171da177e4SLinus Torvalds
2181da177e4SLinus Torvalds Einval:
2191da177e4SLinus Torvalds printk("sysv_count_free_blocks: new block %d is not in data zone\n",
2201da177e4SLinus Torvalds block);
2211da177e4SLinus Torvalds goto trust_sb;
2221da177e4SLinus Torvalds Eio:
2231da177e4SLinus Torvalds printk("sysv_count_free_blocks: cannot read free-list block\n");
2241da177e4SLinus Torvalds goto trust_sb;
2251da177e4SLinus Torvalds E2big:
2261da177e4SLinus Torvalds printk("sysv_count_free_blocks: >flc_size entries in free-list block\n");
2271da177e4SLinus Torvalds if (bh)
2281da177e4SLinus Torvalds brelse(bh);
2291da177e4SLinus Torvalds trust_sb:
2301da177e4SLinus Torvalds count = sb_count;
2311da177e4SLinus Torvalds goto done;
2321da177e4SLinus Torvalds Ecount:
2331da177e4SLinus Torvalds printk("sysv_count_free_blocks: free block count was %d, "
2341da177e4SLinus Torvalds "correcting to %d\n", sb_count, count);
235bc98a42cSDavid Howells if (!sb_rdonly(sb)) {
2361da177e4SLinus Torvalds *sbi->s_free_blocks = cpu_to_fs32(sbi, count);
2371da177e4SLinus Torvalds dirty_sb(sb);
2381da177e4SLinus Torvalds }
2391da177e4SLinus Torvalds goto done;
2401da177e4SLinus Torvalds }
241