📄 buffer.c
字号:
unsigned long page; struct buffer_head *bh, *tmp; struct buffer_head * insert_point; int isize; if ((size & 511) || (size > PAGE_SIZE)) { printk("VFS: grow_buffers: size = %d\n",size); return 0; } isize = BUFSIZE_INDEX(size); if (!(page = __get_free_page(pri))) return 0; bh = create_buffers(page, size); if (!bh) { free_page(page); return 0; } insert_point = free_list[isize]; tmp = bh; while (1) { if (insert_point) { tmp->b_next_free = insert_point->b_next_free; tmp->b_prev_free = insert_point; insert_point->b_next_free->b_prev_free = tmp; insert_point->b_next_free = tmp; } else { tmp->b_prev_free = tmp; tmp->b_next_free = tmp; } insert_point = tmp; ++nr_buffers; if (tmp->b_this_page) tmp = tmp->b_this_page; else break; } tmp->b_this_page = bh; free_list[isize] = bh; mem_map[MAP_NR(page)].buffers = bh; buffermem += PAGE_SIZE; return 1;}/* =========== Reduce the buffer memory ============= */static inline int buffer_waiting(struct buffer_head * bh){ return waitqueue_active(&bh->b_wait);}/* * try_to_free_buffer() checks if all the buffers on this particular page * are unused, and free's the page if so. */int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp, int priority){ unsigned long page; struct buffer_head * tmp, * p; *bhp = bh; page = (unsigned long) bh->b_data; page &= PAGE_MASK; tmp = bh; do { if (!tmp) return 0; if (tmp->b_count || buffer_protected(tmp) || buffer_dirty(tmp) || buffer_locked(tmp) || buffer_waiting(tmp)) return 0; if (priority && buffer_touched(tmp)) return 0; tmp = tmp->b_this_page; } while (tmp != bh); tmp = bh; do { p = tmp; tmp = tmp->b_this_page; nr_buffers--; if (p == *bhp) { *bhp = p->b_prev_free; if (p == *bhp) /* Was this the last in the list? */ *bhp = NULL; } remove_from_queues(p); put_unused_buffer_head(p); } while (tmp != bh); buffermem -= PAGE_SIZE; mem_map[MAP_NR(page)].buffers = NULL; free_page(page); return !mem_map[MAP_NR(page)].count;}/* ================== Debugging =================== */void show_buffers(void){ struct buffer_head * bh; int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0; int protected = 0; int nlist; static char *buf_types[NR_LIST] = {"CLEAN","LOCKED","LOCKED1","DIRTY"}; printk("Buffer memory: %6dkB\n",buffermem>>10); printk("Buffer heads: %6d\n",nr_buffer_heads); printk("Buffer blocks: %6d\n",nr_buffers); for(nlist = 0; nlist < NR_LIST; nlist++) { found = locked = dirty = used = lastused = protected = 0; bh = lru_list[nlist]; if(!bh) continue; do { found++; if (buffer_locked(bh)) locked++; if (buffer_protected(bh)) protected++; if (buffer_dirty(bh)) dirty++; if (bh->b_count) used++, lastused = found; bh = bh->b_next_free; } while (bh != lru_list[nlist]); printk("%8s: %d buffers, %d used (last=%d), " "%d locked, %d protected, %d dirty\n", buf_types[nlist], found, used, lastused, locked, protected, dirty); };}/* ===================== Init ======================= *//* * allocate the hash table and init the free list */void buffer_init(void){ hash_table = (struct buffer_head **)vmalloc(NR_HASH*sizeof(struct buffer_head *)); if (!hash_table) panic("Failed to allocate buffer hash table\n"); memset(hash_table,0,NR_HASH*sizeof(struct buffer_head *)); lru_list[BUF_CLEAN] = 0; grow_buffers(GFP_KERNEL, BLOCK_SIZE);}/* ====================== bdflush support =================== *//* This is a simple kernel daemon, whose job it is to provide a dynamic * response to dirty buffers. Once this process is activated, we write back * a limited number of buffers to the disks and then go back to sleep again. */struct wait_queue * bdflush_wait = NULL;struct wait_queue * bdflush_done = NULL;struct task_struct *bdflush_tsk = 0;static void wakeup_bdflush(int wait){ if (current == bdflush_tsk) return; wake_up(&bdflush_wait); if (wait) { run_task_queue(&tq_disk); sleep_on(&bdflush_done); recover_reusable_buffer_heads(); }}/* * Here we attempt to write back old buffers. We also try to flush inodes * and supers as well, since this function is essentially "update", and * otherwise there would be no way of ensuring that these quantities ever * get written back. Ideally, we would have a timestamp on the inodes * and superblocks so that we could write back only the old ones as well */asmlinkage int sync_old_buffers(void){ int i; int ndirty, nwritten; int nlist; int ncount; struct buffer_head * bh, *next; sync_supers(0); sync_inodes(0); ncount = 0;#ifdef DEBUG for(nlist = 0; nlist < NR_LIST; nlist++)#else for(nlist = BUF_DIRTY; nlist <= BUF_DIRTY; nlist++)#endif { ndirty = 0; nwritten = 0; repeat: allow_interrupts(); bh = lru_list[nlist]; if(bh) for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) { /* We may have stalled while waiting for I/O to complete. */ if(bh->b_list != nlist) goto repeat; next = bh->b_next_free; if(!lru_list[nlist]) { printk("Dirty list empty %d\n", i); break; } /* Clean buffer on dirty list? Refile it */ if (nlist == BUF_DIRTY && !buffer_dirty(bh) && !buffer_locked(bh)) { refile_buffer(bh); continue; } if (buffer_locked(bh) || !buffer_dirty(bh)) continue; ndirty++; if(bh->b_flushtime > jiffies) continue; nwritten++; next->b_count++; bh->b_count++; bh->b_flushtime = 0;#ifdef DEBUG if(nlist != BUF_DIRTY) ncount++;#endif ll_rw_block(WRITE, 1, &bh); bh->b_count--; next->b_count--; } } run_task_queue(&tq_disk);#ifdef DEBUG if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount); printk("Wrote %d/%d buffers\n", nwritten, ndirty);#endif return 0;}/* This is the interface to bdflush. As we get more sophisticated, we can * pass tuning parameters to this "process", to adjust how it behaves. * We would want to verify each parameter, however, to make sure that it * is reasonable. */asmlinkage int sys_bdflush(int func, long data){ if (!suser()) return -EPERM; if (func == 1) return sync_old_buffers(); /* Basically func 1 means read param 1, 2 means write param 1, etc */ if (func >= 2) { int i = (func-2) >> 1; if (i < 0 || i >= N_PARAM) return -EINVAL; if((func & 1) == 0) { int error = verify_area(VERIFY_WRITE, (int*)data, 4); if (!error) put_user(bdf_prm.data[i], (int*)data); return error; } if (data < bdflush_min[i] || data > bdflush_max[i]) return -EINVAL; bdf_prm.data[i] = data; } /* Having func 0 used to launch the actual bdflush and then never * return (unless explicitly killed). We return zero here to * remain semi-compatible with present update(8) programs. */ return 0;}/* This is the actual bdflush daemon itself. It used to be started from * the syscall above, but now we launch it ourselves internally with * kernel_thread(...) directly after the first thread in init/main.c *//* To prevent deadlocks for a loop device: * 1) Do non-blocking writes to loop (avoids deadlock with running * out of request blocks). * 2) But do a blocking write if the only dirty buffers are loop buffers * (otherwise we go into an infinite busy-loop). * 3) Quit writing loop blocks if a freelist went low (avoids deadlock * with running out of free buffers for loop's "real" device).*/int bdflush(void * unused) { int i; int ndirty; int nlist; int ncount; struct buffer_head * bh, *next; int major; int wrta_cmd = WRITEA; /* non-blocking write for LOOP */ /* * We have a bare-bones task_struct, and really should fill * in a few more things so "top" and /proc/2/{exe,root,cwd} * display semi-sane things. Not real crucial though... */ current->session = 1; current->pgrp = 1; sprintf(current->comm, "kflushd"); bdflush_tsk = current; /* * As a kernel thread we want to tamper with system buffers * and other internals and thus be subject to the SMP locking * rules. (On a uniprocessor box this does nothing). */#ifdef __SMP__ lock_kernel(); syscall_count++;#endif for (;;) {#ifdef DEBUG printk("bdflush() activated...");#endif ncount = 0;#ifdef DEBUG for(nlist = 0; nlist < NR_LIST; nlist++)#else for(nlist = BUF_DIRTY; nlist <= BUF_DIRTY; nlist++)#endif { ndirty = 0; refilled = 0; repeat: allow_interrupts(); bh = lru_list[nlist]; if(bh) for (i = nr_buffers_type[nlist]; i-- > 0 && ndirty < bdf_prm.b_un.ndirty; bh = next) { /* We may have stalled while waiting for I/O to complete. */ if(bh->b_list != nlist) goto repeat; next = bh->b_next_free; if(!lru_list[nlist]) { printk("Dirty list empty %d\n", i); break; } /* Clean buffer on dirty list? Refile it */ if (nlist == BUF_DIRTY && !buffer_dirty(bh) && !buffer_locked(bh)) { refile_buffer(bh); continue; } if (buffer_locked(bh) || !buffer_dirty(bh)) continue; major = MAJOR(bh->b_dev); /* Should we write back buffers that are shared or not?? currently dirty buffers are not shared, so it does not matter */ if (refilled && major == LOOP_MAJOR) continue; next->b_count++; bh->b_count++; ndirty++; bh->b_flushtime = 0; if (major == LOOP_MAJOR) { ll_rw_block(wrta_cmd,1, &bh); wrta_cmd = WRITEA; if (buffer_dirty(bh)) --ndirty; } else ll_rw_block(WRITE, 1, &bh);#ifdef DEBUG if(nlist != BUF_DIRTY) ncount++;#endif bh->b_count--; next->b_count--; } }#ifdef DEBUG if (ncount) printk("sys_bdflush: %d dirty buffers not on dirty list\n", ncount); printk("sleeping again.\n");#endif /* If we didn't write anything, but there are still * dirty buffers, then make the next write to a * loop device to be a blocking write. * This lets us block--which we _must_ do! */ if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) { wrta_cmd = WRITE; continue; } run_task_queue(&tq_disk); /* If there are still a lot of dirty buffers around, skip the sleep and flush some more */ if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) { wake_up(&bdflush_done); current->signal = 0; interruptible_sleep_on(&bdflush_wait); } }}#ifdef MAGIC_ROM_PTRint bromptr(kdev_t dev, struct vm_area_struct * vma){ struct inode inode_fake; extern struct file_operations * get_blkfops(unsigned int); if (get_blkfops(MAJOR(dev))->romptr!=NULL) { inode_fake.i_rdev=dev; return get_blkfops(MAJOR(dev))->romptr(&inode_fake, NULL, vma); } return -ENOSYS;}#endif/* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-indent-level: 8 * c-brace-imaginary-offset: 0 * c-brace-offset: -8 * c-argdecl-indent: 8 * c-label-offset: -8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -