1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/fs/hfsplus/bitmap.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 2001
61da177e4SLinus Torvalds * Brad Boyer (flar@allandria.com)
71da177e4SLinus Torvalds * (C) 2003 Ardis Technologies <roman@ardistech.com>
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * Handling of allocation file
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include <linux/pagemap.h>
131da177e4SLinus Torvalds
141da177e4SLinus Torvalds #include "hfsplus_fs.h"
151da177e4SLinus Torvalds #include "hfsplus_raw.h"
161da177e4SLinus Torvalds
1709cbfeafSKirill A. Shutemov #define PAGE_CACHE_BITS (PAGE_SIZE * 8)
181da177e4SLinus Torvalds
hfsplus_block_allocate(struct super_block * sb,u32 size,u32 offset,u32 * max)192753cc28SAnton Salikhmetov int hfsplus_block_allocate(struct super_block *sb, u32 size,
202753cc28SAnton Salikhmetov u32 offset, u32 *max)
211da177e4SLinus Torvalds {
22dd73a01aSChristoph Hellwig struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
231da177e4SLinus Torvalds struct page *page;
241da177e4SLinus Torvalds struct address_space *mapping;
251da177e4SLinus Torvalds __be32 *pptr, *curr, *end;
261da177e4SLinus Torvalds u32 mask, start, len, n;
271da177e4SLinus Torvalds __be32 val;
281da177e4SLinus Torvalds int i;
291da177e4SLinus Torvalds
301da177e4SLinus Torvalds len = *max;
311da177e4SLinus Torvalds if (!len)
321da177e4SLinus Torvalds return size;
331da177e4SLinus Torvalds
34c2b3e1f7SJoe Perches hfs_dbg(BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len);
35dd73a01aSChristoph Hellwig mutex_lock(&sbi->alloc_mutex);
36dd73a01aSChristoph Hellwig mapping = sbi->alloc_file->i_mapping;
37090d2b18SPekka Enberg page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, NULL);
38649f1ee6SEric Sesterhenn if (IS_ERR(page)) {
39649f1ee6SEric Sesterhenn start = size;
40649f1ee6SEric Sesterhenn goto out;
41649f1ee6SEric Sesterhenn }
42*f9ef3b95SFabio M. De Francesco pptr = kmap_local_page(page);
431da177e4SLinus Torvalds curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32;
441da177e4SLinus Torvalds i = offset % 32;
451da177e4SLinus Torvalds offset &= ~(PAGE_CACHE_BITS - 1);
461da177e4SLinus Torvalds if ((size ^ offset) / PAGE_CACHE_BITS)
471da177e4SLinus Torvalds end = pptr + PAGE_CACHE_BITS / 32;
481da177e4SLinus Torvalds else
491da177e4SLinus Torvalds end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds /* scan the first partial u32 for zero bits */
521da177e4SLinus Torvalds val = *curr;
531da177e4SLinus Torvalds if (~val) {
541da177e4SLinus Torvalds n = be32_to_cpu(val);
551da177e4SLinus Torvalds mask = (1U << 31) >> i;
561da177e4SLinus Torvalds for (; i < 32; mask >>= 1, i++) {
571da177e4SLinus Torvalds if (!(n & mask))
581da177e4SLinus Torvalds goto found;
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds }
611da177e4SLinus Torvalds curr++;
621da177e4SLinus Torvalds
631da177e4SLinus Torvalds /* scan complete u32s for the first zero bit */
641da177e4SLinus Torvalds while (1) {
651da177e4SLinus Torvalds while (curr < end) {
661da177e4SLinus Torvalds val = *curr;
671da177e4SLinus Torvalds if (~val) {
681da177e4SLinus Torvalds n = be32_to_cpu(val);
691da177e4SLinus Torvalds mask = 1 << 31;
701da177e4SLinus Torvalds for (i = 0; i < 32; mask >>= 1, i++) {
711da177e4SLinus Torvalds if (!(n & mask))
721da177e4SLinus Torvalds goto found;
731da177e4SLinus Torvalds }
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds curr++;
761da177e4SLinus Torvalds }
77*f9ef3b95SFabio M. De Francesco kunmap_local(pptr);
781da177e4SLinus Torvalds offset += PAGE_CACHE_BITS;
791da177e4SLinus Torvalds if (offset >= size)
801da177e4SLinus Torvalds break;
81090d2b18SPekka Enberg page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS,
82090d2b18SPekka Enberg NULL);
83649f1ee6SEric Sesterhenn if (IS_ERR(page)) {
84649f1ee6SEric Sesterhenn start = size;
85649f1ee6SEric Sesterhenn goto out;
86649f1ee6SEric Sesterhenn }
87*f9ef3b95SFabio M. De Francesco curr = pptr = kmap_local_page(page);
881da177e4SLinus Torvalds if ((size ^ offset) / PAGE_CACHE_BITS)
891da177e4SLinus Torvalds end = pptr + PAGE_CACHE_BITS / 32;
901da177e4SLinus Torvalds else
911da177e4SLinus Torvalds end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
921da177e4SLinus Torvalds }
93c2b3e1f7SJoe Perches hfs_dbg(BITMAP, "bitmap full\n");
941da177e4SLinus Torvalds start = size;
951da177e4SLinus Torvalds goto out;
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds found:
981da177e4SLinus Torvalds start = offset + (curr - pptr) * 32 + i;
991da177e4SLinus Torvalds if (start >= size) {
100c2b3e1f7SJoe Perches hfs_dbg(BITMAP, "bitmap full\n");
1011da177e4SLinus Torvalds goto out;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds /* do any partial u32 at the start */
1041da177e4SLinus Torvalds len = min(size - start, len);
1051da177e4SLinus Torvalds while (1) {
1061da177e4SLinus Torvalds n |= mask;
1071da177e4SLinus Torvalds if (++i >= 32)
1081da177e4SLinus Torvalds break;
1091da177e4SLinus Torvalds mask >>= 1;
1101da177e4SLinus Torvalds if (!--len || n & mask)
1111da177e4SLinus Torvalds goto done;
1121da177e4SLinus Torvalds }
1131da177e4SLinus Torvalds if (!--len)
1141da177e4SLinus Torvalds goto done;
1151da177e4SLinus Torvalds *curr++ = cpu_to_be32(n);
1161da177e4SLinus Torvalds /* do full u32s */
1171da177e4SLinus Torvalds while (1) {
1181da177e4SLinus Torvalds while (curr < end) {
1191da177e4SLinus Torvalds n = be32_to_cpu(*curr);
1201da177e4SLinus Torvalds if (len < 32)
1211da177e4SLinus Torvalds goto last;
1221da177e4SLinus Torvalds if (n) {
1231da177e4SLinus Torvalds len = 32;
1241da177e4SLinus Torvalds goto last;
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds *curr++ = cpu_to_be32(0xffffffff);
1271da177e4SLinus Torvalds len -= 32;
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds set_page_dirty(page);
130*f9ef3b95SFabio M. De Francesco kunmap_local(pptr);
1311da177e4SLinus Torvalds offset += PAGE_CACHE_BITS;
132090d2b18SPekka Enberg page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS,
133090d2b18SPekka Enberg NULL);
134649f1ee6SEric Sesterhenn if (IS_ERR(page)) {
135649f1ee6SEric Sesterhenn start = size;
136649f1ee6SEric Sesterhenn goto out;
137649f1ee6SEric Sesterhenn }
138*f9ef3b95SFabio M. De Francesco pptr = kmap_local_page(page);
1391da177e4SLinus Torvalds curr = pptr;
1401da177e4SLinus Torvalds end = pptr + PAGE_CACHE_BITS / 32;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds last:
1431da177e4SLinus Torvalds /* do any partial u32 at end */
1441da177e4SLinus Torvalds mask = 1U << 31;
1451da177e4SLinus Torvalds for (i = 0; i < len; i++) {
1461da177e4SLinus Torvalds if (n & mask)
1471da177e4SLinus Torvalds break;
1481da177e4SLinus Torvalds n |= mask;
1491da177e4SLinus Torvalds mask >>= 1;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds done:
1521da177e4SLinus Torvalds *curr = cpu_to_be32(n);
1531da177e4SLinus Torvalds set_page_dirty(page);
154*f9ef3b95SFabio M. De Francesco kunmap_local(pptr);
1551da177e4SLinus Torvalds *max = offset + (curr - pptr) * 32 + i - start;
156dd73a01aSChristoph Hellwig sbi->free_blocks -= *max;
1579e6c5829SArtem Bityutskiy hfsplus_mark_mdb_dirty(sb);
158c2b3e1f7SJoe Perches hfs_dbg(BITMAP, "-> %u,%u\n", start, *max);
1591da177e4SLinus Torvalds out:
160dd73a01aSChristoph Hellwig mutex_unlock(&sbi->alloc_mutex);
1611da177e4SLinus Torvalds return start;
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds
hfsplus_block_free(struct super_block * sb,u32 offset,u32 count)1641da177e4SLinus Torvalds int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count)
1651da177e4SLinus Torvalds {
166dd73a01aSChristoph Hellwig struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
1671da177e4SLinus Torvalds struct page *page;
1681da177e4SLinus Torvalds struct address_space *mapping;
1691da177e4SLinus Torvalds __be32 *pptr, *curr, *end;
1701da177e4SLinus Torvalds u32 mask, len, pnr;
1711da177e4SLinus Torvalds int i;
1721da177e4SLinus Torvalds
1731da177e4SLinus Torvalds /* is there any actual work to be done? */
1741da177e4SLinus Torvalds if (!count)
1751da177e4SLinus Torvalds return 0;
1761da177e4SLinus Torvalds
177c2b3e1f7SJoe Perches hfs_dbg(BITMAP, "block_free: %u,%u\n", offset, count);
1781da177e4SLinus Torvalds /* are all of the bits in range? */
179dd73a01aSChristoph Hellwig if ((offset + count) > sbi->total_blocks)
1805daa669cSAlan Cox return -ENOENT;
1811da177e4SLinus Torvalds
182dd73a01aSChristoph Hellwig mutex_lock(&sbi->alloc_mutex);
183dd73a01aSChristoph Hellwig mapping = sbi->alloc_file->i_mapping;
1841da177e4SLinus Torvalds pnr = offset / PAGE_CACHE_BITS;
185090d2b18SPekka Enberg page = read_mapping_page(mapping, pnr, NULL);
1865daa669cSAlan Cox if (IS_ERR(page))
1875daa669cSAlan Cox goto kaboom;
188*f9ef3b95SFabio M. De Francesco pptr = kmap_local_page(page);
1891da177e4SLinus Torvalds curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32;
1901da177e4SLinus Torvalds end = pptr + PAGE_CACHE_BITS / 32;
1911da177e4SLinus Torvalds len = count;
1921da177e4SLinus Torvalds
1931da177e4SLinus Torvalds /* do any partial u32 at the start */
1941da177e4SLinus Torvalds i = offset % 32;
1951da177e4SLinus Torvalds if (i) {
1961da177e4SLinus Torvalds int j = 32 - i;
1971da177e4SLinus Torvalds mask = 0xffffffffU << j;
1981da177e4SLinus Torvalds if (j > count) {
1991da177e4SLinus Torvalds mask |= 0xffffffffU >> (i + count);
2001da177e4SLinus Torvalds *curr++ &= cpu_to_be32(mask);
2011da177e4SLinus Torvalds goto out;
2021da177e4SLinus Torvalds }
2031da177e4SLinus Torvalds *curr++ &= cpu_to_be32(mask);
2041da177e4SLinus Torvalds count -= j;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds
2071da177e4SLinus Torvalds /* do full u32s */
2081da177e4SLinus Torvalds while (1) {
2091da177e4SLinus Torvalds while (curr < end) {
2101da177e4SLinus Torvalds if (count < 32)
2111da177e4SLinus Torvalds goto done;
2121da177e4SLinus Torvalds *curr++ = 0;
2131da177e4SLinus Torvalds count -= 32;
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds if (!count)
2161da177e4SLinus Torvalds break;
2171da177e4SLinus Torvalds set_page_dirty(page);
218*f9ef3b95SFabio M. De Francesco kunmap_local(pptr);
219090d2b18SPekka Enberg page = read_mapping_page(mapping, ++pnr, NULL);
2205daa669cSAlan Cox if (IS_ERR(page))
2215daa669cSAlan Cox goto kaboom;
222*f9ef3b95SFabio M. De Francesco pptr = kmap_local_page(page);
2231da177e4SLinus Torvalds curr = pptr;
2241da177e4SLinus Torvalds end = pptr + PAGE_CACHE_BITS / 32;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds done:
2271da177e4SLinus Torvalds /* do any partial u32 at end */
2281da177e4SLinus Torvalds if (count) {
2291da177e4SLinus Torvalds mask = 0xffffffffU >> count;
2301da177e4SLinus Torvalds *curr &= cpu_to_be32(mask);
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds out:
2331da177e4SLinus Torvalds set_page_dirty(page);
234*f9ef3b95SFabio M. De Francesco kunmap_local(pptr);
235dd73a01aSChristoph Hellwig sbi->free_blocks += len;
2369e6c5829SArtem Bityutskiy hfsplus_mark_mdb_dirty(sb);
237dd73a01aSChristoph Hellwig mutex_unlock(&sbi->alloc_mutex);
2381da177e4SLinus Torvalds
2391da177e4SLinus Torvalds return 0;
2405daa669cSAlan Cox
2415daa669cSAlan Cox kaboom:
242865f38a3SVyacheslav Dubeyko pr_crit("unable to mark blocks free: error %ld\n", PTR_ERR(page));
2435daa669cSAlan Cox mutex_unlock(&sbi->alloc_mutex);
2445daa669cSAlan Cox
2455daa669cSAlan Cox return -EIO;
2461da177e4SLinus Torvalds }
247