123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- /*
- * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
- /************************************************************************/
- /* */
- /* PROJECT : exFAT & FAT12/16/32 File System */
- /* FILE : blkdev.c */
- /* PURPOSE : sdFAT Block Device Driver Glue Layer */
- /* */
- /*----------------------------------------------------------------------*/
- /* NOTES */
- /* */
- /************************************************************************/
- #include <linux/blkdev.h>
- #include <linux/log2.h>
- #include <linux/backing-dev.h>
- #include "sdfat.h"
- /*----------------------------------------------------------------------*/
- /* Constant & Macro Definitions */
- /*----------------------------------------------------------------------*/
- /*----------------------------------------------------------------------*/
- /* Global Variable Definitions */
- /*----------------------------------------------------------------------*/
- /*----------------------------------------------------------------------*/
- /* Local Variable Definitions */
- /*----------------------------------------------------------------------*/
- /*----------------------------------------------------------------------*/
- /* FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY */
- /************************************************************************/
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
- /* EMPTY */
- #else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) */
- static struct backing_dev_info *inode_to_bdi(struct inode *bd_inode)
- {
- return bd_inode->i_mapping->backing_dev_info;
- }
- #endif
- /*======================================================================*/
- /* Function Definitions */
- /*======================================================================*/
- s32 bdev_open_dev(struct super_block *sb)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (fsi->bd_opened)
- return 0;
- fsi->bd_opened = true;
- return 0;
- }
- s32 bdev_close_dev(struct super_block *sb)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- fsi->bd_opened = false;
- return 0;
- }
- static inline s32 block_device_ejected(struct super_block *sb)
- {
- struct inode *bd_inode = sb->s_bdev->bd_inode;
- struct backing_dev_info *bdi = inode_to_bdi(bd_inode);
- return (bdi->dev == NULL);
- }
- s32 bdev_check_bdi_valid(struct super_block *sb)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (block_device_ejected(sb)) {
- if (!(fsi->prev_eio & SDFAT_EIO_BDI)) {
- fsi->prev_eio |= SDFAT_EIO_BDI;
- sdfat_log_msg(sb, KERN_ERR, "%s: block device is "
- "eliminated.(bdi:%p)", __func__, sb->s_bdi);
- }
- return -ENXIO;
- }
- return 0;
- }
- #if IS_BUILTIN(CONFIG_SDFAT_FS)
- static void __bdev_readahead(struct super_block *sb, u64 secno, u64 num_secs)
- {
- u32 sects_per_page = (PAGE_SIZE >> sb->s_blocksize_bits);
- struct blk_plug plug;
- u64 i;
- blk_start_plug(&plug);
- for (i = 0; i < num_secs; i++) {
- if (i && !(i & (sects_per_page - 1)))
- blk_flush_plug(current);
- sb_breadahead(sb, (sector_t)(secno + i));
- }
- blk_finish_plug(&plug);
- }
- #else
- static void __bdev_readahead(struct super_block *sb, u64 secno, u64 num_secs)
- {
- u64 i;
- for (i = 0; i < num_secs; i++)
- sb_breadahead(sb, (sector_t)(secno + i));
- }
- #endif
- /* Make a readahead request */
- s32 bdev_readahead(struct super_block *sb, u64 secno, u64 num_secs)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (!fsi->bd_opened)
- return -EIO;
- __bdev_readahead(sb, secno, num_secs);
- return 0;
- }
- s32 bdev_mread(struct super_block *sb, u64 secno, struct buffer_head **bh, u64 num_secs, s32 read)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- u8 blksize_bits = sb->s_blocksize_bits;
- #ifdef CONFIG_SDFAT_DBG_IOCTL
- struct sdfat_sb_info *sbi = SDFAT_SB(sb);
- long flags = sbi->debug_flags;
- if (flags & SDFAT_DEBUGFLAGS_ERROR_RW)
- return -EIO;
- #endif /* CONFIG_SDFAT_DBG_IOCTL */
- if (!fsi->bd_opened)
- return -EIO;
- brelse(*bh);
- if (read)
- *bh = __bread(sb->s_bdev, (sector_t)secno, num_secs << blksize_bits);
- else
- *bh = __getblk(sb->s_bdev, (sector_t)secno, num_secs << blksize_bits);
- /* read successfully */
- if (*bh)
- return 0;
- /*
- * patch 1.2.4 : reset ONCE warning message per volume.
- */
- if (!(fsi->prev_eio & SDFAT_EIO_READ)) {
- fsi->prev_eio |= SDFAT_EIO_READ;
- sdfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__);
- sdfat_debug_warn_on(1);
- }
- return -EIO;
- }
- s32 bdev_mwrite(struct super_block *sb, u64 secno, struct buffer_head *bh, u64 num_secs, s32 sync)
- {
- u64 count;
- struct buffer_head *bh2;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- #ifdef CONFIG_SDFAT_DBG_IOCTL
- struct sdfat_sb_info *sbi = SDFAT_SB(sb);
- long flags = sbi->debug_flags;
- if (flags & SDFAT_DEBUGFLAGS_ERROR_RW)
- return -EIO;
- #endif /* CONFIG_SDFAT_DBG_IOCTL */
- if (!fsi->bd_opened)
- return -EIO;
- if (secno == bh->b_blocknr) {
- set_buffer_uptodate(bh);
- mark_buffer_dirty(bh);
- if (sync && (sync_dirty_buffer(bh) != 0))
- return -EIO;
- } else {
- count = num_secs << sb->s_blocksize_bits;
- bh2 = __getblk(sb->s_bdev, (sector_t)secno, count);
- if (!bh2)
- goto no_bh;
- lock_buffer(bh2);
- memcpy(bh2->b_data, bh->b_data, count);
- set_buffer_uptodate(bh2);
- mark_buffer_dirty(bh2);
- unlock_buffer(bh2);
- if (sync && (sync_dirty_buffer(bh2) != 0)) {
- __brelse(bh2);
- goto no_bh;
- }
- __brelse(bh2);
- }
- return 0;
- no_bh:
- /*
- * patch 1.2.4 : reset ONCE warning message per volume.
- */
- if (!(fsi->prev_eio & SDFAT_EIO_WRITE)) {
- fsi->prev_eio |= SDFAT_EIO_WRITE;
- sdfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__);
- sdfat_debug_warn_on(1);
- }
- return -EIO;
- }
- s32 bdev_sync_all(struct super_block *sb)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- #ifdef CONFIG_SDFAT_DBG_IOCTL
- struct sdfat_sb_info *sbi = SDFAT_SB(sb);
- long flags = sbi->debug_flags;
- if (flags & SDFAT_DEBUGFLAGS_ERROR_RW)
- return -EIO;
- #endif /* CONFIG_SDFAT_DBG_IOCTL */
- if (!fsi->bd_opened)
- return -EIO;
- return sync_blockdev(sb->s_bdev);
- }
- /*
- * Sector Read/Write Functions
- */
- s32 read_sect(struct super_block *sb, u64 sec, struct buffer_head **bh, s32 read)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- BUG_ON(!bh);
- if ((sec >= fsi->num_sectors) && (fsi->num_sectors > 0)) {
- sdfat_fs_error_ratelimit(sb,
- "%s: out of range (sect:%llu)", __func__, sec);
- return -EIO;
- }
- if (bdev_mread(sb, sec, bh, 1, read)) {
- sdfat_fs_error_ratelimit(sb,
- "%s: I/O error (sect:%llu)", __func__, sec);
- return -EIO;
- }
- return 0;
- }
- s32 write_sect(struct super_block *sb, u64 sec, struct buffer_head *bh, s32 sync)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- BUG_ON(!bh);
- if ((sec >= fsi->num_sectors) && (fsi->num_sectors > 0)) {
- sdfat_fs_error_ratelimit(sb,
- "%s: out of range (sect:%llu)", __func__, sec);
- return -EIO;
- }
- if (bdev_mwrite(sb, sec, bh, 1, sync)) {
- sdfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%llu)",
- __func__, sec);
- return -EIO;
- }
- return 0;
- }
- s32 read_msect(struct super_block *sb, u64 sec, struct buffer_head **bh, u64 num_secs, s32 read)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- BUG_ON(!bh);
- if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) {
- sdfat_fs_error_ratelimit(sb, "%s: out of range(sect:%llu len:%llu)",
- __func__, sec, num_secs);
- return -EIO;
- }
- if (bdev_mread(sb, sec, bh, num_secs, read)) {
- sdfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%llu len:%llu)",
- __func__, sec, num_secs);
- return -EIO;
- }
- return 0;
- }
- s32 write_msect(struct super_block *sb, u64 sec, struct buffer_head *bh, u64 num_secs, s32 sync)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- BUG_ON(!bh);
- if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) {
- sdfat_fs_error_ratelimit(sb, "%s: out of range(sect:%llu len:%llu)",
- __func__, sec, num_secs);
- return -EIO;
- }
- if (bdev_mwrite(sb, sec, bh, num_secs, sync)) {
- sdfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%llu len:%llu)",
- __func__, sec, num_secs);
- return -EIO;
- }
- return 0;
- }
- static inline void __blkdev_write_bhs(struct buffer_head **bhs, s32 nr_bhs)
- {
- s32 i;
- for (i = 0; i < nr_bhs; i++)
- write_dirty_buffer(bhs[i], WRITE);
- }
- static inline s32 __blkdev_sync_bhs(struct buffer_head **bhs, s32 nr_bhs)
- {
- s32 i, err = 0;
- for (i = 0; i < nr_bhs; i++) {
- wait_on_buffer(bhs[i]);
- if (!err && !buffer_uptodate(bhs[i]))
- err = -EIO;
- }
- return err;
- }
- static inline s32 __buffer_zeroed(struct super_block *sb, u64 blknr, u64 num_secs)
- {
- struct buffer_head *bhs[MAX_BUF_PER_PAGE];
- s32 nr_bhs = MAX_BUF_PER_PAGE;
- u64 last_blknr = blknr + num_secs;
- s32 err, i, n;
- struct blk_plug plug;
- /* Zeroing the unused blocks on this cluster */
- n = 0;
- blk_start_plug(&plug);
- while (blknr < last_blknr) {
- bhs[n] = sb_getblk(sb, (sector_t)blknr);
- if (!bhs[n]) {
- err = -ENOMEM;
- blk_finish_plug(&plug);
- goto error;
- }
- memset(bhs[n]->b_data, 0, sb->s_blocksize);
- set_buffer_uptodate(bhs[n]);
- mark_buffer_dirty(bhs[n]);
- n++;
- blknr++;
- if (blknr == last_blknr)
- break;
- if (n == nr_bhs) {
- __blkdev_write_bhs(bhs, n);
- for (i = 0; i < n; i++)
- brelse(bhs[i]);
- n = 0;
- }
- }
- __blkdev_write_bhs(bhs, n);
- blk_finish_plug(&plug);
- err = __blkdev_sync_bhs(bhs, n);
- if (err)
- goto error;
- for (i = 0; i < n; i++)
- brelse(bhs[i]);
- return 0;
- error:
- EMSG("%s: failed zeroed sect %llu\n", __func__, blknr);
- for (i = 0; i < n; i++)
- bforget(bhs[i]);
- return err;
- }
- s32 write_msect_zero(struct super_block *sb, u64 sec, u64 num_secs)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) {
- sdfat_fs_error_ratelimit(sb, "%s: out of range(sect:%llu len:%llu)",
- __func__, sec, num_secs);
- return -EIO;
- }
- /* Just return -EAGAIN if it is failed */
- if (__buffer_zeroed(sb, sec, num_secs))
- return -EAGAIN;
- return 0;
- } /* end of write_msect_zero */
- /* end of blkdev.c */
|