⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 state.c

📁 linux 系统控制硬盘旋转和停止的程序(spin down/up)
💻 C
字号:
/* * <state.c> * Central noflushd state machine, and random accumulated crap. *  * Copyright (C) 2000, 2001 Daniel Kobras <kobras@linux.de> *  * except the sync functions which are *  * Copyright (C) 2000 Pavel Machek <pavel@suse.cz> and Daniel Kobras *  * Pavel Machek's work was sponsored by SuSE. *  * $Id: state.c,v 1.27 2004/04/08 20:13:44 nold Exp $ * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */             #ifdef HAVE_CONFIG_H#include <config.h>#endif#include "state.h"#include "bug.h"#include "diskhelper.h"#include "disk_stat.h"#include "disk_info.h"#include "intr_stat.h"#include "part_info.h"#include "timeout.h"#include "kupdate.h"#include "noflushd.h"#include <stdlib.h>#include <fcntl.h>#include <time.h>#include <string.h>#include <mntent.h>#include <sys/stat.h>#include <sys/mount.h>static part_info_t part;static char *dev_prefix;static int plen;/* Fixup name from /proc/partitions (i.e. from part_info_get_name()) * to yield a valid absolute file name. The returned string may be * used until the next call to devname_get() only. */char *devname_get(char *name){	char *suffix;	if (!dev_prefix)		BUG("Device name requested without prefix.");		dev_prefix=realloc(dev_prefix, strlen(name)+plen+1);	suffix = dev_prefix+plen;		strcpy(suffix, name);	return dev_prefix;}	/* Check for oldstyle or devfs entries in /proc/partitions and set * dev_prefix accordingly, so that dev_prefix + pname yields a valid * fs name to the device node. */static void set_path_fixup(void){	char *dev;	FILE *mnt;	struct mntent *mntent;	struct stat st;		/* Try to determine devfs mountpoint. */	mnt = fopen("/proc/mounts", "r");	while ((mntent = getmntent(mnt)))		if (!strcmp(mntent->mnt_type, "devfs"))			break;	fclose(mnt);	if (mntent) {		dev_prefix = malloc(strlen(mntent->mnt_dir + 2));		strcpy(dev_prefix, mntent->mnt_dir);		strcat(dev_prefix, "/");		plen = strlen(dev_prefix);		DEBUG("Detected devfs at %s", dev_prefix);		return;	}		if (part_info_is_devfs()) {		ERR("Your kernel is configured with devfs, but devfs is not\n"		    "mounted anywhere. This means noflushd cannot work.\n"		    "Please consult the noflushd README for details");		exit(1);	}		/* Looks like oldstyle. Try default name. */	dev_prefix = strdup("/dev/");	plen = strlen(dev_prefix);	part_info_reset(part);	do {		if (!part_info_disk_next(part)) {			ERR("No valid disks found");			exit(1);		}	} while (IS_META(part_info_get_major(part),	                 part_info_get_minor(part)));			dev = devname_get(part_info_get_name(part));		/* Check: If we can access a block dev via the constructed name,	 * our guess was probably correct.	 */	DEBUG("Probing for valid blkdev at %s", dev);	if (!stat(dev, &st) && S_ISBLK(st.st_mode)) {		DEBUG("Detected oldstyle dev at %s", dev_prefix);		return;	}		BUG("Unable to determine device dir at %s", dev);}/* Initialize the static globals for devname handling. Ought to be done * before daemonizing to get error reporting on the console rather than * syslog. These functions are probably misplaced in this file, but then * all other places I thought of were worse. */void devname_init(void){	part = part_info_init();	if (!part) {		ERR("Error reading /proc/partitions");		exit(1);	}		set_path_fixup();}static int sync_part(char *name){	int fd, ret=0;	DEBUG("Syncing %s", name);	fd = open(name, O_WRONLY);	if (fd==-1)		return 0;	if (!fsync(fd))		ret=1;	close(fd);	return ret;}/* Sync disk in current part setting. Returns number of successful * sync attempts. */static int sync_current_disk(void){	char *name;	int ret=0;	do {		name = devname_get(part_info_get_name(part));		ret += sync_part(name);	} while (part_info_part_next(part));	return ret;}static void sync_disk(disk_info_t di){	int succ;	part_info_reset(part);	while ((succ=part_info_disk_next(part)))		if (part_info_get_major(part) == di->major &&		    part_info_get_minor(part) == di->minor)			break;		if (!succ) {		DEBUG("(%d, %d): No such disk.", di->major, di->minor);		return;	}		if (!sync_current_disk())		disk_info_mark_ro(di);}		static void sync_spinning_disks(disk_info_t head){	int succ;	disk_info_t di;		part_info_reset(part);		while ((succ=part_info_disk_next(part))) {		int major = part_info_get_major(part);		int minor = part_info_get_minor(part);				for (di=head; di; di=di->next) {			if (di->major == major && di->minor==minor)				break;		}		if (di && (di->state==DISK_STATE_STOPPED || !di->is_rw))			continue;				sync_current_disk();	}}static int get_min_timeout(disk_info_t di){	int to, min=0;		for (; di; di=di->next) {		to = timeout_get(di->timeouts);		if (!NFD_TO_IS_REGULAR(to))			continue;		if (to < min || min == 0)			min = to;	}		return min ? min : 60;}		static void advance_timeouts(disk_info_t di){	while (advance_timeout) {		advance_timeout--;		timeout_advance_default();		for (; di; di=di->next) {			int delta = timeout_advance(&di->timeouts);			if (NFD_TO_DO_SKIP(timeout_get(di->timeouts))) {				di->state = DISK_STATE_IGNORED;				di->time_left = 0;				DEBUG("Timeout update. Ignoring disk %s",				      di->name);			} else {				/* Don't touch state of already spun down				 * disks regardless of new timeout. */				if (di->state == DISK_STATE_STOPPED)					continue;				di->state = DISK_STATE_SPINNING;				di->time_left += delta;				DEBUG("Timeout update. Disk %s, timeout %d, "				      "left %d", 				      di->name, timeout_get(di->timeouts),				      di->time_left);			}		}			}}/* Checks whether it's time to spin down a disk. If yes, it does so after * syncing the disk. * Returns 1 iff disk is properly spun down. */static int try_spindown(disk_stat_t ds, disk_info_t di){		DEBUG("Disk %s, Time left %d.", di->name, di->time_left);	if (di->time_left > 0)		/* Spindown time not reached. */		return 0;	INFO("Spinning down %s.", di->name);	/* Syncing can last a while, in which time new data might have been	 * produced. So we sync twice, assuming that the second sync is	 * quite fast.	 */	if (di->is_rw) {		sync_disk(di);		sync_disk(di);	}		disk_stat_update(ds);		/* Cancel spindown if there was other activity than our sync on the	 * disk.	 */	if ((disk_stat_check(ds, di->major, di->minor) & ~DISK_STAT_WRITES) 	    != DISK_STAT_VALID) {		INFO("Spindown of %s cancelled.", di->name);		di->time_left=timeout_get(di->timeouts);		return 0;	}		return spindown(di);}/* Returns new noflushd state depending on number of rw disks spinning/stopped. */static nfd_state_t check_io(disk_info_t di, disk_stat_t ds, int interval){	int rw_spinning=0, rw_stopped=0;	int irq_is_idle;	disk_stat_flags io_flags;	disk_stat_update(ds);	/* Racy, but just about as close as we can reasonably get. */	irq_is_idle = intr_stat_check_idleness();		for (; di; di=di->next) {		io_flags=disk_stat_check(ds, di->major, di->minor);		switch (di->state) {		case DISK_STATE_IGNORED:			break;		case DISK_STATE_SPINNING:			if (io_flags==DISK_STAT_INVALID) {				INFO("No stats for %s. Ignoring.", di->name);				di->state = DISK_STATE_IGNORED;				break;			}			if (io_flags & DISK_STAT_READS) {				di->time_left=timeout_get(di->timeouts);			} else {				/* time_left is int, so potentially diving a				 * little bit into negative should be okay. */				if (di->time_left > 0)					di->time_left-=interval;				if (irq_is_idle && try_spindown(ds, di)) 					di->state=DISK_STATE_STOPPED;			}			break;		case DISK_STATE_STOPPED:			if (io_flags==DISK_STAT_INVALID) {				ERR("No stats for stopped disk %s", di->name);				di->state=DISK_STATE_SPINNING;				break;			}			if (io_flags & (DISK_STAT_READS|DISK_STAT_WRITES)) {				time_t now, delta;				di->time_left=timeout_get(di->timeouts);				di->state=DISK_STATE_SPINNING;								now = time(NULL);				if (now > di->spundown_at)					delta = now - di->spundown_at;				else					delta = di->spundown_at-now;								INFO("Spinning up %s after %ld minutes.",				     di->name, delta/60);				sync_disk(di);			}			break;		case DISK_STATE_UNINITIALISED:		default:			BUG("Illegal disk state %d on %s (%d, %d)", 				di->state, di->name, di->major, di->minor);		}		/* Ignored disks are treated as spinning. */		if (di->state==DISK_STATE_STOPPED)			rw_stopped+=di->is_rw;		else			rw_spinning+=di->is_rw;	}		DEBUG("rw disks: %d stopped, %d spinning", rw_stopped, rw_spinning);	if (!rw_stopped)		return NFD_STATE_SPINNING;	if (!rw_spinning)		return NFD_STATE_STOPPED;	return NFD_STATE_PARTIAL;}void nfd_daemon(disk_info_t head, disk_stat_t stat){	long left, sync_left, interval;		time_t t_new, t_old;	nfd_state_t nfd_state=NFD_STATE_UNINITIALISED;	sync_left = 0;	t_old = time(NULL);		for (;;) {		advance_timeouts(head);		/* This is nasty but the only way to eliminate a race		 * between checking i/o stats and spindown. */		kupdate_stop(kupdate);				t_new = time(NULL);				if (t_new > t_old)			interval=t_new-t_old;		else			interval=t_old-t_new;				t_old=t_new;				DEBUG("Check interval %ld", interval);		nfd_state=check_io(head, stat, interval);				switch (nfd_state) {		case NFD_STATE_SPINNING:			kupdate_start(kupdate);			/* All disks spinning - poll 60 times minimum before			 * spindown. */			left = get_min_timeout(head)/60;			sync_left = 0;			break;		/* XXX: The syncing code recognizes hotplugged disks now, but		 *      we do not yet put them on the disk_info list. Don't		 *      optimize the stopped case therefore, as one of these		 *      alien disks might be out there. This will be handled		 *      by rebuiling the disk_info list someday, but for now		 *      we settle with the easy solution.		 */		case NFD_STATE_STOPPED:		case NFD_STATE_PARTIAL:			/* We emulate kupdate - use its wakeup interval			 * for sync calls, but keep (at most) default polling 			 * interval for i/o checks. */			if (sync_left <= 0) {				sync_spinning_disks(head);				sync_left = kupdate_get_interval(kupdate);			}			left = get_min_timeout(head)/60;			if (sync_left < left)				left = sync_left;			sync_left -= left;			break;#if 0		/* Temporarily disabled. See above. */		case NFD_STATE_STOPPED:			/* Poll for spinup every 5 seconds. */			left = 5;			sync_left = 0;			break;#endif		case NFD_STATE_UNINITIALISED:		default:			BUG("Illegal state");				}		DEBUG("State %d, sleeping %ld seconds", nfd_state, left);		while (!advance_timeout && (left=sleep(left)));	}}			

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -