xref: /openbmc/linux/fs/sysv/balloc.c (revision b2441318)
1b2441318SGreg 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