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