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