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