📄 xfs_buf.c
字号:
if (! (free = pb_resv_bh_cnt >= NR_RESERVED_BH)) { spin_lock_irqsave(&pb_resv_bh_lock, flags); if (! (free = pb_resv_bh_cnt >= NR_RESERVED_BH)) { bh->b_pprev = &pb_resv_bh; bh->b_next = pb_resv_bh; pb_resv_bh = bh; pb_resv_bh_cnt++; if (waitqueue_active(&pb_resv_bh_wait)) { wake_up(&pb_resv_bh_wait); } } spin_unlock_irqrestore(&pb_resv_bh_lock, flags); } if (free) { kmem_cache_free(bh_cachep, bh); }}/* * Finding and Reading Buffers *//* * _pagebuf_find * * Looks up, and creates if absent, a lockable buffer for * a given range of an inode. The buffer is returned * locked. If other overlapping buffers exist, they are * released before the new buffer is created and locked, * which may imply that this call will block until those buffers * are unlocked. No I/O is implied by this call. */xfs_buf_t *_pagebuf_find( /* find buffer for block */ xfs_buftarg_t *target,/* target for block */ loff_t ioff, /* starting offset of range */ size_t isize, /* length of range */ page_buf_flags_t flags, /* PBF_TRYLOCK */ xfs_buf_t *new_pb)/* newly allocated buffer */{ loff_t range_base; size_t range_length; int hval; pb_hash_t *h; xfs_buf_t *pb, *n; int not_locked; range_base = (ioff << BBSHIFT); range_length = (isize << BBSHIFT); /* Ensure we never do IOs smaller than the sector size */ BUG_ON(range_length < (1 << target->pbr_sshift)); /* Ensure we never do IOs that are not sector aligned */ BUG_ON(range_base & (loff_t)target->pbr_smask); hval = _bhash(target->pbr_bdev, range_base); h = &pbhash[hval]; spin_lock(&h->pb_hash_lock); list_for_each_entry_safe(pb, n, &h->pb_hash, pb_hash_list) { if (pb->pb_target == target && pb->pb_file_offset == range_base && pb->pb_buffer_length == range_length) { /* If we look at something bring it to the * front of the list for next time */ atomic_inc(&pb->pb_hold); list_move(&pb->pb_hash_list, &h->pb_hash); goto found; } } /* No match found */ if (new_pb) { _pagebuf_initialize(new_pb, target, range_base, range_length, flags); new_pb->pb_hash_index = hval; list_add(&new_pb->pb_hash_list, &h->pb_hash); } else { XFS_STATS_INC(pb_miss_locked); } spin_unlock(&h->pb_hash_lock); return (new_pb);found: spin_unlock(&h->pb_hash_lock); /* Attempt to get the semaphore without sleeping, * if this does not work then we need to drop the * spinlock and do a hard attempt on the semaphore. */ not_locked = down_trylock(&pb->pb_sema); if (not_locked) { if (!(flags & PBF_TRYLOCK)) { /* wait for buffer ownership */ PB_TRACE(pb, "get_lock", 0); pagebuf_lock(pb); XFS_STATS_INC(pb_get_locked_waited); } else { /* We asked for a trylock and failed, no need * to look at file offset and length here, we * know that this pagebuf at least overlaps our * pagebuf and is locked, therefore our buffer * either does not exist, or is this buffer */ pagebuf_rele(pb); XFS_STATS_INC(pb_busy_locked); return (NULL); } } else { /* trylock worked */ PB_SET_OWNER(pb); } if (pb->pb_flags & PBF_STALE) pb->pb_flags &= PBF_MAPPED; PB_TRACE(pb, "got_lock", 0); XFS_STATS_INC(pb_get_locked); return (pb);}/* * xfs_buf_get_flags assembles a buffer covering the specified range. * * Storage in memory for all portions of the buffer will be allocated, * although backing storage may not be. */xfs_buf_t *xfs_buf_get_flags( /* allocate a buffer */ xfs_buftarg_t *target,/* target for buffer */ loff_t ioff, /* starting offset of range */ size_t isize, /* length of range */ page_buf_flags_t flags) /* PBF_TRYLOCK */{ xfs_buf_t *pb, *new_pb; int error = 0, i; new_pb = pagebuf_allocate(flags); if (unlikely(!new_pb)) return NULL; pb = _pagebuf_find(target, ioff, isize, flags, new_pb); if (pb == new_pb) { error = _pagebuf_lookup_pages(pb, flags); if (error) goto no_buffer; } else { pagebuf_deallocate(new_pb); if (unlikely(pb == NULL)) return NULL; } for (i = 0; i < pb->pb_page_count; i++) mark_page_accessed(pb->pb_pages[i]); if (!(pb->pb_flags & PBF_MAPPED)) { error = _pagebuf_map_pages(pb, flags); if (unlikely(error)) { printk(KERN_WARNING "%s: failed to map pages\n", __FUNCTION__); goto no_buffer; } } XFS_STATS_INC(pb_get); /* * Always fill in the block number now, the mapped cases can do * their own overlay of this later. */ pb->pb_bn = ioff; pb->pb_count_desired = pb->pb_buffer_length; PB_TRACE(pb, "get", (unsigned long)flags); return pb; no_buffer: if (flags & (PBF_LOCK | PBF_TRYLOCK)) pagebuf_unlock(pb); pagebuf_rele(pb); return NULL;}xfs_buf_t *xfs_buf_read_flags( xfs_buftarg_t *target, loff_t ioff, size_t isize, page_buf_flags_t flags){ xfs_buf_t *pb; flags |= PBF_READ; pb = xfs_buf_get_flags(target, ioff, isize, flags); if (pb) { if (PBF_NOT_DONE(pb)) { PB_TRACE(pb, "read", (unsigned long)flags); XFS_STATS_INC(pb_get_read); pagebuf_iostart(pb, flags); } else if (flags & PBF_ASYNC) { PB_TRACE(pb, "read_async", (unsigned long)flags); /* * Read ahead call which is already satisfied, * drop the buffer */ goto no_buffer; } else { PB_TRACE(pb, "read_done", (unsigned long)flags); /* We do not want read in the flags */ pb->pb_flags &= ~PBF_READ; } } return pb; no_buffer: if (flags & (PBF_LOCK | PBF_TRYLOCK)) pagebuf_unlock(pb); pagebuf_rele(pb); return NULL;}/* * Create a skeletal pagebuf (no pages associated with it). */xfs_buf_t *pagebuf_lookup( xfs_buftarg_t *target, loff_t ioff, size_t isize, page_buf_flags_t flags){ xfs_buf_t *pb; flags |= _PBF_PRIVATE_BH; pb = pagebuf_allocate(flags); if (pb) { _pagebuf_initialize(pb, target, ioff, isize, flags); } return pb;}/* * If we are not low on memory then do the readahead in a deadlock * safe manner. */voidpagebuf_readahead( xfs_buftarg_t *target, loff_t ioff, size_t isize, page_buf_flags_t flags){ flags |= (PBF_TRYLOCK|PBF_ASYNC|PBF_READ_AHEAD); xfs_buf_read_flags(target, ioff, isize, flags);}xfs_buf_t *pagebuf_get_empty( size_t len, xfs_buftarg_t *target){ xfs_buf_t *pb; pb = pagebuf_allocate(0); if (pb) _pagebuf_initialize(pb, target, 0, len, 0); return pb;}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); }}intpagebuf_associate_memory( xfs_buf_t *pb, 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 (pb->pb_pages) _pagebuf_free_pages(pb); pb->pb_pages = NULL; pb->pb_addr = mem; rval = _pagebuf_get_pages(pb, page_count, 0); if (rval) return rval; pb->pb_offset = offset; ptr = (size_t) mem & PAGE_CACHE_MASK; end = PAGE_CACHE_ALIGN((size_t) mem + len); end_cur = end; /* set up first page */ pb->pb_pages[0] = mem_to_page(mem); ptr += PAGE_CACHE_SIZE; pb->pb_page_count = ++i; while (ptr < end) { pb->pb_pages[i] = mem_to_page((void *)ptr); pb->pb_page_count = ++i; ptr += PAGE_CACHE_SIZE; } pb->pb_locked = 0; pb->pb_count_desired = pb->pb_buffer_length = len; pb->pb_flags |= PBF_MAPPED | _PBF_PRIVATE_BH; return 0;}xfs_buf_t *pagebuf_get_no_daddr( size_t len, xfs_buftarg_t *target){ size_t malloc_len = len; xfs_buf_t *bp; void *data; int error; bp = pagebuf_allocate(0); if (unlikely(bp == NULL)) goto fail; _pagebuf_initialize(bp, target, 0, len, PBF_FORCEIO); 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->pbr_smask)) { /* .. else double the size and try again */ kmem_free(data, malloc_len); malloc_len <<= 1; goto try_again; } error = pagebuf_associate_memory(bp, data, len); if (error) goto fail_free_mem; bp->pb_flags |= _PBF_KMEM_ALLOC; pagebuf_unlock(bp); PB_TRACE(bp, "no_daddr", data); return bp; fail_free_mem: kmem_free(data, malloc_len); fail_free_buf: pagebuf_free(bp); fail: return NULL;}/* * pagebuf_hold * * 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. */voidpagebuf_hold( xfs_buf_t *pb){ atomic_inc(&pb->pb_hold); PB_TRACE(pb, "hold", 0);}/* * pagebuf_rele * * pagebuf_rele releases a hold on the specified buffer. If the * the hold count is 1, pagebuf_rele calls pagebuf_free. */voidpagebuf_rele( xfs_buf_t *pb){ pb_hash_t *hash = pb_hash(pb); PB_TRACE(pb, "rele", pb->pb_relse); if (atomic_dec_and_lock(&pb->pb_hold, &hash->pb_hash_lock)) { int do_free = 1; if (pb->pb_relse) { atomic_inc(&pb->pb_hold); spin_unlock(&hash->pb_hash_lock); (*(pb->pb_relse)) (pb); spin_lock(&hash->pb_hash_lock); do_free = 0; } if (pb->pb_flags & PBF_DELWRI) { pb->pb_flags |= PBF_ASYNC; atomic_inc(&pb->pb_hold); pagebuf_delwri_queue(pb, 0); do_free = 0; } else if (pb->pb_flags & PBF_FS_MANAGED) { do_free = 0; } if (do_free) { list_del_init(&pb->pb_hash_list); spin_unlock(&hash->pb_hash_lock); xfs_buf_free(pb); } else { spin_unlock(&hash->pb_hash_lock); } }}/* * 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. *//* * pagebuf_cond_lock * * pagebuf_cond_lock 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 page buffer objects, not for synchronizing independent * access to the underlying pages. */intpagebuf_cond_lock( /* lock buffer, if not locked */ /* returns -EBUSY if locked) */ xfs_buf_t *pb){ int locked; locked = down_trylock(&pb->pb_sema) == 0; if (locked) { PB_SET_OWNER(pb); } PB_TRACE(pb, "cond_lock", (long)locked); return(locked ? 0 : -EBUSY);}#ifdef DEBUG/* * pagebuf_lock_value * * Return lock value for a pagebuf */intpagebuf_lock_value( xfs_buf_t *pb){ return(atomic_read(&pb->pb_sema.count));}#endif/* * pagebuf_lock * * pagebuf_lock locks a buffer object. Note that this in no way * locks the underlying pages, so it is only useful for synchronizing * concurrent use of page buffer objects, not for synchronizing independent * access to the underlying pages. */intpagebuf_lock( xfs_buf_t *pb){ PB_TRACE(pb, "lock", 0); if (atomic_read(&pb->pb_io_remaining)) run_task_queue(&tq_disk); down(&pb->pb_sema); PB_SET_OWNER(pb); PB_TRACE(pb, "locked", 0); return 0;}/* * pagebuf_unlock * * pagebuf_unlock releases the lock on the buffer object created by * pagebuf_lock or pagebuf_cond_lock (not any * pinning of underlying pages created by pagebuf_pin). */voidpagebuf_unlock( /* unlock buffer */ xfs_buf_t *pb) /* buffer to unlock */{ PB_CLEAR_OWNER(pb); up(&pb->pb_sema); PB_TRACE(pb, "unlock", 0);}/* * Pinning Buffer Storage in Memory *//* * pagebuf_pin * * pagebuf_pin locks all of the memory represented by a buffer in * memory. Multiple calls to pagebuf_pin and pagebuf_unpin, for * the same or different buffers affecting a given page, will * properly count the number of outstanding "pin" requests. The * buffer may be released after the pagebuf_pin and a different * buffer used when calling pagebuf_unpin, if desired. * pagebuf_pin should be used by the file system when it wants be * assured that no attempt will be made to force the affected * memory to disk. It does not assure that a given logical page * will not be moved to a different physical page. */voidpagebuf_pin( xfs_buf_t *pb){ atomic_inc(&pb->pb_pin_count); PB_TRACE(pb, "pin", (long)pb->pb_pin_count.counter);}/* * pagebuf_unpin * * pagebuf_unpin reverses the locking of memory performed by * pagebuf_pin. Note that both functions affected the logical * pages associated with the buffer, not the buffer itself. */voidpagebuf_unpin( xfs_buf_t *pb){ if (atomic_dec_and_test(&pb->pb_pin_count)) { wake_up_all(&pb->pb_waiters); } PB_TRACE(pb, "unpin", (long)pb->pb_pin_count.counter);}intpagebuf_ispin( xfs_buf_t *pb){ return atomic_read(&pb->pb_pin_count);}/* * pagebuf_wait_unpin * * pagebuf_wait_unpin waits until all of the memory associated * with the buffer is not longer locked in memory. It returns * immediately if none of the affected pages are locked. */static inline void_pagebuf_wait_unpin(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -