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