#include "fdesc.h"
#endif /* FDESC */
-static int devfs_update(struct vnode *vp, struct timeval *access,
- struct timeval *modify);
-void devfs_rele_node(devnode_t *);
+static int devfs_update(struct vnode *vp, struct timeval *access,
+ struct timeval *modify);
+void devfs_rele_node(devnode_t *);
+static void devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags);
+static boolean_t devfs_update_needed(long now_s, long last_s);
+void dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags);
+void dn_times_now(devnode_t *dnp, uint32_t just_changed_flags);
+void dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags);
+
+void
+dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags)
+{
+
+ lck_mtx_assert(&devfs_attr_mutex, LCK_MTX_ASSERT_OWNED);
+
+ if (just_changed_flags & DEVFS_UPDATE_ACCESS) {
+ dnp->dn_atime.tv_sec = t1->tv_sec;
+ dnp->dn_atime.tv_nsec = t1->tv_usec * 1000;
+ dnp->dn_access = 0;
+ } else if (dnp->dn_access) {
+ dnp->dn_atime.tv_sec = MIN(t1->tv_sec, dnp->dn_atime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS);
+ dnp->dn_atime.tv_nsec = t1->tv_usec * 1000;
+ dnp->dn_access = 0;
+ }
+
+ if (just_changed_flags & DEVFS_UPDATE_MOD) {
+ dnp->dn_mtime.tv_sec = t2->tv_sec;
+ dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000;
+ dnp->dn_update = 0;
+ } else if (dnp->dn_update) {
+ dnp->dn_mtime.tv_sec = MIN(t2->tv_sec, dnp->dn_mtime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS);
+ dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000;
+ dnp->dn_update = 0;
+ }
+
+ if (just_changed_flags & DEVFS_UPDATE_CHANGE) {
+ dnp->dn_ctime.tv_sec = t3->tv_sec;
+ dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000;
+ dnp->dn_change = 0;
+ } else if (dnp->dn_change) {
+ dnp->dn_ctime.tv_sec = MIN(t3->tv_sec, dnp->dn_ctime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS);
+ dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000;
+ dnp->dn_change = 0;
+ }
+}
+
+void
+dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags)
+{
+ if (just_changed_flags & DEVFS_UPDATE_CHANGE) {
+ dnp->dn_change = 1;
+ }
+ if (just_changed_flags & DEVFS_UPDATE_ACCESS) {
+ dnp->dn_access = 1;
+ }
+ if (just_changed_flags & DEVFS_UPDATE_MOD) {
+ dnp->dn_update = 1;
+ }
+}
+
+/*
+ * Update times based on pending updates and optionally a set of new changes.
+ */
+void
+dn_times_now(devnode_t * dnp, uint32_t just_changed_flags)
+{
+ struct timeval now;
+
+ DEVFS_ATTR_LOCK_SPIN();
+ microtime(&now);
+ dn_times_locked(dnp, &now, &now, &now, just_changed_flags);
+ DEVFS_ATTR_UNLOCK();
+}
/*
DEVFS_LOCK();
file_node = VTODN(vp);
- microtime(&now);
- dn_times(file_node, &now, &now, &now);
-
VATTR_RETURN(vap, va_mode, file_node->dn_mode);
/*
VATTR_RETURN(vap, va_iosize, MAXPHYSIO);
else
VATTR_RETURN(vap, va_iosize, vp->v_mount->mnt_vfsstat.f_iosize);
+
+
+ DEVFS_ATTR_LOCK_SPIN();
+
+ microtime(&now);
+ dn_times_locked(file_node, &now, &now, &now, 0);
+
/* if the time is bogus, set it to the boot time */
if (file_node->dn_ctime.tv_sec == 0) {
file_node->dn_ctime.tv_sec = boottime_sec();
VATTR_RETURN(vap, va_change_time, file_node->dn_ctime);
VATTR_RETURN(vap, va_modify_time, file_node->dn_mtime);
VATTR_RETURN(vap, va_access_time, file_node->dn_atime);
+
+ DEVFS_ATTR_UNLOCK();
+
VATTR_RETURN(vap, va_gen, 0);
VATTR_RETURN(vap, va_filerev, 0);
VATTR_RETURN(vap, va_acl, NULL);
{
struct vnode * vp = ap->a_vp;
register devnode_t * dnp;
- struct timeval now;
if (vnode_isinuse(vp, 1)) {
DEVFS_LOCK();
dnp = VTODN(vp);
- microtime(&now);
- dn_times(dnp, &now, &now, &now);
+ dn_times_now(dnp, 0);
DEVFS_UNLOCK();
}
return (0);
{
struct vnode * vp = ap->a_vp;
register devnode_t * dnp;
- struct timeval now;
if (vnode_isinuse(vp, 0)) {
DEVFS_LOCK();
- microtime(&now);
dnp = VTODN(vp);
- dn_times(dnp, &now, &now, &now);
+ dn_times_now(dnp, 0);
DEVFS_UNLOCK();
}
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
}
+static boolean_t
+devfs_update_needed(long now_s, long last_s)
+{
+ if (now_s > last_s) {
+ if (now_s - last_s >= DEVFS_LAZY_UPDATE_SECONDS) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Given a set of time updates required [to happen at some point], check
+ * either make those changes (and resolve other pending updates) or mark
+ * the devnode for a subsequent update.
+ */
+static void
+devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags)
+{
+ struct timeval now;
+ long now_s;
+
+ microtime(&now);
+ now_s = now.tv_sec;
+
+ if (dnp->dn_change || (just_changed_flags & DEVFS_UPDATE_CHANGE)) {
+ if (devfs_update_needed(now_s, dnp->dn_ctime.tv_sec)) {
+ dn_times_now(dnp, just_changed_flags);
+ return;
+ }
+ }
+ if (dnp->dn_access || (just_changed_flags & DEVFS_UPDATE_ACCESS)) {
+ if (devfs_update_needed(now_s, dnp->dn_atime.tv_sec)) {
+ dn_times_now(dnp, just_changed_flags);
+ return;
+ }
+ }
+ if (dnp->dn_update || (just_changed_flags & DEVFS_UPDATE_MOD)) {
+ if (devfs_update_needed(now_s, dnp->dn_mtime.tv_sec)) {
+ dn_times_now(dnp, just_changed_flags);
+ return;
+ }
+ }
+
+ /* Not going to do anything now--mark for later update */
+ dn_mark_for_delayed_times_update(dnp, just_changed_flags);
+
+ return;
+}
+
static int
devfsspec_read(struct vnop_read_args *ap)
/* struct vnop_read_args {
{
register devnode_t * dnp = VTODN(ap->a_vp);
- dnp->dn_access = 1;
+ devfs_consider_time_update(dnp, DEVFS_UPDATE_ACCESS);
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
}
{
register devnode_t * dnp = VTODN(ap->a_vp);
- dnp->dn_change = 1;
- dnp->dn_update = 1;
+ devfs_consider_time_update(dnp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD);
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
}
/***********************************
* Start actually doing things.... *
***********************************/
- tdp->dn_change = 1;
- tdp->dn_update = 1;
+ devfs_consider_time_update(tdp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD);
/*
* Target must be empty if a directory and have no links
devnode_t * tdp;
devdirent_t * tnp;
int error = 0;
- struct timeval now;
/*
* First catch an arbitrary restriction for this FS
/***********************************
* Start actually doing things.... *
***********************************/
- fp->dn_change = 1;
-
- microtime(&now);
- error = devfs_update(vp, &now, &now);
+ dn_times_now(fp, DEVFS_UPDATE_CHANGE);
if (!error) {
error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
devdirent_t *fnp,*tnp;
int doingdirectory = 0;
int error = 0;
- struct timeval now;
DEVFS_LOCK();
/*
/***********************************
* Start actually doing things.... *
***********************************/
- fp->dn_change = 1;
- microtime(&now);
+ dn_times_now(fp, DEVFS_UPDATE_CHANGE);
- if ( (error = devfs_update(fvp, &now, &now)) ) {
- goto out;
- }
/*
* Check if just deleting a link name.
*/
name_node = dir_node->dn_typeinfo.Dir.dirlist;
nodenumber = 0;
- dir_node->dn_access = 1;
-
while ((name_node || (nodenumber < 2)) && (uio_resid(uio) > 0))
{
switch(nodenumber)
DEVFS_UNLOCK();
uio->uio_offset = pos;
+ devfs_consider_time_update(dir_node, DEVFS_UPDATE_ACCESS);
+
return (error);
}
return (0);
}
+
+ DEVFS_ATTR_LOCK_SPIN();
microtime(&now);
- dn_times(ip, access, modify, &now);
+ dn_times_locked(ip, access, modify, &now, DEVFS_UPDATE_ACCESS | DEVFS_UPDATE_MOD);
+ DEVFS_ATTR_UNLOCK();
return (0);
}