18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/sysv/balloc.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  minix/bitmap.c
68c2ecf20Sopenharmony_ci *  Copyright (C) 1991, 1992  Linus Torvalds
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  ext/freelists.c
98c2ecf20Sopenharmony_ci *  Copyright (C) 1992  Remy Card (card@masi.ibp.fr)
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *  xenix/alloc.c
128c2ecf20Sopenharmony_ci *  Copyright (C) 1992  Doug Evans
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *  coh/alloc.c
158c2ecf20Sopenharmony_ci *  Copyright (C) 1993  Pascal Haible, Bruno Haible
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *  sysv/balloc.c
188c2ecf20Sopenharmony_ci *  Copyright (C) 1993  Bruno Haible
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci *  This file contains code for allocating/freeing blocks.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/buffer_head.h>
248c2ecf20Sopenharmony_ci#include <linux/string.h>
258c2ecf20Sopenharmony_ci#include "sysv.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* We don't trust the value of
288c2ecf20Sopenharmony_ci   sb->sv_sbd2->s_tfree = *sb->sv_free_blocks
298c2ecf20Sopenharmony_ci   but we nevertheless keep it up to date. */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic inline sysv_zone_t *get_chunk(struct super_block *sb, struct buffer_head *bh)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	char *bh_data = bh->b_data;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	if (SYSV_SB(sb)->s_type == FSTYPE_SYSV4)
368c2ecf20Sopenharmony_ci		return (sysv_zone_t*)(bh_data+4);
378c2ecf20Sopenharmony_ci	else
388c2ecf20Sopenharmony_ci		return (sysv_zone_t*)(bh_data+2);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* NOTE NOTE NOTE: nr is a block number _as_ _stored_ _on_ _disk_ */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_civoid sysv_free_block(struct super_block * sb, sysv_zone_t nr)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct sysv_sb_info * sbi = SYSV_SB(sb);
468c2ecf20Sopenharmony_ci	struct buffer_head * bh;
478c2ecf20Sopenharmony_ci	sysv_zone_t *blocks = sbi->s_bcache;
488c2ecf20Sopenharmony_ci	unsigned count;
498c2ecf20Sopenharmony_ci	unsigned block = fs32_to_cpu(sbi, nr);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/*
528c2ecf20Sopenharmony_ci	 * This code does not work at all for AFS (it has a bitmap
538c2ecf20Sopenharmony_ci	 * free list).  As AFS is supposed to be read-only no one
548c2ecf20Sopenharmony_ci	 * should call this for an AFS filesystem anyway...
558c2ecf20Sopenharmony_ci	 */
568c2ecf20Sopenharmony_ci	if (sbi->s_type == FSTYPE_AFS)
578c2ecf20Sopenharmony_ci		return;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) {
608c2ecf20Sopenharmony_ci		printk("sysv_free_block: trying to free block not in datazone\n");
618c2ecf20Sopenharmony_ci		return;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	mutex_lock(&sbi->s_lock);
658c2ecf20Sopenharmony_ci	count = fs16_to_cpu(sbi, *sbi->s_bcache_count);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (count > sbi->s_flc_size) {
688c2ecf20Sopenharmony_ci		printk("sysv_free_block: flc_count > flc_size\n");
698c2ecf20Sopenharmony_ci		mutex_unlock(&sbi->s_lock);
708c2ecf20Sopenharmony_ci		return;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci	/* If the free list head in super-block is full, it is copied
738c2ecf20Sopenharmony_ci	 * into this block being freed, ditto if it's completely empty
748c2ecf20Sopenharmony_ci	 * (applies only on Coherent).
758c2ecf20Sopenharmony_ci	 */
768c2ecf20Sopenharmony_ci	if (count == sbi->s_flc_size || count == 0) {
778c2ecf20Sopenharmony_ci		block += sbi->s_block_base;
788c2ecf20Sopenharmony_ci		bh = sb_getblk(sb, block);
798c2ecf20Sopenharmony_ci		if (!bh) {
808c2ecf20Sopenharmony_ci			printk("sysv_free_block: getblk() failed\n");
818c2ecf20Sopenharmony_ci			mutex_unlock(&sbi->s_lock);
828c2ecf20Sopenharmony_ci			return;
838c2ecf20Sopenharmony_ci		}
848c2ecf20Sopenharmony_ci		memset(bh->b_data, 0, sb->s_blocksize);
858c2ecf20Sopenharmony_ci		*(__fs16*)bh->b_data = cpu_to_fs16(sbi, count);
868c2ecf20Sopenharmony_ci		memcpy(get_chunk(sb,bh), blocks, count * sizeof(sysv_zone_t));
878c2ecf20Sopenharmony_ci		mark_buffer_dirty(bh);
888c2ecf20Sopenharmony_ci		set_buffer_uptodate(bh);
898c2ecf20Sopenharmony_ci		brelse(bh);
908c2ecf20Sopenharmony_ci		count = 0;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	sbi->s_bcache[count++] = nr;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	*sbi->s_bcache_count = cpu_to_fs16(sbi, count);
958c2ecf20Sopenharmony_ci	fs32_add(sbi, sbi->s_free_blocks, 1);
968c2ecf20Sopenharmony_ci	dirty_sb(sb);
978c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->s_lock);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cisysv_zone_t sysv_new_block(struct super_block * sb)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct sysv_sb_info *sbi = SYSV_SB(sb);
1038c2ecf20Sopenharmony_ci	unsigned int block;
1048c2ecf20Sopenharmony_ci	sysv_zone_t nr;
1058c2ecf20Sopenharmony_ci	struct buffer_head * bh;
1068c2ecf20Sopenharmony_ci	unsigned count;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	mutex_lock(&sbi->s_lock);
1098c2ecf20Sopenharmony_ci	count = fs16_to_cpu(sbi, *sbi->s_bcache_count);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (count == 0) /* Applies only to Coherent FS */
1128c2ecf20Sopenharmony_ci		goto Enospc;
1138c2ecf20Sopenharmony_ci	nr = sbi->s_bcache[--count];
1148c2ecf20Sopenharmony_ci	if (nr == 0)  /* Applies only to Xenix FS, SystemV FS */
1158c2ecf20Sopenharmony_ci		goto Enospc;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	block = fs32_to_cpu(sbi, nr);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	*sbi->s_bcache_count = cpu_to_fs16(sbi, count);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) {
1228c2ecf20Sopenharmony_ci		printk("sysv_new_block: new block %d is not in data zone\n",
1238c2ecf20Sopenharmony_ci			block);
1248c2ecf20Sopenharmony_ci		goto Enospc;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (count == 0) { /* the last block continues the free list */
1288c2ecf20Sopenharmony_ci		unsigned count;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		block += sbi->s_block_base;
1318c2ecf20Sopenharmony_ci		if (!(bh = sb_bread(sb, block))) {
1328c2ecf20Sopenharmony_ci			printk("sysv_new_block: cannot read free-list block\n");
1338c2ecf20Sopenharmony_ci			/* retry this same block next time */
1348c2ecf20Sopenharmony_ci			*sbi->s_bcache_count = cpu_to_fs16(sbi, 1);
1358c2ecf20Sopenharmony_ci			goto Enospc;
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci		count = fs16_to_cpu(sbi, *(__fs16*)bh->b_data);
1388c2ecf20Sopenharmony_ci		if (count > sbi->s_flc_size) {
1398c2ecf20Sopenharmony_ci			printk("sysv_new_block: free-list block with >flc_size entries\n");
1408c2ecf20Sopenharmony_ci			brelse(bh);
1418c2ecf20Sopenharmony_ci			goto Enospc;
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci		*sbi->s_bcache_count = cpu_to_fs16(sbi, count);
1448c2ecf20Sopenharmony_ci		memcpy(sbi->s_bcache, get_chunk(sb, bh),
1458c2ecf20Sopenharmony_ci				count * sizeof(sysv_zone_t));
1468c2ecf20Sopenharmony_ci		brelse(bh);
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci	/* Now the free list head in the superblock is valid again. */
1498c2ecf20Sopenharmony_ci	fs32_add(sbi, sbi->s_free_blocks, -1);
1508c2ecf20Sopenharmony_ci	dirty_sb(sb);
1518c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->s_lock);
1528c2ecf20Sopenharmony_ci	return nr;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ciEnospc:
1558c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->s_lock);
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ciunsigned long sysv_count_free_blocks(struct super_block * sb)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct sysv_sb_info * sbi = SYSV_SB(sb);
1628c2ecf20Sopenharmony_ci	int sb_count;
1638c2ecf20Sopenharmony_ci	int count;
1648c2ecf20Sopenharmony_ci	struct buffer_head * bh = NULL;
1658c2ecf20Sopenharmony_ci	sysv_zone_t *blocks;
1668c2ecf20Sopenharmony_ci	unsigned block;
1678c2ecf20Sopenharmony_ci	int n;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * This code does not work at all for AFS (it has a bitmap
1718c2ecf20Sopenharmony_ci	 * free list).  As AFS is supposed to be read-only we just
1728c2ecf20Sopenharmony_ci	 * lie and say it has no free block at all.
1738c2ecf20Sopenharmony_ci	 */
1748c2ecf20Sopenharmony_ci	if (sbi->s_type == FSTYPE_AFS)
1758c2ecf20Sopenharmony_ci		return 0;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	mutex_lock(&sbi->s_lock);
1788c2ecf20Sopenharmony_ci	sb_count = fs32_to_cpu(sbi, *sbi->s_free_blocks);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (0)
1818c2ecf20Sopenharmony_ci		goto trust_sb;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* this causes a lot of disk traffic ... */
1848c2ecf20Sopenharmony_ci	count = 0;
1858c2ecf20Sopenharmony_ci	n = fs16_to_cpu(sbi, *sbi->s_bcache_count);
1868c2ecf20Sopenharmony_ci	blocks = sbi->s_bcache;
1878c2ecf20Sopenharmony_ci	while (1) {
1888c2ecf20Sopenharmony_ci		sysv_zone_t zone;
1898c2ecf20Sopenharmony_ci		if (n > sbi->s_flc_size)
1908c2ecf20Sopenharmony_ci			goto E2big;
1918c2ecf20Sopenharmony_ci		zone = 0;
1928c2ecf20Sopenharmony_ci		while (n && (zone = blocks[--n]) != 0)
1938c2ecf20Sopenharmony_ci			count++;
1948c2ecf20Sopenharmony_ci		if (zone == 0)
1958c2ecf20Sopenharmony_ci			break;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		block = fs32_to_cpu(sbi, zone);
1988c2ecf20Sopenharmony_ci		if (bh)
1998c2ecf20Sopenharmony_ci			brelse(bh);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		if (block < sbi->s_firstdatazone || block >= sbi->s_nzones)
2028c2ecf20Sopenharmony_ci			goto Einval;
2038c2ecf20Sopenharmony_ci		block += sbi->s_block_base;
2048c2ecf20Sopenharmony_ci		bh = sb_bread(sb, block);
2058c2ecf20Sopenharmony_ci		if (!bh)
2068c2ecf20Sopenharmony_ci			goto Eio;
2078c2ecf20Sopenharmony_ci		n = fs16_to_cpu(sbi, *(__fs16*)bh->b_data);
2088c2ecf20Sopenharmony_ci		blocks = get_chunk(sb, bh);
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci	if (bh)
2118c2ecf20Sopenharmony_ci		brelse(bh);
2128c2ecf20Sopenharmony_ci	if (count != sb_count)
2138c2ecf20Sopenharmony_ci		goto Ecount;
2148c2ecf20Sopenharmony_cidone:
2158c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->s_lock);
2168c2ecf20Sopenharmony_ci	return count;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciEinval:
2198c2ecf20Sopenharmony_ci	printk("sysv_count_free_blocks: new block %d is not in data zone\n",
2208c2ecf20Sopenharmony_ci		block);
2218c2ecf20Sopenharmony_ci	goto trust_sb;
2228c2ecf20Sopenharmony_ciEio:
2238c2ecf20Sopenharmony_ci	printk("sysv_count_free_blocks: cannot read free-list block\n");
2248c2ecf20Sopenharmony_ci	goto trust_sb;
2258c2ecf20Sopenharmony_ciE2big:
2268c2ecf20Sopenharmony_ci	printk("sysv_count_free_blocks: >flc_size entries in free-list block\n");
2278c2ecf20Sopenharmony_ci	if (bh)
2288c2ecf20Sopenharmony_ci		brelse(bh);
2298c2ecf20Sopenharmony_citrust_sb:
2308c2ecf20Sopenharmony_ci	count = sb_count;
2318c2ecf20Sopenharmony_ci	goto done;
2328c2ecf20Sopenharmony_ciEcount:
2338c2ecf20Sopenharmony_ci	printk("sysv_count_free_blocks: free block count was %d, "
2348c2ecf20Sopenharmony_ci		"correcting to %d\n", sb_count, count);
2358c2ecf20Sopenharmony_ci	if (!sb_rdonly(sb)) {
2368c2ecf20Sopenharmony_ci		*sbi->s_free_blocks = cpu_to_fs32(sbi, count);
2378c2ecf20Sopenharmony_ci		dirty_sb(sb);
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci	goto done;
2408c2ecf20Sopenharmony_ci}
241