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