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