]> git.saurik.com Git - apple/hfs.git/blob - core/hfs_notification.c
614c32d3840fff2757531451fb701c997848fa1d
[apple/hfs.git] / core / hfs_notification.c
1 /*
2 * Copyright (C) 2003-2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/file.h>
32 #include <sys/dirent.h>
33 #include <sys/stat.h>
34 #include <sys/mount.h>
35 #include <sys/vnode.h>
36 #include <sys/malloc.h>
37 #include <sys/ubc.h>
38 #include <sys/quota.h>
39
40 #include <sys/kdebug.h>
41
42 #include "hfs.h"
43 #include "hfs_catalog.h"
44 #include "hfs_cnode.h"
45 #include "hfs_dbg.h"
46 #include "hfs_mount.h"
47 #include "hfs_quota.h"
48 #include "hfs_endian.h"
49
50 #include "BTreesInternal.h"
51 #include "FileMgrInternal.h"
52
53
54
55 void hfs_generate_volume_notifications(struct hfsmount *hfsmp)
56 {
57 fsid_t fsid;
58 u_int32_t freeblks, state=999;
59
60 /* Do not generate low disk notifications for read-only volumes */
61 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
62 return;
63 }
64
65 fsid.val[0] = hfsmp->hfs_raw_dev;
66 fsid.val[1] = vfs_typenum(HFSTOVFS(hfsmp));
67
68 freeblks = hfs_freeblks(hfsmp, 1);
69
70 /*
71 * Find the theshold the number of free blocks fits into.
72 * We fire upon reaching a level below desired only once,
73 * except for when we reach the low disk or near low disk levels
74 * from below, in which case we do not fire unless we have also
75 * reached the desired disk level (hysteresis).
76 * This is illustrated in the following diagram:
77 *
78 * fire ^
79 * --------- desired level
80 * |
81 *
82 *
83 * |
84 * --------- near low disk level
85 * fire v
86 *
87 *
88 * |
89 * --------- low disk level
90 * fire v
91 *
92 *
93 * | ^ fire
94 * --------- very low disk level
95 * fire v |
96 *
97 */
98 if (freeblks < hfsmp->hfs_freespace_notify_dangerlimit) {
99 state = 4;
100 } else if (freeblks < hfsmp->hfs_freespace_notify_warninglimit) {
101 state = 3;
102 } else if (freeblks < hfsmp->hfs_freespace_notify_nearwarninglimit) {
103 state = 2;
104 } else if (freeblks < hfsmp->hfs_freespace_notify_desiredlevel) {
105 /* We are between the near low disk and desired levels */
106 state = 1;
107 } else if (freeblks >= hfsmp->hfs_freespace_notify_desiredlevel) {
108 state = 0;
109 }
110
111 /* Free blocks are less than dangerlimit for the first time */
112 if (state == 4 && !(hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK)) {
113 /* Dump some logging to track down intermittent issues */
114 printf("hfs: set VeryLowDisk: vol:%s, freeblks:%d, dangerlimit:%d\n", hfsmp->vcbVN, freeblks, hfsmp->hfs_freespace_notify_dangerlimit);
115
116 #if HFS_SPARSE_DEV
117 // If we're a sparse device, dump some info about the backing store..
118 hfs_lock_mount(hfsmp);
119 vnode_t backing_vp = hfsmp->hfs_backingvp;
120 if (backing_vp && vnode_get(backing_vp) != 0)
121 backing_vp = NULL;
122 hfs_unlock_mount(hfsmp);
123
124 if (backing_vp) {
125 struct vfsstatfs *sfs = vfs_statfs(vnode_mount(backing_vp));
126 printf("hfs: set VeryLowDisk: vol:%s, backingstore b_avail:%lld, tag:%d\n",
127 hfsmp->vcbVN, sfs->f_bavail, vnode_tag(backing_vp));
128 vnode_put(backing_vp);
129 }
130 #endif
131
132 hfsmp->hfs_notification_conditions |= (VQ_VERYLOWDISK|VQ_LOWDISK|VQ_NEARLOWDISK);
133 vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
134 } else if (state == 3) {
135 /* Free blocks are less than warning limit for the first time */
136 if (!(hfsmp->hfs_notification_conditions & VQ_LOWDISK)) {
137 printf("hfs: set LowDisk: vol:%s, freeblks:%d, warninglimit:%d\n", hfsmp->vcbVN, freeblks, hfsmp->hfs_freespace_notify_warninglimit);
138 hfsmp->hfs_notification_conditions |= (VQ_LOWDISK|VQ_NEARLOWDISK);
139 vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
140 } else if (hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK) {
141 /* Free blocks count has increased from danger limit to warning limit, so just clear VERYLOWDISK warning */
142 printf("hfs: clear VeryLowDisk: vol:%s, freeblks:%d, dangerlimit:%d\n", hfsmp->vcbVN, freeblks, hfsmp->hfs_freespace_notify_dangerlimit);
143 hfsmp->hfs_notification_conditions &= ~VQ_VERYLOWDISK;
144 vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
145 }
146 } else if (state == 2) {
147 /* Free blocks are less than the near warning limit for the first time */
148 if (!(hfsmp->hfs_notification_conditions & VQ_NEARLOWDISK)) {
149 printf("hfs: set NearLowDisk: vol:%s, freeblks:%d, nearwarninglimit:%d\n", hfsmp->vcbVN, freeblks,
150 hfsmp->hfs_freespace_notify_nearwarninglimit);
151
152 hfsmp->hfs_notification_conditions |= VQ_NEARLOWDISK;
153 vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
154 } else {
155 /* Free blocks count has increased from warning/danger limit to near warning limit,
156 * so clear VERYLOWDISK / LOWDISK warnings, and signal if we clear VERYLOWDISK */
157 hfsmp->hfs_notification_conditions &= ~VQ_LOWDISK;
158 if (hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK) {
159 printf("hfs: clear VeryLowDisk: vol:%s, freeblks:%d, dangerlimit:%d\n", hfsmp->vcbVN, freeblks,
160 hfsmp->hfs_freespace_notify_dangerlimit);
161
162 hfsmp->hfs_notification_conditions &= ~VQ_VERYLOWDISK;
163 vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
164 }
165 }
166 } else if (state == 1) {
167 /* Free blocks are less than the desireable level, but more than the near warning level
168 * In this case, we may have to notify if we were previously underneath the danger limit */
169 if (hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK) {
170 printf("hfs: clear VeryLowDisk: vol:%s, freeblks:%d, dangerlimit:%d\n", hfsmp->vcbVN, freeblks,
171 hfsmp->hfs_freespace_notify_dangerlimit);
172
173 hfsmp->hfs_notification_conditions &= ~VQ_VERYLOWDISK;
174 vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
175 }
176 } else if (state == 0) {
177 /* Free blocks count has increased to desirable level, so clear all conditions */
178 if (hfsmp->hfs_notification_conditions & (VQ_NEARLOWDISK|VQ_LOWDISK|VQ_VERYLOWDISK)) {
179 if (hfsmp->hfs_notification_conditions & VQ_NEARLOWDISK) {
180 printf("hfs: clear NearLowDisk: vol:%s, freeblks:%d, nearwarninglimit:%d, desiredlevel:%d\n", hfsmp->vcbVN,
181 freeblks, hfsmp->hfs_freespace_notify_nearwarninglimit, hfsmp->hfs_freespace_notify_desiredlevel);
182 }
183 if (hfsmp->hfs_notification_conditions & VQ_LOWDISK) {
184 printf("hfs: clear LowDisk: vol:%s, freeblks:%d, warninglimit:%d, desiredlevel:%d\n", hfsmp->vcbVN, freeblks,
185 hfsmp->hfs_freespace_notify_warninglimit, hfsmp->hfs_freespace_notify_desiredlevel);
186 }
187 if (hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK) {
188 printf("hfs: clear VeryLowDisk: vol:%s, freeblks:%d, dangerlimit:%d\n", hfsmp->vcbVN, freeblks, hfsmp->hfs_freespace_notify_warninglimit);
189 }
190 hfsmp->hfs_notification_conditions &= ~(VQ_VERYLOWDISK|VQ_LOWDISK|VQ_NEARLOWDISK);
191 if (hfsmp->hfs_notification_conditions == 0) {
192 vfs_event_signal(&fsid, VQ_UPDATE|VQ_DESIRED_DISK, (intptr_t)NULL);
193 } else {
194 vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
195 }
196 }
197 }
198 }