linux22-fix-20040405
[openafs.git] / src / afs / LINUX / osi_misc.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Linux support routines.
12  *
13  */
14 #include <afsconfig.h>
15 #include "afs/param.h"
16
17 RCSID
18     ("$Header$");
19
20 #include "afs/sysincludes.h"
21 #include "afsincludes.h"
22 #include "afs/afs_stats.h"
23 #if defined(AFS_LINUX24_ENV)
24 #include "h/smp_lock.h"
25 #endif
26
27 #if defined(AFS_LINUX24_ENV)
28 /* Lookup name and return vnode for same. */
29 int
30 osi_lookupname(char *aname, uio_seg_t seg, int followlink,
31                         vnode_t ** dirvpp, struct dentry **dpp)
32 {
33     int code;
34     extern struct nameidata afs_cacheNd;
35     struct nameidata *nd = &afs_cacheNd;
36
37     code = ENOENT;
38     if (seg == AFS_UIOUSER) {
39         code =
40             followlink ? user_path_walk(aname,
41                                         nd) : user_path_walk_link(aname, nd);
42     } else {
43         if (path_init(aname, followlink ? LOOKUP_FOLLOW : 0, nd))
44             code = path_walk(aname, nd);
45     }
46
47     if (!code) {
48         if (nd->dentry->d_inode) {
49             *dpp = dget(nd->dentry);
50             code = 0;
51         } else {
52             code = ENOENT;
53             path_release(nd);
54         }
55     }
56     return code;
57 }
58 #else
59 int
60 osi_lookupname(char *aname, uio_seg_t seg, int followlink, vnode_t ** dirvpp,
61                struct dentry **dpp)
62 {
63     struct dentry *dp = NULL;
64     int code;
65
66     code = ENOENT;
67     if (seg == AFS_UIOUSER) {
68         dp = followlink ? namei(aname) : lnamei(aname);
69     } else {
70         dp = lookup_dentry(aname, NULL, followlink ? 1 : 0);
71     }
72
73     if (dp && !IS_ERR(dp)) {
74         if (dp->d_inode) {
75             *dpp = dp;
76             code = 0;
77         } else
78             dput(dp);
79     }
80
81     return code;
82 }
83 #endif
84
85 /* Intialize cache device info and fragment size for disk cache partition. */
86 int
87 osi_InitCacheInfo(char *aname)
88 {
89     int code;
90     struct dentry *dp;
91     extern ino_t cacheInode;
92     extern struct osi_dev cacheDev;
93     extern afs_int32 afs_fsfragsize;
94     extern struct super_block *afs_cacheSBp;
95     code = osi_lookupname(aname, AFS_UIOSYS, 1, NULL, &dp);
96     if (code)
97         return ENOENT;
98
99     cacheInode = dp->d_inode->i_ino;
100     cacheDev.dev = dp->d_inode->i_sb->s_dev;
101     afs_fsfragsize = dp->d_inode->i_sb->s_blocksize - 1;
102     afs_cacheSBp = dp->d_inode->i_sb;
103
104     dput(dp);
105
106     return 0;
107 }
108
109
110 #define FOP_READ(F, B, C) (F)->f_op->read(F, B, (size_t)(C), &(F)->f_pos)
111 #define FOP_WRITE(F, B, C) (F)->f_op->write(F, B, (size_t)(C), &(F)->f_pos)
112
113 /* osi_rdwr
114  * Seek, then read or write to an open inode. addrp points to data in
115  * kernel space.
116  */
117 int
118 osi_rdwr(int rw, struct osi_file *file, caddr_t addrp, size_t asize,
119          size_t * resid)
120 {
121     int code = 0;
122     KERNEL_SPACE_DECL;
123     struct file *filp = &file->file;
124     off_t offset = file->offset;
125     unsigned long savelim;
126
127     /* Seek to the desired position. Return -1 on error. */
128     if (filp->f_op->llseek) {
129         if (filp->f_op->llseek(filp, (loff_t) offset, 0) != offset)
130             return -1;
131     } else
132         filp->f_pos = offset;
133
134     savelim = current->rlim[RLIMIT_FSIZE].rlim_cur;
135     current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
136
137     /* Read/Write the data. */
138     TO_USER_SPACE();
139     if (rw == UIO_READ)
140         code = FOP_READ(filp, addrp, asize);
141     else if (rw == UIO_WRITE)
142         code = FOP_WRITE(filp, addrp, asize);
143     else                        /* all is well? */
144         code = asize;
145     TO_KERNEL_SPACE();
146
147     current->rlim[RLIMIT_FSIZE].rlim_cur = savelim;
148
149     if (code >= 0) {
150         *resid = asize - code;
151         return 0;
152     } else
153         return -1;
154 }
155
156 /* This variant is called from AFS read/write routines and takes a uio
157  * struct and, if successful, returns 0.
158  */
159 int
160 osi_file_uio_rdwr(struct osi_file *osifile, uio_t * uiop, int rw)
161 {
162     struct file *filp = &osifile->file;
163     struct inode *ip = FILE_INODE(&osifile->file);
164     KERNEL_SPACE_DECL;
165     int code = 0;
166     struct iovec *iov;
167     int count;
168     unsigned long savelim;
169
170     savelim = current->rlim[RLIMIT_FSIZE].rlim_cur;
171     current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
172
173     if (uiop->uio_seg == AFS_UIOSYS)
174         TO_USER_SPACE();
175
176     filp->f_pos = uiop->uio_offset;
177     while (code == 0 && uiop->uio_resid > 0 && uiop->uio_iovcnt > 0) {
178         iov = uiop->uio_iov;
179         count = iov->iov_len;
180         if (count == 0) {
181             uiop->uio_iov++;
182             uiop->uio_iovcnt--;
183             continue;
184         }
185
186         if (rw == UIO_READ)
187             code = FOP_READ(filp, iov->iov_base, count);
188         else
189             code = FOP_WRITE(filp, iov->iov_base, count);
190
191         if (code < 0) {
192             code = -code;
193             break;
194         }
195
196         iov->iov_base += code;
197         iov->iov_len -= code;
198         uiop->uio_resid -= code;
199         uiop->uio_offset += code;
200         code = 0;
201     }
202
203     if (uiop->uio_seg == AFS_UIOSYS)
204         TO_KERNEL_SPACE();
205
206     current->rlim[RLIMIT_FSIZE].rlim_cur = savelim;
207
208     return code;
209 }
210
211 /* setup_uio 
212  * Setup a uio struct.
213  */
214 void
215 setup_uio(uio_t * uiop, struct iovec *iovecp, char *buf, afs_offs_t pos,
216           int count, uio_flag_t flag, uio_seg_t seg)
217 {
218     iovecp->iov_base = buf;
219     iovecp->iov_len = count;
220     uiop->uio_iov = iovecp;
221     uiop->uio_iovcnt = 1;
222     uiop->uio_offset = pos;
223     uiop->uio_seg = seg;
224     uiop->uio_resid = count;
225     uiop->uio_flag = flag;
226 }
227
228
229 /* uiomove
230  * UIO_READ : dp -> uio
231  * UIO_WRITE : uio -> dp
232  */
233 int
234 uiomove(char *dp, int length, uio_flag_t rw, uio_t * uiop)
235 {
236     int count, n;
237     struct iovec *iov;
238     int code;
239
240     while (length > 0 && uiop->uio_resid > 0 && uiop->uio_iovcnt > 0) {
241         iov = uiop->uio_iov;
242         count = iov->iov_len;
243
244         if (!count) {
245             uiop->uio_iov++;
246             uiop->uio_iovcnt--;
247             continue;
248         }
249
250         if (count > length)
251             count = length;
252
253         switch (uiop->uio_seg) {
254         case AFS_UIOSYS:
255             switch (rw) {
256             case UIO_READ:
257                 memcpy(iov->iov_base, dp, count);
258                 break;
259             case UIO_WRITE:
260                 memcpy(dp, iov->iov_base, count);
261                 break;
262             default:
263                 printf("uiomove: Bad rw = %d\n", rw);
264                 return -EINVAL;
265             }
266             break;
267         case AFS_UIOUSER:
268             switch (rw) {
269             case UIO_READ:
270                 AFS_COPYOUT(dp, iov->iov_base, count, code);
271                 break;
272             case UIO_WRITE:
273                 AFS_COPYIN(iov->iov_base, dp, count, code);
274                 break;
275             default:
276                 printf("uiomove: Bad rw = %d\n", rw);
277                 return -EINVAL;
278             }
279             break;
280         default:
281             printf("uiomove: Bad seg = %d\n", uiop->uio_seg);
282             return -EINVAL;
283         }
284
285         dp += count;
286         length -= count;
287         iov->iov_base += count;
288         iov->iov_len -= count;
289         uiop->uio_offset += count;
290         uiop->uio_resid -= count;
291     }
292     return 0;
293 }
294
295 void
296 afs_osi_SetTime(osi_timeval_t * tvp)
297 {
298     extern int (*sys_settimeofdayp) (struct timeval * tv,
299                                      struct timezone * tz);
300 #ifdef AFS_LINUX_64BIT_KERNEL
301     struct timeval tv;
302     AFS_STATCNT(osi_SetTime);
303     tv.tv_sec = tvp->tv_sec;
304     tv.tv_usec = tvp->tv_usec;
305     (void)(*sys_settimeofdayp) (&tv, NULL);
306 #else
307     KERNEL_SPACE_DECL;
308
309     AFS_STATCNT(osi_SetTime);
310
311     TO_USER_SPACE();
312     (void)(*sys_settimeofdayp) (tvp, NULL);
313     TO_KERNEL_SPACE();
314 #endif
315 }
316
317 /* Free all the pages on any of the vnodes in the vlru. Must be done before
318  * freeing all memory.
319  */
320 void
321 osi_linux_free_inode_pages(void)
322 {
323     int i;
324     struct vcache *tvc;
325     struct inode *ip;
326     extern struct vcache *afs_vhashT[VCSIZE];
327
328     for (i = 0; i < VCSIZE; i++) {
329         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
330             ip = AFSTOI(tvc);
331 #if defined(AFS_LINUX24_ENV)
332             if (ip->i_data.nrpages) {
333 #else
334             if (ip->i_nrpages) {
335 #endif
336 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
337                 truncate_inode_pages(&ip->i_data, 0);
338 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,15)
339                 truncate_inode_pages(ip, 0);
340 #else
341                 invalidate_inode_pages(ip);
342 #endif
343 #if defined(AFS_LINUX24_ENV)
344                 if (ip->i_data.nrpages) {
345 #else
346                 if (ip->i_nrpages) {
347 #endif
348                     printf("Failed to invalidate all pages on inode 0x%x\n",
349                            ip);
350                 }
351             }
352         }
353     }
354 }
355
356 void
357 osi_clear_inode(struct inode *ip)
358 {
359     cred_t *credp = crref();
360     struct vcache *vcp = ITOAFS(ip);
361
362 #if defined(AFS_LINUX24_ENV)
363     if (atomic_read(&ip->i_count) > 1)
364 #else
365     if (ip->i_count > 1)
366 #endif
367         printf("afs_put_inode: ino %d (0x%x) has count %d\n", ip->i_ino, ip,
368                ip->i_count);
369
370     afs_InactiveVCache(vcp, credp);
371     ObtainWriteLock(&vcp->lock, 504);
372 #if defined(AFS_LINUX24_ENV)
373     atomic_set(&ip->i_count, 0);
374 #else
375     ip->i_count = 0;
376 #endif
377     ip->i_nlink = 0;            /* iput checks this after calling this routine. */
378     ReleaseWriteLock(&vcp->lock);
379     crfree(credp);
380 }
381
382 /* iput an inode. Since we still have a separate inode pool, we don't want
383  * to call iput on AFS inodes, since they would then end up on Linux's
384  * inode_unsed list.
385  */
386 void
387 osi_iput(struct inode *ip)
388 {
389     extern struct vfs *afs_globalVFS;
390
391     AFS_GLOCK();
392 #if defined(AFS_LINUX24_ENV)
393     if (atomic_read(&ip->i_count) == 0
394         || atomic_read(&ip->i_count) & 0xffff0000) {
395 #else
396     if (ip->i_count == 0 || ip->i_count & 0xffff0000) {
397 #endif
398         osi_Panic("IPUT Bad refCount %d on inode 0x%x\n",
399 #if defined(AFS_LINUX24_ENV)
400                   atomic_read(&ip->i_count), ip);
401 #else
402                   ip->i_count, ip);
403 #endif
404     }
405     if (afs_globalVFS && afs_globalVFS == ip->i_sb) {
406 #if defined(AFS_LINUX24_ENV)
407         atomic_dec(&ip->i_count);
408         if (!atomic_read(&ip->i_count))
409 #else
410         ip->i_count--;
411         if (!ip->i_count)
412 #endif
413             osi_clear_inode(ip);
414     } else
415         iput(ip);
416     AFS_GUNLOCK();
417 }
418
419 /* check_bad_parent() : Checks if this dentry's vcache is a root vcache
420  * that has its mvid (parent dir's fid) pointer set to the wrong directory
421  * due to being mounted in multiple points at once. If so, check_bad_parent()
422  * calls afs_lookup() to correct the vcache's mvid, as well as the volume's
423  * dotdotfid and mtpoint fid members.
424  * Parameters:
425  *  dp - dentry to be checked.
426  * Return Values:
427  *  None.
428  * Sideeffects:
429  *   This dentry's vcache's mvid will be set to the correct parent directory's
430  *   fid.
431  *   This root vnode's volume will have its dotdotfid and mtpoint fids set
432  *    to the correct parent and mountpoint fids.
433  */
434
435 void
436 check_bad_parent(struct dentry *dp)
437 {
438     cred_t *credp;
439     struct vcache *vcp = ITOAFS(dp->d_inode), *avc = NULL;
440     struct vcache *pvc = ITOAFS(dp->d_parent->d_inode);
441
442     if (vcp->mvid->Fid.Volume != pvc->fid.Fid.Volume) { /* bad parent */
443         credp = crref();
444
445
446         /* force a lookup, so vcp->mvid is fixed up */
447         afs_lookup(pvc, dp->d_name.name, &avc, credp);
448         if (!avc || vcp != avc) {       /* bad, very bad.. */
449             afs_Trace4(afs_iclSetp, CM_TRACE_TMP_1S3L, ICL_TYPE_STRING,
450                        "afs_linux_revalidate : bad pointer returned from afs_lookup origvc newvc dentry",
451                        ICL_TYPE_POINTER, vcp, ICL_TYPE_POINTER, avc,
452                        ICL_TYPE_POINTER, dp);
453         }
454         if (avc)
455             AFS_RELE(avc);
456         crfree(credp);
457     }
458     /* if bad parent */
459     return;
460 }
461
462 struct task_struct *rxk_ListenerTask;
463
464 void
465 osi_linux_mask(void)
466 {
467     SIG_LOCK(current);
468     sigfillset(&current->blocked);
469     RECALC_SIGPENDING(current);
470     SIG_UNLOCK(current);
471 }
472
473 void
474 osi_linux_rxkreg(void)
475 {
476     rxk_ListenerTask = current;
477 }