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