📄 xfs_buf.c
字号:
xfs_buftarg_t *target, xfs_off_t ioff, size_t isize, xfs_buf_flags_t flags){ xfs_buf_t *bp; flags |= XBF_READ; bp = xfs_buf_get_flags(target, ioff, isize, flags); if (bp) { if (!XFS_BUF_ISDONE(bp)) { XB_TRACE(bp, "read", (unsigned long)flags); XFS_STATS_INC(xb_get_read); xfs_buf_iostart(bp, flags); } else if (flags & XBF_ASYNC) { XB_TRACE(bp, "read_async", (unsigned long)flags); /* * Read ahead call which is already satisfied, * drop the buffer */ goto no_buffer; } else { XB_TRACE(bp, "read_done", (unsigned long)flags); /* We do not want read in the flags */ bp->b_flags &= ~XBF_READ; } } return bp; no_buffer: if (flags & (XBF_LOCK | XBF_TRYLOCK)) xfs_buf_unlock(bp); xfs_buf_rele(bp); return NULL;}/* * If we are not low on memory then do the readahead in a deadlock * safe manner. */voidxfs_buf_readahead( xfs_buftarg_t *target, xfs_off_t ioff, size_t isize, xfs_buf_flags_t flags){ struct backing_dev_info *bdi; bdi = target->bt_mapping->backing_dev_info; if (bdi_read_congested(bdi)) return; flags |= (XBF_TRYLOCK|XBF_ASYNC|XBF_READ_AHEAD); xfs_buf_read_flags(target, ioff, isize, flags);}xfs_buf_t *xfs_buf_get_empty( size_t len, xfs_buftarg_t *target){ xfs_buf_t *bp; bp = xfs_buf_allocate(0); if (bp) _xfs_buf_initialize(bp, target, 0, len, 0); return bp;}static inline struct page *mem_to_page( void *addr){ if (((unsigned long)addr < VMALLOC_START) || ((unsigned long)addr >= VMALLOC_END)) { return virt_to_page(addr); } else { return vmalloc_to_page(addr); }}intxfs_buf_associate_memory( xfs_buf_t *bp, void *mem, size_t len){ int rval; int i = 0; size_t ptr; size_t end, end_cur; off_t offset; int page_count; page_count = PAGE_CACHE_ALIGN(len) >> PAGE_CACHE_SHIFT; offset = (off_t) mem - ((off_t)mem & PAGE_CACHE_MASK); if (offset && (len > PAGE_CACHE_SIZE)) page_count++; /* Free any previous set of page pointers */ if (bp->b_pages) _xfs_buf_free_pages(bp); bp->b_pages = NULL; bp->b_addr = mem; rval = _xfs_buf_get_pages(bp, page_count, 0); if (rval) return rval; bp->b_offset = offset; ptr = (size_t) mem & PAGE_CACHE_MASK; end = PAGE_CACHE_ALIGN((size_t) mem + len); end_cur = end; /* set up first page */ bp->b_pages[0] = mem_to_page(mem); ptr += PAGE_CACHE_SIZE; bp->b_page_count = ++i; while (ptr < end) { bp->b_pages[i] = mem_to_page((void *)ptr); bp->b_page_count = ++i; ptr += PAGE_CACHE_SIZE; } bp->b_locked = 0; bp->b_count_desired = bp->b_buffer_length = len; bp->b_flags |= XBF_MAPPED; return 0;}xfs_buf_t *xfs_buf_get_noaddr( size_t len, xfs_buftarg_t *target){ size_t malloc_len = len; xfs_buf_t *bp; void *data; int error; bp = xfs_buf_allocate(0); if (unlikely(bp == NULL)) goto fail; _xfs_buf_initialize(bp, target, 0, len, 0); try_again: data = kmem_alloc(malloc_len, KM_SLEEP | KM_MAYFAIL); if (unlikely(data == NULL)) goto fail_free_buf; /* check whether alignment matches.. */ if ((__psunsigned_t)data != ((__psunsigned_t)data & ~target->bt_smask)) { /* .. else double the size and try again */ kmem_free(data, malloc_len); malloc_len <<= 1; goto try_again; } error = xfs_buf_associate_memory(bp, data, len); if (error) goto fail_free_mem; bp->b_flags |= _XBF_KMEM_ALLOC; xfs_buf_unlock(bp); XB_TRACE(bp, "no_daddr", data); return bp; fail_free_mem: kmem_free(data, malloc_len); fail_free_buf: xfs_buf_free(bp); fail: return NULL;}/* * Increment reference count on buffer, to hold the buffer concurrently * with another thread which may release (free) the buffer asynchronously. * Must hold the buffer already to call this function. */voidxfs_buf_hold( xfs_buf_t *bp){ atomic_inc(&bp->b_hold); XB_TRACE(bp, "hold", 0);}/* * Releases a hold on the specified buffer. If the * the hold count is 1, calls xfs_buf_free. */voidxfs_buf_rele( xfs_buf_t *bp){ xfs_bufhash_t *hash = bp->b_hash; XB_TRACE(bp, "rele", bp->b_relse); if (unlikely(!hash)) { ASSERT(!bp->b_relse); if (atomic_dec_and_test(&bp->b_hold)) xfs_buf_free(bp); return; } if (atomic_dec_and_lock(&bp->b_hold, &hash->bh_lock)) { if (bp->b_relse) { atomic_inc(&bp->b_hold); spin_unlock(&hash->bh_lock); (*(bp->b_relse)) (bp); } else if (bp->b_flags & XBF_FS_MANAGED) { spin_unlock(&hash->bh_lock); } else { ASSERT(!(bp->b_flags & (XBF_DELWRI|_XBF_DELWRI_Q))); list_del_init(&bp->b_hash_list); spin_unlock(&hash->bh_lock); xfs_buf_free(bp); } } else { /* * Catch reference count leaks */ ASSERT(atomic_read(&bp->b_hold) >= 0); }}/* * Mutual exclusion on buffers. Locking model: * * Buffers associated with inodes for which buffer locking * is not enabled are not protected by semaphores, and are * assumed to be exclusively owned by the caller. There is a * spinlock in the buffer, used by the caller when concurrent * access is possible. *//* * Locks a buffer object, if it is not already locked. * Note that this in no way locks the underlying pages, so it is only * useful for synchronizing concurrent use of buffer objects, not for * synchronizing independent access to the underlying pages. */intxfs_buf_cond_lock( xfs_buf_t *bp){ int locked; locked = down_trylock(&bp->b_sema) == 0; if (locked) { XB_SET_OWNER(bp); } XB_TRACE(bp, "cond_lock", (long)locked); return locked ? 0 : -EBUSY;}#if defined(DEBUG) || defined(XFS_BLI_TRACE)intxfs_buf_lock_value( xfs_buf_t *bp){ return atomic_read(&bp->b_sema.count);}#endif/* * Locks a buffer object. * Note that this in no way locks the underlying pages, so it is only * useful for synchronizing concurrent use of buffer objects, not for * synchronizing independent access to the underlying pages. */voidxfs_buf_lock( xfs_buf_t *bp){ XB_TRACE(bp, "lock", 0); if (atomic_read(&bp->b_io_remaining)) blk_run_address_space(bp->b_target->bt_mapping); down(&bp->b_sema); XB_SET_OWNER(bp); XB_TRACE(bp, "locked", 0);}/* * Releases the lock on the buffer object. * If the buffer is marked delwri but is not queued, do so before we * unlock the buffer as we need to set flags correctly. We also need to * take a reference for the delwri queue because the unlocker is going to * drop their's and they don't know we just queued it. */voidxfs_buf_unlock( xfs_buf_t *bp){ if ((bp->b_flags & (XBF_DELWRI|_XBF_DELWRI_Q)) == XBF_DELWRI) { atomic_inc(&bp->b_hold); bp->b_flags |= XBF_ASYNC; xfs_buf_delwri_queue(bp, 0); } XB_CLEAR_OWNER(bp); up(&bp->b_sema); XB_TRACE(bp, "unlock", 0);}/* * Pinning Buffer Storage in Memory * Ensure that no attempt to force a buffer to disk will succeed. */voidxfs_buf_pin( xfs_buf_t *bp){ atomic_inc(&bp->b_pin_count); XB_TRACE(bp, "pin", (long)bp->b_pin_count.counter);}voidxfs_buf_unpin( xfs_buf_t *bp){ if (atomic_dec_and_test(&bp->b_pin_count)) wake_up_all(&bp->b_waiters); XB_TRACE(bp, "unpin", (long)bp->b_pin_count.counter);}intxfs_buf_ispin( xfs_buf_t *bp){ return atomic_read(&bp->b_pin_count);}STATIC voidxfs_buf_wait_unpin( xfs_buf_t *bp){ DECLARE_WAITQUEUE (wait, current); if (atomic_read(&bp->b_pin_count) == 0) return; add_wait_queue(&bp->b_waiters, &wait); for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); if (atomic_read(&bp->b_pin_count) == 0) break; if (atomic_read(&bp->b_io_remaining)) blk_run_address_space(bp->b_target->bt_mapping); schedule(); } remove_wait_queue(&bp->b_waiters, &wait); set_current_state(TASK_RUNNING);}/* * Buffer Utility Routines */STATIC voidxfs_buf_iodone_work( void *v){ xfs_buf_t *bp = (xfs_buf_t *)v; if (bp->b_iodone) (*(bp->b_iodone))(bp); else if (bp->b_flags & XBF_ASYNC) xfs_buf_relse(bp);}voidxfs_buf_ioend( xfs_buf_t *bp, int schedule){ bp->b_flags &= ~(XBF_READ | XBF_WRITE); if (bp->b_error == 0) bp->b_flags |= XBF_DONE; XB_TRACE(bp, "iodone", bp->b_iodone); if ((bp->b_iodone) || (bp->b_flags & XBF_ASYNC)) { if (schedule) { INIT_WORK(&bp->b_iodone_work, xfs_buf_iodone_work, bp); queue_work(xfslogd_workqueue, &bp->b_iodone_work); } else { xfs_buf_iodone_work(bp); } } else { up(&bp->b_iodonesema); }}voidxfs_buf_ioerror( xfs_buf_t *bp, int error){ ASSERT(error >= 0 && error <= 0xffff); bp->b_error = (unsigned short)error; XB_TRACE(bp, "ioerror", (unsigned long)error);}/* * Initiate I/O on a buffer, based on the flags supplied. * The b_iodone routine in the buffer supplied will only be called * when all of the subsidiary I/O requests, if any, have been completed. */intxfs_buf_iostart( xfs_buf_t *bp, xfs_buf_flags_t flags){ int status = 0; XB_TRACE(bp, "iostart", (unsigned long)flags); if (flags & XBF_DELWRI) { bp->b_flags &= ~(XBF_READ | XBF_WRITE | XBF_ASYNC); bp->b_flags |= flags & (XBF_DELWRI | XBF_ASYNC); xfs_buf_delwri_queue(bp, 1); return status; } bp->b_flags &= ~(XBF_READ | XBF_WRITE | XBF_ASYNC | XBF_DELWRI | \ XBF_READ_AHEAD | _XBF_RUN_QUEUES); bp->b_flags |= flags & (XBF_READ | XBF_WRITE | XBF_ASYNC | \ XBF_READ_AHEAD | _XBF_RUN_QUEUES); BUG_ON(bp->b_bn == XFS_BUF_DADDR_NULL); /* For writes allow an alternate strategy routine to precede * the actual I/O request (which may not be issued at all in * a shutdown situation, for example). */ status = (flags & XBF_WRITE) ? xfs_buf_iostrategy(bp) : xfs_buf_iorequest(bp); /* Wait for I/O if we are not an async request. * Note: async I/O request completion will release the buffer, * and that can already be done by this point. So using the * buffer pointer from here on, after async I/O, is invalid. */ if (!status && !(flags & XBF_ASYNC)) status = xfs_buf_iowait(bp); return status;}STATIC __inline__ int_xfs_buf_iolocked( xfs_buf_t *bp){ ASSERT(bp->b_flags & (XBF_READ | XBF_WRITE)); if (bp->b_flags & XBF_READ) return bp->b_locked; return 0;}STATIC __inline__ void_xfs_buf_ioend( xfs_buf_t *bp, int schedule){ if (atomic_dec_and_test(&bp->b_io_remaining) == 1) { bp->b_locked = 0; xfs_buf_ioend(bp, schedule); }}STATIC intxfs_buf_bio_end_io( struct bio *bio, unsigned int bytes_done, int error){ xfs_buf_t *bp = (xfs_buf_t *)bio->bi_private; unsigned int blocksize = bp->b_target->bt_bsize; struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; if (bio->bi_size) return 1; if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) bp->b_error = EIO; do { struct page *page = bvec->bv_page; if (unlikely(bp->b_error)) { if (bp->b_flags & XBF_READ) ClearPageUptodate(page); SetPageError(page); } else if (blocksize >= PAGE_CACHE_SIZE) { SetPageUptodate(page); } else if (!PagePrivate(page) && (bp->b_flags & _XBF_PAGE_CACHE)) { set_page_region(page, bvec->bv_offset, bvec->bv_len); } if (--bvec >= bio->bi_io_vec) prefetchw(&bvec->bv_page->flags); if (_xfs_buf_iolocked(bp)) { unlock_page(page); } } while (bvec >= bio->bi_io_vec); _xfs_buf_ioend(bp, 1); bio_put(bio); return 0;}STATIC void_xfs_buf_ioapply( xfs_buf_t *bp){ int i, rw, map_i, total_nr_pages, nr_pages; struct bio *bio; int offset = bp->b_offset; int size = bp->b_count_desired; sector_t sector = bp->b_bn; unsigned int blocksize = bp->b_target->bt_bsize; int locking = _xfs_buf_iolocked(bp); total_nr_pages = bp->b_page_count; map_i = 0; if (bp->b_flags & _XBF_RUN_QUEUES) { bp->b_flags &= ~_XBF_RUN_QUEUES; rw = (bp->b_flags & XBF_READ) ? READ_SYNC : WRITE_SYNC; } else { rw = (bp->b_flags & XBF_READ) ? READ : WRITE; } if (bp->b_flags & XBF_ORDERED) { ASSERT(!(bp->b_flags & XBF_READ)); rw = WRITE_BARRIER; } /* Special code path for reading a sub page size buffer in -- * we populate up the whole page, and hence the other metadata * in the same page. This optimization is only valid when the * filesystem block size is not smaller than the page size. */ if ((bp->b_buffer_length < PAGE_CACHE_SIZE) && (bp->b_flags & XBF_READ) && locking && (blocksize >= PAGE_CACHE_SIZE)) { bio = bio_alloc(GFP_NOIO, 1); bio->bi_bdev = bp->b_target->bt_bdev; bio->bi_sector = sector - (offset >> BBSHIFT); bio->bi_end_io = xfs_buf_bio_end_io; bio->bi_private = bp; bio_add_page(bio, bp->b_pages[0], PAGE_CACHE_SIZE, 0); size = 0; atomic_inc(&bp->b_io_remaining); goto submit_io; } /* Lock down the pages which we need to for the request */ if (locking && (bp->b_flags & XBF_WRITE) && (bp->b_locked == 0)) { for (i = 0; size; i++) { int nbytes = PAGE_CACHE_SIZE - offset; struct page *page = bp->b_pages[i]; if (nbytes > size) nbytes = size; lock_page(page); size -= nbytes; offset = 0; } offset = bp->b_offset; size = bp->b_count_desired; }next_chunk: atomic_inc(&bp->b_io_remaining); nr_pages = BIO_MAX_SECTORS >> (PAGE_SHIFT - BBSHIFT); if (nr_pages > total_nr_pages) nr_pages = total_nr_pages; bio = bio_alloc(GFP_NOIO, nr_pages); bio->bi_bdev = bp->b_target->bt_bdev; bio->bi_sector = sector; bio->bi_end_io = xfs_buf_bio_end_io; bio->bi_private = bp; for (; size && nr_pages; nr_pages--, map_i++) { int rbytes, nbytes = PAGE_CACHE_SIZE - offset; if (nbytes > size) nbytes = size; rbytes = bio_add_page(bio, bp->b_pages[map_i], nbytes, offset); if (rbytes < nbytes) break; offset = 0; sector += nbytes >> BBSHIFT; size -= nbytes; total_nr_pages--; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -