1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/systm.h>
31 #include <sys/cred.h>
32 #include <sys/proc.h>
33 #include <sys/user.h>
34 #include <sys/vfs.h>
35 #include <sys/vnode.h>
36 #include <sys/pathname.h>
37 #include <sys/uio.h>
38 #include <sys/tiuser.h>
39 #include <sys/sysmacros.h>
40 #include <sys/kmem.h>
41 #include <netinet/in.h>
42 #include <sys/mount.h>
43 #include <sys/ioctl.h>
44 #include <sys/statvfs.h>
45 #include <sys/errno.h>
46 #include <sys/debug.h>
47 #include <sys/cmn_err.h>
48 #include <sys/utsname.h>
49 #include <sys/bootconf.h>
50 #include <sys/modctl.h>
51
52 #include <vm/hat.h>
53 #include <vm/as.h>
54 #include <vm/page.h>
55 #include <vm/pvn.h>
56 #include <vm/seg.h>
57 #include <vm/seg_map.h>
58 #include <vm/seg_vn.h>
59 #include <vm/rm.h>
60 #include <sys/fs/cachefs_fs.h>
61
62 #define C_CACHE_VALID(TOKEN_MTIME, NEW_MTIME) \
63 ((TOKEN_MTIME.tv_sec == NEW_MTIME.tv_sec) && \
64 (TOKEN_MTIME.tv_nsec == NEW_MTIME.tv_nsec))
65
66 static int
c_cod_init_cached_object(fscache_t * fscp,cnode_t * cp,vattr_t * vap,cred_t * cr)67 c_cod_init_cached_object(fscache_t *fscp, cnode_t *cp, vattr_t *vap,
68 cred_t *cr)
69 {
70 int error;
71 cachefs_metadata_t *mdp = &cp->c_metadata;
72
73 ASSERT(cr != NULL);
74 ASSERT(MUTEX_HELD(&cp->c_statelock));
75
76 /* NFSv4 option sets strict consistency */
77 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
78
79 /* if attributes not passed in then get them */
80 if (vap == NULL) {
81 /* if not connected then cannot get attrs */
82 if ((fscp->fs_cdconnected != CFS_CD_CONNECTED) ||
83 (fscp->fs_backvfsp == NULL))
84 return (ETIMEDOUT);
85
86 /* get backvp if necessary */
87 if (cp->c_backvp == NULL) {
88 error = cachefs_getbackvp(fscp, cp);
89 if (error)
90 return (error);
91 }
92
93 /* get the attributes */
94 cp->c_attr.va_mask = AT_ALL;
95 error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr, NULL);
96 if (error)
97 return (error);
98 } else {
99 /* copy passed in attributes into the cnode */
100 cp->c_attr = *vap;
101 }
102
103 cp->c_size = cp->c_attr.va_size;
104 mdp->md_x_time = fscp->fs_cod_time;
105 mdp->md_consttype = CFS_FS_CONST_CODCONST;
106 cp->c_flags |= CN_UPDATED;
107 return (0);
108 }
109
110 static int
c_cod_check_cached_object(struct fscache * fscp,struct cnode * cp,int verify_what,cred_t * cr)111 c_cod_check_cached_object(struct fscache *fscp, struct cnode *cp,
112 int verify_what, cred_t *cr)
113 {
114 struct vattr attrs;
115 int fail = 0, backhit = 0;
116 int error = 0;
117 cachefs_metadata_t *mdp = &cp->c_metadata;
118
119 #ifdef CFSDEBUG
120 CFS_DEBUG(CFSDEBUG_VOPS)
121 printf("c_cod_check_cached_object: ENTER cp %p\n", cp);
122 #endif
123 ASSERT(cr);
124 ASSERT(MUTEX_HELD(&cp->c_statelock));
125
126 /* nothing to do if not connected */
127 if ((fscp->fs_cdconnected != CFS_CD_CONNECTED) ||
128 (fscp->fs_backvfsp == NULL))
129 goto out;
130
131 /* done if do not have to check and cod button has not been pushed */
132 if (((verify_what & C_BACK_CHECK) == 0) &&
133 (C_CACHE_VALID(mdp->md_x_time, fscp->fs_cod_time)) &&
134 ((mdp->md_flags & MD_NEEDATTRS) == 0))
135 goto out;
136
137 /* get backvp if necessary */
138 if (cp->c_backvp == NULL) {
139 error = cachefs_getbackvp(fscp, cp);
140 if (error)
141 goto out;
142 }
143
144 /*
145 * If the cnode is being populated, and we're not the populating
146 * thread, then block until the pop thread completes. If we are the
147 * pop thread, then we may come in here, but not to nuke the directory
148 * cnode at a critical juncture.
149 */
150 again:
151 while ((cp->c_flags & CN_ASYNC_POP_WORKING) &&
152 (cp->c_popthrp != curthread)) {
153 cv_wait(&cp->c_popcv, &cp->c_statelock);
154
155 /*
156 * recheck backvp and connectivity - if backvp now null,
157 * something bad happened, so don't bother trying to 'get' it
158 */
159 if ((cp->c_backvp == NULL) ||
160 (fscp->fs_cdconnected != CFS_CD_CONNECTED) ||
161 (fscp->fs_backvfsp == NULL)) {
162 if (cp->c_flags | CN_STALE) {
163 cp->c_flags |= CN_NOCACHE;
164 error = ESTALE;
165 }
166 goto out;
167 }
168 }
169
170 /* get the file attributes from the back fs */
171 attrs.va_mask = AT_ALL;
172 error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL);
173 backhit = 1;
174 if (error)
175 goto out;
176
177 /* if the mtime or size of the file has changed */
178 if ((!C_CACHE_VALID(mdp->md_vattr.va_mtime, attrs.va_mtime) ||
179 (cp->c_size != attrs.va_size)) &&
180 ((mdp->md_flags & MD_NEEDATTRS) == 0)) {
181 fail = 1;
182 if (vn_has_cached_data(CTOV(cp))) {
183 mutex_exit(&cp->c_statelock);
184 error = cachefs_putpage_common(CTOV(cp),
185 (offset_t)0, 0, B_INVAL, cr);
186 mutex_enter(&cp->c_statelock);
187 if (CFS_TIMEOUT(fscp, error))
188 goto out;
189 error = 0;
190 /*
191 * if an async pop started while the lock was
192 * dropped, go back and try again
193 */
194 if ((cp->c_flags & CN_ASYNC_POP_WORKING) &&
195 (cp->c_popthrp != curthread))
196 goto again;
197 }
198 /*
199 * We should properly handle the CN_NOCACHE flag here.
200 * In fact, we should remember that cachefs_inval_object()
201 * forcibly sets/unsets the flag, so we should keep a
202 * state of the flag over the call.
203 */
204 if ((cp->c_flags & CN_NOCACHE) == 0)
205 cachefs_inval_object(cp);
206 else {
207 cachefs_inval_object(cp);
208 cp->c_flags |= CN_NOCACHE;
209 }
210 if ((CTOV(cp))->v_type == VREG) {
211 attrs.va_mask = AT_ALL;
212 error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL);
213 if (error)
214 goto out;
215 }
216 if (!vn_has_cached_data(CTOV(cp))) {
217 cp->c_size = attrs.va_size;
218 #ifdef CFSDEBUG
219 } else {
220 CFS_DEBUG(CFSDEBUG_VOPS)
221 printf("c_cod_check: v_pages not null\n");
222 #endif
223 }
224 }
225
226 /* toss cached acl info if ctime changed */
227 if (!C_CACHE_VALID(mdp->md_vattr.va_ctime, attrs.va_ctime)) {
228 cachefs_purgeacl(cp);
229 }
230
231 cp->c_attr = attrs;
232 if (attrs.va_size > cp->c_size)
233 cp->c_size = attrs.va_size;
234 mdp->md_x_time = fscp->fs_cod_time;
235 mdp->md_flags &= ~MD_NEEDATTRS;
236 cachefs_cnode_setlocalstats(cp);
237 cp->c_flags |= CN_UPDATED;
238
239 out:
240 if (backhit != 0) {
241 if (fail != 0)
242 fscp->fs_stats.st_fails++;
243 else
244 fscp->fs_stats.st_passes++;
245 }
246
247 #ifdef CFSDEBUG
248 CFS_DEBUG(CFSDEBUG_VOPS)
249 printf("c_cod_check_cached_object: EXIT\n");
250 #endif
251
252 return (error);
253 }
254
255 /*ARGSUSED*/
256 static void
c_cod_modify_cached_object(struct fscache * fscp,struct cnode * cp,cred_t * cr)257 c_cod_modify_cached_object(struct fscache *fscp, struct cnode *cp, cred_t *cr)
258 {
259 struct vattr attrs;
260 int error = 0;
261 nlink_t nlink;
262 cachefs_metadata_t *mdp = &cp->c_metadata;
263
264 ASSERT(MUTEX_HELD(&cp->c_statelock));
265 ASSERT(fscp->fs_cdconnected == CFS_CD_CONNECTED);
266 ASSERT(fscp->fs_backvfsp);
267
268 fscp->fs_stats.st_modifies++;
269
270 /* from now on, make sure we're using the server's idea of time */
271 mdp->md_flags &= ~(MD_LOCALCTIME | MD_LOCALMTIME);
272 mdp->md_flags |= MD_NEEDATTRS;
273
274 /* if in write-around mode, make sure file is nocached */
275 if (CFS_ISFS_WRITE_AROUND(fscp)) {
276 if ((cp->c_flags & CN_NOCACHE) == 0)
277 cachefs_nocache(cp);
278
279 /*
280 * If a directory, then defer getting the new attributes
281 * until requested. Might be a little bit faster this way.
282 */
283 if (CTOV(cp)->v_type == VDIR)
284 goto out;
285 }
286
287 /* get the new mtime so the next call to check_cobject does not fail */
288 if (cp->c_backvp == NULL) {
289 error = cachefs_getbackvp(fscp, cp);
290 if (error) {
291 mdp->md_vattr.va_mtime.tv_sec = 0;
292 goto out;
293 }
294 }
295 attrs.va_mask = AT_ALL;
296 ASSERT(cp->c_backvp != NULL);
297 error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL);
298 if (error) {
299 mdp->md_vattr.va_mtime.tv_sec = 0;
300 goto out;
301 }
302 nlink = cp->c_attr.va_nlink;
303 cp->c_attr = attrs;
304 cp->c_attr.va_nlink = nlink;
305 if ((attrs.va_size > cp->c_size) || !vn_has_cached_data(CTOV(cp)))
306 cp->c_size = attrs.va_size;
307 mdp->md_flags &= ~MD_NEEDATTRS;
308 cachefs_cnode_setlocalstats(cp);
309 out:
310 cp->c_flags |= CN_UPDATED;
311 }
312
313 /*ARGSUSED*/
314 static void
c_cod_invalidate_cached_object(struct fscache * fscp,struct cnode * cp,cred_t * cr)315 c_cod_invalidate_cached_object(struct fscache *fscp, struct cnode *cp,
316 cred_t *cr)
317 {
318 cachefs_metadata_t *mdp = &cp->c_metadata;
319
320 ASSERT(MUTEX_HELD(&cp->c_statelock));
321 mdp->md_vattr.va_mtime.tv_sec = 0;
322 mdp->md_flags |= MD_NEEDATTRS;
323 cp->c_flags |= CN_UPDATED;
324 }
325
326 /*ARGSUSED*/
327 static void
c_cod_convert_cached_object(struct fscache * fscp,struct cnode * cp,cred_t * cr)328 c_cod_convert_cached_object(struct fscache *fscp, struct cnode *cp,
329 cred_t *cr)
330 {
331 cachefs_metadata_t *mdp = &cp->c_metadata;
332
333 ASSERT(MUTEX_HELD(&cp->c_statelock));
334 mdp->md_flags |= MD_NEEDATTRS;
335 mdp->md_consttype = CFS_FS_CONST_CODCONST;
336 cp->c_flags |= CN_UPDATED;
337 }
338
339 struct cachefsops codcfsops = {
340 c_cod_init_cached_object,
341 c_cod_check_cached_object,
342 c_cod_modify_cached_object,
343 c_cod_invalidate_cached_object,
344 c_cod_convert_cached_object
345 };
346