diff -urNp linux-5240/fs/afs/cache.c linux-5250/fs/afs/cache.c --- linux-5240/fs/afs/cache.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/cache.c @@ -0,0 +1,664 @@ +/* cache.c: AFS local cache management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cell.h" +#include "cmservice.h" +#include "fsclient.h" +#include "cache.h" +#include "volume.h" +#include "vnode.h" +#include "internal.h" + +static LIST_HEAD(afs_cache_list); +static DECLARE_MUTEX(afs_cache_list_sem); + +static int afs_cache_read_sig(afs_cache_t *cache); + +/*****************************************************************************/ +/* + * stat a cache device to find its device numbers + */ +static int afs_cache_get_kdev(const char *cachename, kdev_t *_kdev, struct file **_bdfile) +{ + struct nameidata nd; + struct inode *inode; + struct file *bdfile; + int ret; + + /* look up the cache device file */ + if (!cachename) + return -EINVAL; + + ret = path_lookup(cachename,LOOKUP_FOLLOW,&nd); + if (ret) + return ret; + + /* check it's a block device file */ + inode = nd.dentry->d_inode; + ret = -ENOTBLK; + if (!S_ISBLK(inode->i_mode)) { + path_release(&nd); + return ret; + } + + /* open a file for it */ + bdfile = dentry_open(nd.dentry,nd.mnt,O_RDWR); + if (IS_ERR(bdfile)) + return ret; + + *_kdev = inode->i_rdev; + *_bdfile = bdfile; + return 0; +} /* end afs_cache_get_kdev() */ + +/*****************************************************************************/ +/* + * open a cache device + */ +int afs_cache_open(const char *cachename, afs_cache_t **_cache) +{ + struct list_head *_p; + afs_cache_t *cache, *ncache; + kdev_t dev; + int ret = 0; + + _enter("{%s}",cachename); + + BUG(); + + /* pre-allocate a cache record */ + ret = -ENOMEM; + ncache = kmalloc(sizeof(*ncache),GFP_KERNEL); + if (!ncache) { + _leave(" = %d [lookup failed]",ret); + return ret; + } + memset(ncache,0,sizeof(*ncache)); + + atomic_set(&ncache->usage,1); + INIT_LIST_HEAD(&ncache->link); + init_rwsem(&ncache->sem); + + /* lookup the block device */ + ret = afs_cache_get_kdev(cachename,&dev,&ncache->bdfile); + if (ret<0) { + kfree(ncache); + _leave(" = %d [lookup failed]",ret); + return ret; + } + + ncache->dev = dev; + + /* see if we've already got the cache open */ + cache = NULL; + down(&afs_cache_list_sem); + + list_for_each(_p,&afs_cache_list) { + cache = list_entry(_p,afs_cache_t,link); + if (kdev_same(cache->dev,dev)) + goto found; + } + goto not_found; + + /* we already have the cache open */ + found: + kdebug("kAFS re-using cache block dev %s",kdevname(dev)); + filp_close(cache->bdfile,NULL); + kfree(ncache); + ncache = NULL; + afs_get_cache(cache); + goto success; + + /* we don't already have the cache open */ + not_found: + kdebug("kAFS using cache block dev %s",kdevname(dev)); + cache = ncache; + ncache = NULL; + + /* grab a handle to the block device */ + ret = -ENOMEM; + cache->bdev = bdget(kdev_t_to_nr(dev)); + if (!cache->bdev) + goto out; + + /* open the block device node */ + ret = blkdev_get(cache->bdev,FMODE_READ|FMODE_WRITE,0,BDEV_RAW); + if (ret) + goto out; + + /* quick insanity check */ + check_disk_change(cache->dev); + ret = -EACCES; + if (is_read_only(cache->dev)) + goto out; + + /* mark it as mine */ + ret = bd_claim(cache->bdev,cache); + if (ret) + goto out; + + /* check it */ + ret = afs_cache_read_sig(cache); + if (ret<0) + goto out_unclaim; + + list_add_tail(&cache->link,&afs_cache_list); + + success: + *_cache = cache; + up(&afs_cache_list_sem); + _leave(" = 0 (%p{%x})",cache->bdev,kdev_t_to_nr(cache->dev)); + return 0; + + out_unclaim: + bd_release(cache->bdev); + out: + if (cache->bdfile) + filp_close(cache->bdfile,NULL); + if (cache->bdev) { + blkdev_put(cache->bdev,BDEV_RAW); + cache->bdev = NULL; + } + + kfree(cache); + + up(&afs_cache_list_sem); + _leave(" = %d",ret); + return ret; + +} /* end afs_cache_open() */ + +/*****************************************************************************/ +/* + * release a cache device + */ +void afs_put_cache(afs_cache_t *cache) +{ + _enter("%p{u=%d}",cache,atomic_read(&cache->usage)); + + down(&afs_cache_list_sem); + + if (!atomic_dec_and_test(&cache->usage)) + cache = NULL; + else + list_del(&cache->link); + + up(&afs_cache_list_sem); + + /* if that was the last ref, then release the kernel resources */ + if (cache) { + kdebug("kAFS releasing cache block dev %s",kdevname(cache->dev)); + filp_close(cache->bdfile,NULL); + bd_release(cache->bdev); + blkdev_put(cache->bdev,BDEV_RAW); + kfree(cache); + } + + _leave(""); +} /* end afs_put_cache() */ + +/*****************************************************************************/ +/* + * read the cache signature block from the cache device + */ +static int afs_cache_read_sig(afs_cache_t *cache) +{ + struct afs_cache_super_block *csb; + struct buffer_head *bh; + + bh = __bread(cache->bdev,0,PAGE_CACHE_SIZE); + if (!bh) + return -EIO; + + csb = (struct afs_cache_super_block*) bh->b_data; + + /* validate the cache superblock */ + if (memcmp(csb->magic,AFS_CACHE_SUPER_MAGIC,sizeof(csb->magic))!=0) { + printk("kAFS cache magic string doesn't match\n"); + return -EINVAL; + } + if (csb->endian!=AFS_CACHE_SUPER_ENDIAN) { + printk("kAFS endian spec doesn't match (%hx not %hx)\n", + csb->endian,AFS_CACHE_SUPER_ENDIAN); + return -EINVAL; + } + if (csb->version!=AFS_CACHE_SUPER_VERSION) { + printk("kAFS version doesn't match (%u not %u)\n", + csb->version,AFS_CACHE_SUPER_VERSION); + return -EINVAL; + } + + /* copy the layout into the cache management structure */ + memcpy(&cache->layout,csb,sizeof(cache->layout)); + + brelse(bh); + return 0; +} /* end afs_cache_read_sig() */ + +/*****************************************************************************/ +/* + * update part of one page in the cache + * - the caller must hold any required protective locks + * - based on rw_swap_page_base() + */ +static int afs_cache_update_region(afs_cache_t *cache, afs_cache_bix_t bix, + unsigned off, size_t size, void *buf) +{ + mm_segment_t oldfs; + loff_t pos; + int ret; + + _enter("%s,%u,%u,%u,",kdevname(cache->dev),bix,off,size); + + pos = bix*cache->layout.bsize + off; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = generic_file_write(cache->bdfile,buf,size,&pos); + set_fs(oldfs); + + if (ret>0) + ret = 0; + + _leave(" = %d",ret); + return ret; +} /* end afs_cache_update_region() */ + +/*****************************************************************************/ +/* + * look up cell information in the cache + * - mkafscache preloads /etc/sysconfig/kafs/cell-serv-db into the cache + */ +int afs_cache_lookup_cell(afs_cache_t *cache, + afs_cell_t *cell) +{ + struct afs_cache_cell_block *ccells; + struct afs_cache_cell *ccell; + struct buffer_head *bh; + afs_cache_cellix_t cix, stop, rem; + afs_cache_bix_t bix; + int loop; + + _enter("%s,%s",kdevname(cache->dev),cell->name); + + BUG(); + + rem = cache->layout.ncells; + + for (bix=cache->layout.off_cell_cache; bixlayout.off_volume_bitmap; bix++) { + /* read the next block */ + bh = __bread(cache->bdev,bix,PAGE_CACHE_SIZE); + if (!bh) { + kleave(" = -EIO (block %u)",bix); + return -EIO; + } + + ccells = (struct afs_cache_cell_block*) bh->b_data; + + /* and scan it */ + stop = min((size_t)rem, + sizeof(struct afs_cache_cell_block)/sizeof(struct afs_cache_cell)); + rem -= stop; + + for (cix=0; cixentries[cix]; + if (strncmp(cell->name,ccell->name,sizeof(ccell->name))==0) + goto found; + } + + brelse(bh); + } + + _leave(" = -ENOENT"); + return -ENOENT; + + found: + /* found the cell record - copy out the details */ + bix -= cache->layout.off_cell_cache; + cell->cache_ix = cix; + cell->cache_ix += bix * sizeof(struct afs_cache_cell_block)/sizeof(struct afs_cache_cell); + + memcpy(cell->vl_addrs,ccell->servers,sizeof(cell->vl_addrs)); + + for (loop=0; loopvl_addrs)/sizeof(cell->vl_addrs[0]); loop++) + if (!cell->vl_addrs[loop].s_addr) + break; + cell->vl_naddrs = loop; + + brelse(bh); + _leave(" = 0 (bix=%u cix=%u ccix=%u)",bix,cix,cell->cache_ix); + return 0; + +} /* end afs_cache_lookup_cell() */ + +/*****************************************************************************/ +/* + * search for a volume location record in the cache + */ +int afs_cache_lookup_vlocation(afs_vlocation_t *vlocation) +{ + BUG(); + +#if 0 + struct afs_cache_volume_block *cvols; + struct afs_cache_volume *cvol; + struct buffer_head *bh; + afs_cache_bix_t bix; + unsigned rem, stop, ix; + + _enter("%s,{v=%s cix=%u}", + kdevname(vlocation->cache->dev),vlocation->vldb.name,vlocation->vldb.cell_ix); + + rem = vlocation->cache->layout.nvols; + + for (bix=vlocation->cache->layout.off_volume_cache; + bixcache->layout.off_vnode_bitmap; + bix++ + ) { + /* read the next block */ + bh = __bread(vlocation->cache->bdev,bix,PAGE_CACHE_SIZE); + if (!bh) { + kleave(" = -EIO (block %u)",bix); + return -EIO; + } + + cvols = (struct afs_cache_volume_block*) bh->b_data; + + /* and scan it */ + stop = min((size_t)rem,sizeof(*cvols)/sizeof(*cvol)); + rem -= stop; + + for (ix=0; ixentries[ix]; + if (cvol->name[0]) + _debug("FOUND[%u.%u]: cell %u vol '%s' %08x", + bix,ix,cvol->cell_ix,cvol->name,cvol->vid[0]); + if (cvol->cell_ix==vlocation->vldb.cell_ix && + memcmp(vlocation->vldb.name,cvol->name,sizeof(cvol->name))==0) { + goto found; + } + } + + brelse(bh); + } + + _leave(" = %d",-ENOENT); + return -ENOENT; + + found: + /* found the cell record */ + memcpy(&vlocation->vldb,cvol,sizeof(*cvol)); + brelse(bh); + + /* note the volume ID */ + bix -= vlocation->cache->layout.off_volume_cache; + vlocation->vix.index = (ix + bix * (sizeof(*cvols)/sizeof(*cvol))) << 2; + + _leave(" = 0 (bix=%u ix=%u vix=%hu)",bix,ix,vlocation->vix.index); +#endif + return 0; + +} /* end afs_cache_lookup_vlocation() */ + +/*****************************************************************************/ +/* + * search for a volume location record in the cache, and if one's not available then reap the + * eldest not currently in use + */ +int afs_cache_update_vlocation(afs_vlocation_t *vlocation) +{ + BUG(); + +#if 0 + struct afs_cache_volume_block *cvols; + struct afs_cache_volume *cvol; + struct buffer_head *bh; + afs_cache_bix_t bix; + unsigned rem, stop, ix, candidate, tmp; + time_t cand_age; + int ret; + + + _enter("%s,{v=%s cix=%u}", + kdevname(vlocation->cache->dev),vlocation->vldb.name,vlocation->vldb.cell_ix); + + candidate = UINT_MAX; + cand_age = ULONG_MAX; + rem = vlocation->cache->layout.nvols; + + for (bix=vlocation->cache->layout.off_volume_cache; + bixcache->layout.off_vnode_bitmap; + bix++ + ) { + /* read the next block */ + bh = __bread(vlocation->cache->bdev,bix,PAGE_CACHE_SIZE); + if (!bh) { + kleave(" = -EIO (block %u)",bix); + return -EIO; + } + + cvols = (struct afs_cache_volume_block*) bh->b_data; + + /* and scan it */ + stop = min((size_t)rem,sizeof(*cvols)/sizeof(*cvol)); + rem -= stop; + + for (ix=0; ixentries[ix]; + if (cvol->name[0]) + _debug("FOUND[%u.%u]: cell %u vol '%s' %08x", + bix,ix,cvol->cell_ix,cvol->name,cvol->vid[0]); + if (cvol->cell_ix==vlocation->vldb.cell_ix && + memcmp(vlocation->vldb.name,cvol->name,sizeof(cvol->name))==0) { + goto found; + } + + if (candidate!=UINT_MAX && cvol->ctimectime; + candidate = bix - vlocation->cache->layout.off_volume_cache; + candidate = ix + candidate * sizeof(*cvols)/sizeof(*cvol); + } + } + + brelse(bh); + } + + /* TODO: recycle old entry if no spare slots available */ + if (vlocation->cache->layout.nvols>=vlocation->cache->layout.maxvols) + BUG(); + + /* insert new entry */ + ix = vlocation->vix.index = vlocation->cache->layout.nvols++; + tmp = (sizeof(*cvols)/sizeof(*cvol)); + bix = ix / tmp + vlocation->cache->layout.off_volume_cache; + ix %= tmp; + + kdebug("INSERT (bix=%u ix=%u)",bix,ix); + ret = afs_cache_update_region(vlocation->cache, + bix, + ix*sizeof(*cvol), + sizeof(*cvol), + &vlocation->vldb); + if (ret<0) + goto out; + + /* update the superblock */ + ret = afs_cache_update_region(vlocation->cache, + 0,0, + sizeof(vlocation->cache->layout), + &vlocation->cache->layout); + + /* TODO: handle failure by winding back cache->layout.nvols */ + + out: + _leave(" = %d (bix=%u ix=%u vix=%hu)",ret,bix,ix,vlocation->vix.index); + return ret; + + found: + brelse(bh); + + /* update the on-disk cache with the latest news */ + _debug("UPDATE (bix=%u ix=%u)",bix,ix); + ret = afs_cache_update_region(vlocation->cache, + bix, + ix*sizeof(*cvol), + sizeof(*cvol), + &vlocation->vldb); + if (ret<0) + goto out; + + /* found the cell record - note the volume ID */ + bix -= vlocation->cache->layout.off_volume_cache; + vlocation->vix.index = (ix + bix * (sizeof(*cvols)/sizeof(*cvol))) << 2; + + _leave(" = 0 (bix=%u ix=%u vix=%hu)",bix,ix,vlocation->vix.index); +#endif + return 0; + +} /* end afs_cache_update_vlocation() */ + +/*****************************************************************************/ +/* + * search for a vnode record in the cache, and if one's not available then reap the + * eldest not currently in use + */ +int afs_cache_lookup_vnode(afs_volume_t *volume, afs_vnode_t *vnode) +{ + BUG(); + +#if 0 + struct afs_cache_vnode_index_block *cindexb; + struct afs_cache_vnode_index cindex; + struct buffer_head *bh; + afs_cache_bix_t bix; + unsigned rem, stop, ix, candidate, tmp; + time_t cand_age; + int ret; + + _enter("{cix=%u vix=%u},{%u,%u,%u}", + volume->cix,volume->vix.index,vnode->fid.vid,vnode->fid.vnode,vnode->fid.unique); + + candidate = UINT_MAX; + cand_age = ULONG_MAX; + rem = volume->cache->layout.nvnodes; + + for (bix=volume->cache->layout.off_vnode_index; + bixcache->layout.off_vnode_cache; + bix++ + ) { + /* read the next block */ + bh = __bread(volume->cache->bdev,bix,PAGE_CACHE_SIZE); + if (!bh) { + kleave(" = -EIO (block %u)",bix); + return -EIO; + } + + cindexb = (struct afs_cache_vnode_index_block*) bh->b_data; + + /* and scan it */ + stop = min((size_t)rem,AFS_CACHE_VNODE_INDEX_PER_BLOCK); + rem -= stop; + + for (ix=0; ixindex[ix],sizeof(cindex)); + +#if 0 + if (cindex.vnode>0) + kdebug("FOUND[%u.%u]: vix %u vnode %u", + bix,ix,cindex.volume_ix.index,cindex.vnode); +#endif + + if (cindex.vnode==vnode->fid.vnode && + cindex.volume_ix.index==volume->vix.index) + goto found; + + if (candidate!=UINT_MAX && cindex.atimecache->layout.off_vnode_index; + candidate = ix + candidate * AFS_CACHE_VNODE_INDEX_PER_BLOCK; + } + } + + brelse(bh); + } + + /* TODO: recycle old entry if no spare slots available */ + if (volume->cache->layout.nvnodes>=volume->cache->layout.maxvnodes) + BUG(); + + /* append new entry */ + vnode->nix = volume->cache->layout.nvnodes++; + + cindex.vnode = vnode->fid.vnode; + cindex.atime = xtime.tv_sec; + cindex.volume_ix = volume->vix; + + ix = vnode->nix; + tmp = AFS_CACHE_VNODE_INDEX_PER_BLOCK; + bix = ix / tmp + volume->cache->layout.off_vnode_index; + ix %= tmp; + + _debug("CACHE APPEND VNODE %u (bix=%u ix=%u)",vnode->nix,bix,ix); + ret = afs_cache_update_region(volume->cache, + bix, + ix*sizeof(cindex), + sizeof(cindex), + &cindex); + if (ret<0) + goto out; + + /* update the superblock */ + ret = afs_cache_update_region(volume->cache, + 0,0, + sizeof(volume->cache->layout), + &volume->cache->layout); + + /* TODO: handle failure by winding back cache->layout.nvnodes */ + + out: + _leave(" = %d (bix=%u ix=%u nix=%u)",ret,bix,ix,vnode->nix); + return ret; + + found: + brelse(bh); + + cindex.atime = xtime.tv_sec; + + /* update the on-disk cache with the latest news */ + _debug("UPDATE (bix=%u ix=%u)",bix,ix); + ret = afs_cache_update_region(volume->cache, + bix, + ix*sizeof(cindex), + sizeof(cindex), + &cindex); + if (ret<0) + goto out; + + /* found the cell record - note the volume ID */ + bix -= volume->cache->layout.off_vnode_index; + vnode->nix = ix + bix * AFS_CACHE_VNODE_INDEX_PER_BLOCK; + + _leave(" = 0 (bix=%u ix=%u nix=%u)",bix,ix,vnode->nix); +#endif + return 0; + +} /* end afs_cache_lookup_vnode() */ diff -urNp linux-5240/fs/afs/cache.h linux-5250/fs/afs/cache.h --- linux-5240/fs/afs/cache.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/cache.h @@ -0,0 +1,48 @@ +/* cache.h: AFS local cache management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_CACHE_H +#define _LINUX_AFS_CACHE_H + +#include +#include "cache-layout.h" + +#ifdef __KERNEL__ + +/*****************************************************************************/ +/* + * AFS cache management record + */ +struct afs_cache +{ + atomic_t usage; /* usage count */ + struct list_head link; /* link in cache list */ + kdev_t dev; /* device numbers */ + struct block_device *bdev; /* block device */ + struct file *bdfile; /* file attached to block device */ + struct rw_semaphore sem; /* access semaphore */ + struct afs_cache_super_block layout; /* layout description */ +}; + +extern int afs_cache_open(const char *name, afs_cache_t **_cache); + +#define afs_get_cache(C) do { atomic_inc(&(C)->usage); } while(0) + +extern void afs_put_cache(afs_cache_t *cache); + +extern int afs_cache_lookup_cell(afs_cache_t *cache, afs_cell_t *cell); +extern int afs_cache_lookup_vlocation(afs_vlocation_t *vlocation); +extern int afs_cache_update_vlocation(afs_vlocation_t *vlocation); +extern int afs_cache_lookup_vnode(afs_volume_t *volume, afs_vnode_t *vnode); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_AFS_CACHE_H */ diff -urNp linux-5240/fs/afs/cache-layout.h linux-5250/fs/afs/cache-layout.h --- linux-5240/fs/afs/cache-layout.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/cache-layout.h @@ -0,0 +1,231 @@ +/* cache-layout.h: AFS cache layout + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * + * The cache is stored on a block device and is laid out as follows: + * + * 0 +------------------------------------------------ + * | + * | SuperBlock + * | + * 1 +------------------------------------------------ + * | + * | Cell Cache (preloaded by mkafscache) + * | + * +------------------------------------------------ + * | + * | Volume Cache Allocation BitMap (1 page) + * | + * +------------------------------------------------ + * | + * | Volume Cache + * | + * +------------------------------------------------ + * | + * | Vnode Cache Allocation BitMap + * | + * +------------------------------------------------ + * | + * | Vnode Cache Index + * | + * +------------------------------------------------ + * | + * | Vnode Cache + * | + * +------------------------------------------------ + * | + * | Data Cache Allocation BitMap + * | + * +------------------------------------------------ + * | + * | Data Cache + * | + * End +------------------------------------------------ + * + */ + +#ifndef _LINUX_AFS_CACHE_LAYOUT_H +#define _LINUX_AFS_CACHE_LAYOUT_H + +#include "types.h" + +typedef unsigned afs_cache_bix_t; +typedef unsigned short afs_cache_cellix_t; + +typedef struct { unsigned short index; } afs_cache_volix_t; + +/*****************************************************************************/ +/* + * cache superblock block layout + */ +struct afs_cache_super_block +{ + char magic[10]; /* magic number */ +#define AFS_CACHE_SUPER_MAGIC "kafscache" + + unsigned short endian; /* 0x1234 stored CPU-normal order */ +#define AFS_CACHE_SUPER_ENDIAN 0x1234 + + unsigned version; /* format version */ +#define AFS_CACHE_SUPER_VERSION 1 + + /* accounting */ + afs_cache_cellix_t ncells; /* number of cells cached */ + afs_cache_cellix_t maxcells; /* max number of cells cacheable */ + afs_cache_cellix_t thiscell; /* index of this cell in cache */ + unsigned short nvols; /* volume cache usage */ + unsigned short maxvols; /* maximum number of volumes cacheable */ + unsigned nvnodes; /* vnode cache usage */ + unsigned maxvnodes; /* maximum number of vnodes cacheable */ + + /* layout */ + unsigned bsize; /* cache block size */ + afs_cache_bix_t off_cell_cache; /* block offset of cell cache */ + afs_cache_bix_t off_volume_bitmap; /* block offset of volume alloc bitmap */ + afs_cache_bix_t off_volume_cache; /* block offset of volume cache */ + afs_cache_bix_t off_vnode_bitmap; /* block offset of vnode alloc bitmap */ + afs_cache_bix_t off_vnode_index; /* block offset of vnode index */ + afs_cache_bix_t off_vnode_cache; /* block offset of vnode cache */ + afs_cache_bix_t off_data_bitmap; /* block offset of data bitmap */ + afs_cache_bix_t off_data_cache; /* block offset of data cache */ + afs_cache_bix_t off_end; /* block offset of end of cache */ +}; + +/*****************************************************************************/ +/* + * cached cell info + */ +struct afs_cache_cell +{ + char name[64]; /* cell name (padded with NULs) */ + struct in_addr servers[16]; /* cached cell servers */ +}; + +struct afs_cache_cell_block +{ + struct afs_cache_cell entries[PAGE_SIZE/sizeof(struct afs_cache_cell)]; +}; + +/*****************************************************************************/ +/* + * cached volume info + * - indexed by (afs_cache_volix_t/4) + * - (afs_cache_volix_t%4) is 0 for R/W, 1 for R/O and 2 for Bak (3 is not used) + */ +struct afs_cache_volume +{ + char name[64]; /* volume name (padded with NULs) */ + afs_volid_t vid[3]; /* volume IDs for R/W, R/O and Bak volumes */ + unsigned char vidmask; /* voltype mask for vid[] */ + unsigned char _pad[1]; + unsigned short nservers; /* number of entries used in servers[] */ + struct in_addr servers[8]; /* fileserver addresses */ + unsigned char srvtmask[8]; /* voltype masks for servers[] */ +#define AFS_CACHE_VOL_STM_RW 0x01 /* server holds a R/W version of the volume */ +#define AFS_CACHE_VOL_STM_RO 0x02 /* server holds a R/O version of the volume */ +#define AFS_CACHE_VOL_STM_BAK 0x04 /* server holds a backup version of the volume */ + + afs_cache_cellix_t cell_ix; /* cell cache index (MAX_UINT if unused) */ + time_t ctime; /* time at which cached */ +}; + +struct afs_cache_volume_block +{ + struct afs_cache_volume entries[PAGE_SIZE/sizeof(struct afs_cache_volume)]; +}; + +/*****************************************************************************/ +/* + * cached vnode index + * - map on a 1:1 basis with the vnode index table + */ +struct afs_cache_vnode_index +{ + afs_vnodeid_t vnode; /* vnode ID */ + time_t atime; /* last time accessed */ + afs_cache_volix_t volume_ix; /* volume cache index */ +} __attribute__((packed)); + +#define AFS_CACHE_VNODE_INDEX_PER_BLOCK ((size_t)(PAGE_SIZE/sizeof(struct afs_cache_vnode_index))) + +struct afs_cache_vnode_index_block +{ + struct afs_cache_vnode_index index[AFS_CACHE_VNODE_INDEX_PER_BLOCK]; +}; + +/*****************************************************************************/ +/* + * cached vnode rights entry + */ +struct afs_cache_rights +{ + uid_t uid; + unsigned access; + unsigned short mode; +} __attribute__((packed)); + +/*****************************************************************************/ +/* + * vnode (inode) metadata cache + * - PAGE_SIZE in size + */ +struct afs_cache_vnode_block +{ + /* file ID */ + unsigned unique; /* FID unique */ + + /* file status */ + afs_file_type_t type; /* file type */ + unsigned nlink; /* link count */ + size_t size; /* file size */ + afs_dataversion_t version; /* current data version */ + unsigned author; /* author ID */ + unsigned owner; /* owner ID */ + unsigned anon_access; /* access rights for unauthenticated caller */ + unsigned short mode; /* UNIX mode */ + time_t mtime; /* last time server changed data */ + time_t cachetime; /* time at which cached */ + + /* file contents */ + afs_cache_bix_t pt0_bix; /* "page table 0" block index */ + afs_cache_bix_t pgd_bix; /* "page directory" block index */ + + /* access rights */ + size_t nrights; /* number of cached rights */ + struct afs_cache_rights rights[0]; /* cached access rights buffer */ +}; + +#define AFS_CACHE_VNODE_MAXRIGHTS \ + ((PAGE_SIZE - sizeof(struct afs_cache_vnode_block)) / sizeof(struct afs_cache_rights)) + +/*****************************************************************************/ +/* + * vnode data "page directory" block + * - first 1024 pages don't map through here + * - PAGE_SIZE in size + */ +struct afs_cache_pgd_block +{ + unsigned _unused; + afs_cache_bix_t pt_bix[1023]; /* "page table" block indices */ +}; + +/*****************************************************************************/ +/* + * vnode data "page table" block + * - PAGE_SIZE in size + */ +struct afs_cache_pt_block +{ + afs_cache_bix_t page_bix[1024]; /* "page" block indices */ +}; + + +#endif /* _LINUX_AFS_CACHE_LAYOUT_H */ diff -urNp linux-5240/fs/afs/callback.c linux-5250/fs/afs/callback.c --- linux-5240/fs/afs/callback.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/callback.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2002 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: David Woodhouse + * David Howells + * + */ + +#include +#include +#include +#include "server.h" +#include "vnode.h" +#include "internal.h" + +/*****************************************************************************/ +/* + * allow the fileserver to request callback state (re-)initialisation + */ +int SRXAFSCM_InitCallBackState(afs_server_t *server) +{ + struct list_head callbacks; + + _enter("%p",server); + + INIT_LIST_HEAD(&callbacks); + + /* transfer the callback list from the server to a temp holding area */ + spin_lock(&server->cb_lock); + + list_add(&callbacks,&server->cb_promises); + list_del_init(&server->cb_promises); + + /* munch our way through the list, grabbing the inode, dropping all the locks and regetting + * them in the right order + */ + while (!list_empty(&callbacks)) { + struct inode *inode; + afs_vnode_t *vnode; + + vnode = list_entry(callbacks.next,afs_vnode_t,cb_link); + list_del_init(&vnode->cb_link); + + /* try and grab the inode - may fail */ + inode = igrab(AFS_VNODE_TO_I(vnode)); + if (inode) { + int release = 0; + + spin_unlock(&server->cb_lock); + spin_lock(&vnode->lock); + + if (cmpxchg(&vnode->cb_server,server,NULL)==server) { + afs_kafstimod_del_timer(&vnode->cb_timeout); + spin_lock(&afs_cb_hash_lock); + list_del_init(&vnode->cb_hash_link); + spin_unlock(&afs_cb_hash_lock); + release = 1; + } + + spin_unlock(&vnode->lock); + + iput(inode); + if (release) afs_put_server(server); + + spin_lock(&server->cb_lock); + } + } + + spin_unlock(&server->cb_lock); + + _leave(" = 0"); + return 0; +} /* end SRXAFSCM_InitCallBackState() */ + +/*****************************************************************************/ +/* + * allow the fileserver to break callback promises + */ +int SRXAFSCM_CallBack(afs_server_t *server, size_t count, afs_callback_t callbacks[]) +{ + struct list_head *_p; + + _enter("%p,%u,",server,count); + + for (; count>0; callbacks++, count--) { + struct inode *inode = NULL; + afs_vnode_t *vnode = NULL; + int valid = 0; + + _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }", + callbacks->fid.vid, + callbacks->fid.vnode, + callbacks->fid.unique, + callbacks->version, + callbacks->expiry, + callbacks->type + ); + + /* find the inode for this fid */ + spin_lock(&afs_cb_hash_lock); + + list_for_each(_p,&afs_cb_hash(server,&callbacks->fid)) { + vnode = list_entry(_p,afs_vnode_t,cb_hash_link); + + if (memcmp(&vnode->fid,&callbacks->fid,sizeof(afs_fid_t))!=0) + continue; + + /* right vnode, but is it same server? */ + if (vnode->cb_server!=server) + break; /* no */ + + /* try and nail the inode down */ + inode = igrab(AFS_VNODE_TO_I(vnode)); + break; + } + + spin_unlock(&afs_cb_hash_lock); + + if (inode) { + /* we've found the record for this vnode */ + spin_lock(&vnode->lock); + if (cmpxchg(&vnode->cb_server,server,NULL)==server) { + /* the callback _is_ on the calling server */ + valid = 1; + + afs_kafstimod_del_timer(&vnode->cb_timeout); + vnode->flags |= AFS_VNODE_CHANGED; + + spin_lock(&server->cb_lock); + list_del_init(&vnode->cb_link); + spin_unlock(&server->cb_lock); + + spin_lock(&afs_cb_hash_lock); + list_del_init(&vnode->cb_hash_link); + spin_unlock(&afs_cb_hash_lock); + } + spin_unlock(&vnode->lock); + + if (valid) { + invalidate_inode_pages(inode); + afs_put_server(server); + } + iput(inode); + } + } + + _leave(" = 0"); + return 0; +} /* end SRXAFSCM_CallBack() */ + +/*****************************************************************************/ +/* + * allow the fileserver to see if the cache manager is still alive + */ +int SRXAFSCM_Probe(afs_server_t *server) +{ + _debug("SRXAFSCM_Probe(%p)\n",server); + return 0; +} /* end SRXAFSCM_Probe() */ diff -urNp linux-5240/fs/afs/cell.c linux-5250/fs/afs/cell.c --- linux-5240/fs/afs/cell.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/cell.c @@ -0,0 +1,452 @@ +/* cell.c: AFS cell and server record management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "volume.h" +#include "cell.h" +#include "server.h" +#include "transport.h" +#include "cache.h" +#include "vlclient.h" +#include "kafstimod.h" +#include "super.h" +#include "internal.h" + +DECLARE_RWSEM(afs_proc_cells_sem); +LIST_HEAD(afs_proc_cells); + +static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells); +static rwlock_t afs_cells_lock = RW_LOCK_UNLOCKED; +static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */ +static afs_cell_t *afs_cell_root; + +static char *rootcell; + +MODULE_PARM(rootcell,"s"); +MODULE_PARM_DESC(rootcell,"root AFS cell name and VL server IP addr list"); + +/*****************************************************************************/ +/* + * create a cell record + * - "name" is the name of the cell + * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format + */ +int afs_cell_create(const char *name, char *vllist, afs_cell_t **_cell) +{ + afs_cell_t *cell; + char *next; + int ret; + + _enter("%s",name); + + if (!name) BUG(); /* TODO: want to look up "this cell" in the cache */ + + down_write(&afs_cells_sem); + + /* allocate and initialise a cell record */ + cell = kmalloc(sizeof(afs_cell_t) + strlen(name) + 1,GFP_KERNEL); + if (!cell) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(cell,0,sizeof(afs_cell_t)); + atomic_set(&cell->usage,0); + + INIT_LIST_HEAD(&cell->link); + + rwlock_init(&cell->sv_lock); + INIT_LIST_HEAD(&cell->sv_list); + INIT_LIST_HEAD(&cell->sv_graveyard); + spin_lock_init(&cell->sv_gylock); + + init_rwsem(&cell->vl_sem); + INIT_LIST_HEAD(&cell->vl_list); + INIT_LIST_HEAD(&cell->vl_graveyard); + spin_lock_init(&cell->vl_gylock); + + strcpy(cell->name,name); + + /* fill in the VL server list from the rest of the string */ + ret = -EINVAL; + do { + unsigned a, b, c, d; + + next = strchr(vllist,':'); + if (next) *next++ = 0; + + if (sscanf(vllist,"%u.%u.%u.%u",&a,&b,&c,&d)!=4) + goto badaddr; + + if (a>255 || b>255 || c>255 || d>255) + goto badaddr; + + cell->vl_addrs[cell->vl_naddrs++].s_addr = + htonl((a<<24)|(b<<16)|(c<<8)|d); + + if (cell->vl_naddrs>=16) + break; + + } while(vllist=next, vllist); + + /* add a proc dir for this cell */ + ret = afs_proc_cell_setup(cell); + if (ret<0) + goto error; + + /* add to the cell lists */ + write_lock(&afs_cells_lock); + list_add_tail(&cell->link,&afs_cells); + write_unlock(&afs_cells_lock); + + down_write(&afs_proc_cells_sem); + list_add_tail(&cell->proc_link,&afs_proc_cells); + up_write(&afs_proc_cells_sem); + + *_cell = cell; + up_write(&afs_cells_sem); + + _leave(" = 0 (%p)",cell); + return 0; + + badaddr: + printk("kAFS: bad VL server IP address: '%s'\n",vllist); + error: + up_write(&afs_cells_sem); + kfree(afs_cell_root); + return ret; +} /* end afs_cell_create() */ + +/*****************************************************************************/ +/* + * initialise the cell database from module parameters + */ +int afs_cell_init(void) +{ + char *cp; + int ret; + + _enter(""); + + if (!rootcell) { + printk("kAFS: no root cell specified\n"); + return -EINVAL; + } + + cp = strchr(rootcell,':'); + if (!cp) { + printk("kAFS: no VL server IP addresses specified\n"); + return -EINVAL; + } + + /* allocate a cell record for the root cell */ + *cp++ = 0; + ret = afs_cell_create(rootcell,cp,&afs_cell_root); + if (ret==0) + afs_get_cell(afs_cell_root); + + _leave(" = %d",ret); + return ret; + +} /* end afs_cell_init() */ + +/*****************************************************************************/ +/* + * lookup a cell record + */ +int afs_cell_lookup(afs_cache_t *cache, const char *name, afs_cell_t **_cell) +{ + struct list_head *_p; + afs_cell_t *cell; + + _enter("\"%s\",",name?name:"*thiscell*"); + + cell = afs_cell_root; + + if (name) { + /* if the cell was named, look for it in the cell record list */ + cell = NULL; + read_lock(&afs_cells_lock); + + list_for_each(_p,&afs_cells) { + cell = list_entry(_p,afs_cell_t,link); + if (strcmp(cell->name,name)==0) + break; + cell = NULL; + } + + read_unlock(&afs_cells_lock); + } + + if (cell) + afs_get_cell(cell); + + *_cell = cell; + _leave(" = %d (%p)",cell?0:-ENOENT,cell); + return cell ? 0 : -ENOENT; + +} /* end afs_cell_lookup() */ + +/*****************************************************************************/ +/* + * try and get a cell record + */ +afs_cell_t *afs_get_cell_maybe(afs_cell_t **_cell) +{ + afs_cell_t *cell; + + write_lock(&afs_cells_lock); + + cell = *_cell; + if (cell && !list_empty(&cell->link)) + atomic_inc(&cell->usage); + else + cell = NULL; + + write_unlock(&afs_cells_lock); + + return cell; +} /* end afs_get_cell_maybe() */ + +/*****************************************************************************/ +/* + * destroy a cell record + */ +void afs_put_cell(afs_cell_t *cell) +{ + _enter("%p{%d,%s}",cell,atomic_read(&cell->usage),cell->name); + + /* sanity check */ + if (atomic_read(&cell->usage)<=0) + BUG(); + + /* to prevent a race, the decrement and the dequeue must be effectively atomic */ + write_lock(&afs_cells_lock); + + if (likely(!atomic_dec_and_test(&cell->usage))) { + write_unlock(&afs_cells_lock); + _leave(""); + return; + } + + write_unlock(&afs_cells_lock); + + if (!list_empty(&cell->sv_list)) BUG(); + if (!list_empty(&cell->sv_graveyard)) BUG(); + if (!list_empty(&cell->vl_list)) BUG(); + if (!list_empty(&cell->vl_graveyard)) BUG(); + + _leave(" [unused]"); +} /* end afs_put_cell() */ + +/*****************************************************************************/ +/* + * destroy a cell record + */ +static void afs_cell_destroy(afs_cell_t *cell) +{ + _enter("%p{%d,%s}",cell,atomic_read(&cell->usage),cell->name); + + /* to prevent a race, the decrement and the dequeue must be effectively atomic */ + write_lock(&afs_cells_lock); + + /* sanity check */ + if (atomic_read(&cell->usage)!=0) + BUG(); + + list_del_init(&cell->link); + + write_unlock(&afs_cells_lock); + + down_write(&afs_cells_sem); + + afs_proc_cell_remove(cell); + + down_write(&afs_proc_cells_sem); + list_del_init(&afs_cell_root->proc_link); + up_write(&afs_proc_cells_sem); + + up_write(&afs_cells_sem); + + if (!list_empty(&cell->sv_list)) BUG(); + if (!list_empty(&cell->sv_graveyard)) BUG(); + if (!list_empty(&cell->vl_list)) BUG(); + if (!list_empty(&cell->vl_graveyard)) BUG(); + + /* finish cleaning up the cell */ + kfree(cell); + + _leave(" [destroyed]"); +} /* end afs_cell_destroy() */ + +/*****************************************************************************/ +/* + * lookup the server record corresponding to an Rx RPC peer + */ +int afs_server_find_by_peer(const struct rxrpc_peer *peer, afs_server_t **_server) +{ + struct list_head *_pc, *_ps; + afs_server_t *server; + afs_cell_t *cell; + + _enter("%p{a=%08x},",peer,ntohl(peer->addr.s_addr)); + + /* search the cell list */ + read_lock(&afs_cells_lock); + + list_for_each(_pc,&afs_cells) { + cell = list_entry(_pc,afs_cell_t,link); + + _debug("? cell %s",cell->name); + + write_lock(&cell->sv_lock); + + /* check the active list */ + list_for_each(_ps,&cell->sv_list) { + server = list_entry(_ps,afs_server_t,link); + + _debug("?? server %08x",ntohl(server->addr.s_addr)); + + if (memcmp(&server->addr,&peer->addr,sizeof(struct in_addr))==0) + goto found_server; + } + + /* check the inactive list */ + spin_lock(&cell->sv_gylock); + list_for_each(_ps,&cell->sv_graveyard) { + server = list_entry(_ps,afs_server_t,link); + + _debug("?? dead server %08x",ntohl(server->addr.s_addr)); + + if (memcmp(&server->addr,&peer->addr,sizeof(struct in_addr))==0) + goto found_dead_server; + } + spin_unlock(&cell->sv_gylock); + + write_unlock(&cell->sv_lock); + } + read_unlock(&afs_cells_lock); + + _leave(" = -ENOENT"); + return -ENOENT; + + /* we found it in the graveyard - resurrect it */ + found_dead_server: + list_del(&server->link); + list_add_tail(&server->link,&cell->sv_list); + afs_get_server(server); + afs_kafstimod_del_timer(&server->timeout); + spin_unlock(&cell->sv_gylock); + goto success; + + /* we found it - increment its ref count and return it */ + found_server: + afs_get_server(server); + + success: + write_unlock(&cell->sv_lock); + read_unlock(&afs_cells_lock); + + *_server = server; + _leave(" = 0 (s=%p c=%p)",server,cell); + return 0; + +} /* end afs_server_find_by_peer() */ + +/*****************************************************************************/ +/* + * purge in-memory cell database on module unload + * - the timeout daemon is stopped before calling this + */ +void afs_cell_purge(void) +{ + afs_vlocation_t *vlocation; + afs_cell_t *cell; + + _enter(""); + + if (afs_cell_root) + afs_put_cell(afs_cell_root); + + while (!list_empty(&afs_cells)) { + cell = NULL; + + /* remove the next cell from the front of the list */ + write_lock(&afs_cells_lock); + + if (!list_empty(&afs_cells)) { + cell = list_entry(afs_cells.next,afs_cell_t,link); + list_del_init(&cell->link); + } + + write_unlock(&afs_cells_lock); + + if (cell) { + _debug("PURGING CELL %s (%d)",cell->name,atomic_read(&cell->usage)); + + if (!list_empty(&cell->sv_list)) BUG(); + if (!list_empty(&cell->vl_list)) BUG(); + + /* purge the cell's VL graveyard list */ + _debug(" - clearing VL graveyard"); + + spin_lock(&cell->vl_gylock); + + while (!list_empty(&cell->vl_graveyard)) { + vlocation = list_entry(cell->vl_graveyard.next, + afs_vlocation_t,link); + list_del_init(&vlocation->link); + + afs_kafstimod_del_timer(&vlocation->timeout); + + spin_unlock(&cell->vl_gylock); + + afs_vlocation_do_timeout(vlocation); +#warning race if move to use krxtimod instead of kafstimod + + spin_lock(&cell->vl_gylock); + } + + spin_unlock(&cell->vl_gylock); + + /* purge the cell's server graveyard list */ + _debug(" - clearing server graveyard"); + + spin_lock(&cell->sv_gylock); + + while (!list_empty(&cell->sv_graveyard)) { + afs_server_t *server; + + server = list_entry(cell->sv_graveyard.next,afs_server_t,link); + list_del_init(&server->link); + + afs_kafstimod_del_timer(&server->timeout); + + spin_unlock(&cell->sv_gylock); + + afs_server_do_timeout(server); + + spin_lock(&cell->sv_gylock); + } + + spin_unlock(&cell->sv_gylock); + + /* now the cell should be left with no references */ + afs_cell_destroy(cell); + } + } + + _leave(""); +} /* end afs_cell_purge() */ diff -urNp linux-5240/fs/afs/cell.h linux-5250/fs/afs/cell.h --- linux-5240/fs/afs/cell.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/cell.h @@ -0,0 +1,64 @@ +/* cell.h: AFS cell record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_CELL_H +#define _LINUX_AFS_CELL_H + +#include "types.h" +#include "cache-layout.h" + +extern volatile int afs_cells_being_purged; /* T when cells are being purged by rmmod */ + +/*****************************************************************************/ +/* + * AFS cell record + */ +struct afs_cell +{ + atomic_t usage; + struct list_head link; /* main cell list link */ + struct list_head proc_link; /* /proc cell list link */ + struct proc_dir_entry *proc_dir; /* /proc dir for this cell */ + afs_cache_cellix_t cache_ix; /* cell cache index */ + + /* server record management */ + rwlock_t sv_lock; /* active server list lock */ + struct list_head sv_list; /* active server list */ + struct list_head sv_graveyard; /* inactive server list */ + spinlock_t sv_gylock; /* inactive server list lock */ + + /* volume location record management */ + struct rw_semaphore vl_sem; /* volume management serialisation semaphore */ + struct list_head vl_list; /* cell's active VL record list */ + struct list_head vl_graveyard; /* cell's inactive VL record list */ + spinlock_t vl_gylock; /* graveyard lock */ + unsigned short vl_naddrs; /* number of VL servers in addr list */ + unsigned short vl_curr_svix; /* current server index */ + struct in_addr vl_addrs[16]; /* cell VL server addresses */ + + char name[0]; /* cell name - must go last */ +}; + +extern int afs_cell_init(void); + +extern int afs_cell_create(const char *name, char *vllist, afs_cell_t **_cell); + +extern int afs_cell_lookup(afs_cache_t *cache, const char *name, afs_cell_t **_cell); + +#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0) + +extern afs_cell_t *afs_get_cell_maybe(afs_cell_t **_cell); + +extern void afs_put_cell(afs_cell_t *cell); + +extern void afs_cell_purge(void); + +#endif /* _LINUX_AFS_CELL_H */ diff -urNp linux-5240/fs/afs/cmservice.c linux-5250/fs/afs/cmservice.c --- linux-5240/fs/afs/cmservice.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/cmservice.c @@ -0,0 +1,639 @@ +/* cmservice.c: AFS Cache Manager Service + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "server.h" +#include "cell.h" +#include "transport.h" +#include +#include +#include +#include +#include "cmservice.h" +#include "internal.h" + +static unsigned afscm_usage; /* AFS cache manager usage count */ +static struct rw_semaphore afscm_sem; /* AFS cache manager start/stop semaphore */ + +static int afscm_new_call(struct rxrpc_call *call); +static void afscm_attention(struct rxrpc_call *call); +static void afscm_error(struct rxrpc_call *call); +static void afscm_aemap(struct rxrpc_call *call); + +static void _SRXAFSCM_CallBack(struct rxrpc_call *call); +static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call); +static void _SRXAFSCM_Probe(struct rxrpc_call *call); + +typedef void (*_SRXAFSCM_xxxx_t)(struct rxrpc_call *call); + +static const struct rxrpc_operation AFSCM_ops[] = { + { + id: 204, + asize: RXRPC_APP_MARK_EOF, + name: "CallBack", + user: _SRXAFSCM_CallBack, + }, + { + id: 205, + asize: RXRPC_APP_MARK_EOF, + name: "InitCallBackState", + user: _SRXAFSCM_InitCallBackState, + }, + { + id: 206, + asize: RXRPC_APP_MARK_EOF, + name: "Probe", + user: _SRXAFSCM_Probe, + }, +#if 0 + { + id: 207, + asize: RXRPC_APP_MARK_EOF, + name: "GetLock", + user: _SRXAFSCM_GetLock, + }, + { + id: 208, + asize: RXRPC_APP_MARK_EOF, + name: "GetCE", + user: _SRXAFSCM_GetCE, + }, + { + id: 209, + asize: RXRPC_APP_MARK_EOF, + name: "GetXStatsVersion", + user: _SRXAFSCM_GetXStatsVersion, + }, + { + id: 210, + asize: RXRPC_APP_MARK_EOF, + name: "GetXStats", + user: _SRXAFSCM_GetXStats, + } +#endif +}; + +static struct rxrpc_service AFSCM_service = { + name: "AFS/CM", + owner: THIS_MODULE, + link: LIST_HEAD_INIT(AFSCM_service.link), + new_call: afscm_new_call, + service_id: 1, + attn_func: afscm_attention, + error_func: afscm_error, + aemap_func: afscm_aemap, + ops_begin: &AFSCM_ops[0], + ops_end: &AFSCM_ops[sizeof(AFSCM_ops)/sizeof(AFSCM_ops[0])], +}; + +static DECLARE_COMPLETION(kafscmd_alive); +static DECLARE_COMPLETION(kafscmd_dead); +static DECLARE_WAIT_QUEUE_HEAD(kafscmd_sleepq); +static LIST_HEAD(kafscmd_attention_list); +static LIST_HEAD(afscm_calls); +static spinlock_t afscm_calls_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t kafscmd_attention_lock = SPIN_LOCK_UNLOCKED; +static int kafscmd_die; + +/*****************************************************************************/ +/* + * AFS Cache Manager kernel thread + */ +static int kafscmd(void *arg) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_call *call; + _SRXAFSCM_xxxx_t func; + int die; + + printk("kAFS: Started kafscmd %d\n",current->pid); + strcpy(current->comm,"kafscmd"); + + daemonize(); + + complete(&kafscmd_alive); + + /* only certain signals are of interest */ + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked,0); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3) + recalc_sigpending(); +#else + recalc_sigpending(current); +#endif + spin_unlock_irq(¤t->sigmask_lock); + + /* loop around looking for things to attend to */ + do { + if (list_empty(&kafscmd_attention_list)) { + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&kafscmd_sleepq,&myself); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (!list_empty(&kafscmd_attention_list) || + signal_pending(current) || + kafscmd_die) + break; + + schedule(); + } + + remove_wait_queue(&kafscmd_sleepq,&myself); + set_current_state(TASK_RUNNING); + } + + die = kafscmd_die; + + /* dequeue the next call requiring attention */ + call = NULL; + spin_lock(&kafscmd_attention_lock); + + if (!list_empty(&kafscmd_attention_list)) { + call = list_entry(kafscmd_attention_list.next, + struct rxrpc_call, + app_attn_link); + list_del_init(&call->app_attn_link); + die = 0; + } + + spin_unlock(&kafscmd_attention_lock); + + if (call) { + /* act upon it */ + _debug("@@@ Begin Attend Call %p",call); + + func = call->app_user; + if (func) + func(call); + + rxrpc_put_call(call); + + _debug("@@@ End Attend Call %p",call); + } + + } while(!die); + + /* and that's all */ + complete_and_exit(&kafscmd_dead,0); + +} /* end kafscmd() */ + +/*****************************************************************************/ +/* + * handle a call coming in to the cache manager + * - if I want to keep the call, I must increment its usage count + * - the return value will be negated and passed back in an abort packet if non-zero + * - serialised by virtue of there only being one krxiod + */ +static int afscm_new_call(struct rxrpc_call *call) +{ + _enter("%p{cid=%u u=%d}",call,ntohl(call->call_id),atomic_read(&call->usage)); + + rxrpc_get_call(call); + + /* add to my current call list */ + spin_lock(&afscm_calls_lock); + list_add(&call->app_link,&afscm_calls); + spin_unlock(&afscm_calls_lock); + + _leave(" = 0"); + return 0; + +} /* end afscm_new_call() */ + +/*****************************************************************************/ +/* + * queue on the kafscmd queue for attention + */ +static void afscm_attention(struct rxrpc_call *call) +{ + _enter("%p{cid=%u u=%d}",call,ntohl(call->call_id),atomic_read(&call->usage)); + + spin_lock(&kafscmd_attention_lock); + + if (list_empty(&call->app_attn_link)) { + list_add_tail(&call->app_attn_link,&kafscmd_attention_list); + rxrpc_get_call(call); + } + + spin_unlock(&kafscmd_attention_lock); + + wake_up(&kafscmd_sleepq); + + _leave(" {u=%d}",atomic_read(&call->usage)); +} /* end afscm_attention() */ + +/*****************************************************************************/ +/* + * handle my call being aborted + * - clean up, dequeue and put my ref to the call + */ +static void afscm_error(struct rxrpc_call *call) +{ + int removed; + + _enter("%p{est=%s ac=%u er=%d}", + call, + rxrpc_call_error_states[call->app_err_state], + call->app_abort_code, + call->app_errno); + + spin_lock(&kafscmd_attention_lock); + + if (list_empty(&call->app_attn_link)) { + list_add_tail(&call->app_attn_link,&kafscmd_attention_list); + rxrpc_get_call(call); + } + + spin_unlock(&kafscmd_attention_lock); + + removed = 0; + spin_lock(&afscm_calls_lock); + if (!list_empty(&call->app_link)) { + list_del_init(&call->app_link); + removed = 1; + } + spin_unlock(&afscm_calls_lock); + + if (removed) + rxrpc_put_call(call); + + wake_up(&kafscmd_sleepq); + + _leave(""); +} /* end afscm_error() */ + +/*****************************************************************************/ +/* + * map afs abort codes to/from Linux error codes + * - called with call->lock held + */ +static void afscm_aemap(struct rxrpc_call *call) +{ + switch (call->app_err_state) { + case RXRPC_ESTATE_LOCAL_ABORT: + call->app_abort_code = -call->app_errno; + break; + case RXRPC_ESTATE_PEER_ABORT: + call->app_errno = -ECONNABORTED; + break; + default: + break; + } +} /* end afscm_aemap() */ + +/*****************************************************************************/ +/* + * start the cache manager service if not already started + */ +int afscm_start(void) +{ + int ret; + + down_write(&afscm_sem); + if (!afscm_usage) { + ret = kernel_thread(kafscmd,NULL,0); + if (ret<0) + goto out; + + wait_for_completion(&kafscmd_alive); + + ret = rxrpc_add_service(afs_transport,&AFSCM_service); + if (ret<0) + goto kill; + } + + afscm_usage++; + up_write(&afscm_sem); + + return 0; + + kill: + kafscmd_die = 1; + wake_up(&kafscmd_sleepq); + wait_for_completion(&kafscmd_dead); + + out: + up_write(&afscm_sem); + return ret; + +} /* end afscm_start() */ + +/*****************************************************************************/ +/* + * stop the cache manager service + */ +void afscm_stop(void) +{ + struct rxrpc_call *call; + + down_write(&afscm_sem); + + if (afscm_usage==0) BUG(); + afscm_usage--; + + if (afscm_usage==0) { + /* don't want more incoming calls */ + rxrpc_del_service(afs_transport,&AFSCM_service); + + /* abort any calls I've still got open (the afscm_error() will dequeue them) */ + spin_lock(&afscm_calls_lock); + while (!list_empty(&afscm_calls)) { + call = list_entry(afscm_calls.next,struct rxrpc_call,app_link); + list_del_init(&call->app_link); + rxrpc_get_call(call); + spin_unlock(&afscm_calls_lock); + + rxrpc_call_abort(call,-ESRCH); /* abort, dequeue and put */ + + rxrpc_put_call(call); + + spin_lock(&afscm_calls_lock); + } + spin_unlock(&afscm_calls_lock); + + /* get rid of my daemon */ + kafscmd_die = 1; + wake_up(&kafscmd_sleepq); + wait_for_completion(&kafscmd_dead); + + /* dispose of any calls waiting for attention */ + spin_lock(&kafscmd_attention_lock); + while (!list_empty(&kafscmd_attention_list)) { + call = list_entry(kafscmd_attention_list.next, + struct rxrpc_call, + app_attn_link); + + list_del_init(&call->app_attn_link); + spin_unlock(&kafscmd_attention_lock); + + rxrpc_put_call(call); + + spin_lock(&kafscmd_attention_lock); + } + spin_unlock(&kafscmd_attention_lock); + } + + up_write(&afscm_sem); + +} /* end afscm_stop() */ + +/*****************************************************************************/ +/* + * handle the fileserver breaking a set of callbacks + */ +static void _SRXAFSCM_CallBack(struct rxrpc_call *call) +{ + afs_server_t *server; + size_t count, qty, tmp; + int ret = 0, removed; + + _enter("%p{acs=%s}",call,rxrpc_call_states[call->app_call_state]); + + server = afs_server_get_from_peer(call->conn->peer); + + switch (call->app_call_state) { + /* we've received the last packet + * - drain all the data from the call and send the reply + */ + case RXRPC_CSTATE_SRVR_GOT_ARGS: + ret = -EBADMSG; + qty = call->app_ready_qty; + if (qty<8 || qty>50*(6*4)+8) + break; + + { + afs_callback_t *cb, *pcb; + int loop; + u32 *fp, *bp; + + fp = rxrpc_call_alloc_scratch(call,qty); + + /* drag the entire argument block out to the scratch space */ + ret = rxrpc_call_read_data(call,fp,qty,0); + if (ret<0) + break; + + /* and unmarshall the parameter block */ + ret = -EBADMSG; + count = ntohl(*fp++); + if (count>AFSCBMAX || + (count*(3*4)+8 != qty && count*(6*4)+8 != qty)) + break; + + bp = fp + count*3; + tmp = ntohl(*bp++); + if (tmp>0 && tmp!=count) + break; + if (tmp==0) + bp = NULL; + + pcb = cb = rxrpc_call_alloc_scratch_s(call,afs_callback_t); + + for (loop=count-1; loop>=0; loop--) { + pcb->fid.vid = ntohl(*fp++); + pcb->fid.vnode = ntohl(*fp++); + pcb->fid.unique = ntohl(*fp++); + if (bp) { + pcb->version = ntohl(*bp++); + pcb->expiry = ntohl(*bp++); + pcb->type = ntohl(*bp++); + } + else { + pcb->version = 0; + pcb->expiry = 0; + pcb->type = AFSCM_CB_UNTYPED; + } + pcb++; + } + + /* invoke the actual service routine */ + ret = SRXAFSCM_CallBack(server,count,cb); + if (ret<0) + break; + } + + /* send the reply */ + ret = rxrpc_call_write_data(call,0,NULL,RXRPC_LAST_PACKET,GFP_KERNEL,0,&count); + if (ret<0) + break; + break; + + /* operation complete */ + case RXRPC_CSTATE_COMPLETE: + call->app_user = NULL; + removed = 0; + spin_lock(&afscm_calls_lock); + if (!list_empty(&call->app_link)) { + list_del_init(&call->app_link); + removed = 1; + } + spin_unlock(&afscm_calls_lock); + + if (removed) + rxrpc_put_call(call); + break; + + /* operation terminated on error */ + case RXRPC_CSTATE_ERROR: + call->app_user = NULL; + break; + + default: + break; + } + + if (ret<0) + rxrpc_call_abort(call,ret); + + if (server) afs_put_server(server); + + _leave(" = %d",ret); + +} /* end _SRXAFSCM_CallBack() */ + +/*****************************************************************************/ +/* + * handle the fileserver asking us to initialise our callback state + */ +static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call) +{ + afs_server_t *server; + size_t count; + int ret = 0, removed; + + _enter("%p{acs=%s}",call,rxrpc_call_states[call->app_call_state]); + + server = afs_server_get_from_peer(call->conn->peer); + + switch (call->app_call_state) { + /* we've received the last packet - drain all the data from the call */ + case RXRPC_CSTATE_SRVR_GOT_ARGS: + /* shouldn't be any args */ + ret = -EBADMSG; + break; + + /* send the reply when asked for it */ + case RXRPC_CSTATE_SRVR_SND_REPLY: + /* invoke the actual service routine */ + ret = SRXAFSCM_InitCallBackState(server); + if (ret<0) + break; + + ret = rxrpc_call_write_data(call,0,NULL,RXRPC_LAST_PACKET,GFP_KERNEL,0,&count); + if (ret<0) + break; + break; + + /* operation complete */ + case RXRPC_CSTATE_COMPLETE: + call->app_user = NULL; + removed = 0; + spin_lock(&afscm_calls_lock); + if (!list_empty(&call->app_link)) { + list_del_init(&call->app_link); + removed = 1; + } + spin_unlock(&afscm_calls_lock); + + if (removed) + rxrpc_put_call(call); + break; + + /* operation terminated on error */ + case RXRPC_CSTATE_ERROR: + call->app_user = NULL; + break; + + default: + break; + } + + if (ret<0) + rxrpc_call_abort(call,ret); + + if (server) afs_put_server(server); + + _leave(" = %d",ret); + +} /* end _SRXAFSCM_InitCallBackState() */ + +/*****************************************************************************/ +/* + * handle a probe from a fileserver + */ +static void _SRXAFSCM_Probe(struct rxrpc_call *call) +{ + afs_server_t *server; + size_t count; + int ret = 0, removed; + + _enter("%p{acs=%s}",call,rxrpc_call_states[call->app_call_state]); + + server = afs_server_get_from_peer(call->conn->peer); + + switch (call->app_call_state) { + /* we've received the last packet - drain all the data from the call */ + case RXRPC_CSTATE_SRVR_GOT_ARGS: + /* shouldn't be any args */ + ret = -EBADMSG; + break; + + /* send the reply when asked for it */ + case RXRPC_CSTATE_SRVR_SND_REPLY: + /* invoke the actual service routine */ + ret = SRXAFSCM_Probe(server); + if (ret<0) + break; + + ret = rxrpc_call_write_data(call,0,NULL,RXRPC_LAST_PACKET,GFP_KERNEL,0,&count); + if (ret<0) + break; + break; + + /* operation complete */ + case RXRPC_CSTATE_COMPLETE: + call->app_user = NULL; + removed = 0; + spin_lock(&afscm_calls_lock); + if (!list_empty(&call->app_link)) { + list_del_init(&call->app_link); + removed = 1; + } + spin_unlock(&afscm_calls_lock); + + if (removed) + rxrpc_put_call(call); + break; + + /* operation terminated on error */ + case RXRPC_CSTATE_ERROR: + call->app_user = NULL; + break; + + default: + break; + } + + if (ret<0) + rxrpc_call_abort(call,ret); + + if (server) afs_put_server(server); + + _leave(" = %d",ret); + +} /* end _SRXAFSCM_Probe() */ diff -urNp linux-5240/fs/afs/cmservice.h linux-5250/fs/afs/cmservice.h --- linux-5240/fs/afs/cmservice.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/cmservice.h @@ -0,0 +1,27 @@ +/* cmservice.h: AFS Cache Manager Service declarations + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_CMSERVICE_H +#define _LINUX_AFS_CMSERVICE_H + +#include +#include "types.h" + +/* cache manager start/stop */ +extern int afscm_start(void); +extern void afscm_stop(void); + +/* cache manager server functions */ +extern int SRXAFSCM_InitCallBackState(afs_server_t *server); +extern int SRXAFSCM_CallBack(afs_server_t *server, size_t count, afs_callback_t callbacks[]); +extern int SRXAFSCM_Probe(afs_server_t *server); + +#endif /* _LINUX_AFS_CMSERVICE_H */ diff -urNp linux-5240/fs/afs/dir.c linux-5250/fs/afs/dir.c --- linux-5240/fs/afs/dir.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/dir.c @@ -0,0 +1,640 @@ +/* dir.c: AFS filesystem directory handling + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "vnode.h" +#include "volume.h" +#include +#include "super.h" +#include "internal.h" + +static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry); +static int afs_dir_open(struct inode *inode, struct file *file); +static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir); +static int afs_d_revalidate(struct dentry *dentry, int flags); +static int afs_d_delete(struct dentry *dentry); +static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, loff_t fpos, + ino_t ino, unsigned dtype); + +struct file_operations afs_dir_file_operations = { + open: afs_dir_open, + readdir: afs_dir_readdir, +}; + +struct inode_operations afs_dir_inode_operations = { + lookup: afs_dir_lookup, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + getattr: afs_inode_getattr, +#else + revalidate: afs_inode_revalidate, +#endif +// create: afs_dir_create, +// link: afs_dir_link, +// unlink: afs_dir_unlink, +// symlink: afs_dir_symlink, +// mkdir: afs_dir_mkdir, +// rmdir: afs_dir_rmdir, +// mknod: afs_dir_mknod, +// rename: afs_dir_rename, +}; + +static struct dentry_operations afs_fs_dentry_operations = { + d_revalidate: afs_d_revalidate, + d_delete: afs_d_delete, +}; + +#define AFS_DIR_HASHTBL_SIZE 128 +#define AFS_DIR_DIRENT_SIZE 32 +#define AFS_DIRENT_PER_BLOCK 64 + +typedef struct afs_dirent { + u8 valid; + u8 unused[1]; + u16 hash_next; + u32 vnode; + u32 unique; + u8 name[16]; + u8 overflow[4]; /* if any char of the name (inc NUL) reaches here, consume + * the next dirent too */ + u8 extended_name[32]; +} afs_dirent_t; + +/* AFS directory page header (one at the beginning of every 2048-byte chunk) */ +typedef struct afs_dir_pagehdr { + u16 npages; + u16 magic; +#define AFS_DIR_MAGIC htons(1234) + u8 nentries; + u8 bitmap[8]; + u8 pad[19]; +} afs_dir_pagehdr_t; + +/* directory block layout */ +typedef union afs_dir_block { + + afs_dir_pagehdr_t pagehdr; + + struct { + afs_dir_pagehdr_t pagehdr; + u8 alloc_ctrs[128]; + u16 hashtable[AFS_DIR_HASHTBL_SIZE]; /* dir hash table */ + } hdr; + + afs_dirent_t dirents[AFS_DIRENT_PER_BLOCK]; +} afs_dir_block_t; + +/* layout on a linux VM page */ +typedef struct afs_dir_page { + afs_dir_block_t blocks[PAGE_SIZE/sizeof(afs_dir_block_t)]; +} afs_dir_page_t; + +struct afs_dir_lookup_cookie { + afs_fid_t fid; + const char *name; + size_t nlen; + int found; +}; + +/*****************************************************************************/ +/* + * check that a directory page is valid + */ +static inline void afs_dir_check_page(struct inode *dir, struct page *page) +{ + afs_dir_page_t *dbuf; + loff_t latter; + int tmp, qty; + +#if 0 + /* check the page count */ + qty = desc.size/sizeof(dbuf->blocks[0]); + if (qty==0) + goto error; + + if (page->index==0 && qty!=ntohs(dbuf->blocks[0].pagehdr.npages)) { + printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n", + __FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages)); + goto error; + } +#endif + + /* determine how many magic numbers there should be in this page */ + latter = dir->i_size - (page->index << PAGE_CACHE_SHIFT); + if (latter >= PAGE_SIZE) + qty = PAGE_SIZE; + else + qty = latter; + qty /= sizeof(afs_dir_block_t); + + /* check them */ + dbuf = page_address(page); + for (tmp=0; tmpblocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) { + printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n", + __FUNCTION__,dir->i_ino,tmp, + qty,ntohs(dbuf->blocks[tmp].pagehdr.magic)); + goto error; + } + } + + SetPageChecked(page); + return; + + error: + SetPageChecked(page); + SetPageError(page); + +} /* end afs_dir_check_page() */ + +/*****************************************************************************/ +/* + * discard a page cached in the pagecache + */ +static inline void afs_dir_put_page(struct page *page) +{ + kunmap(page); + page_cache_release(page); + +} /* end afs_dir_put_page() */ + +/*****************************************************************************/ +/* + * get a page into the pagecache + */ +static struct page *afs_dir_get_page(struct inode *dir, unsigned long index) +{ + struct page *page; + + _enter("{%lu},%lu",dir->i_ino,index); + + page = read_cache_page(dir->i_mapping,index, + (filler_t*)dir->i_mapping->a_ops->readpage,NULL); + if (!IS_ERR(page)) { + wait_on_page_locked(page); + kmap(page); + if (!PageUptodate(page)) + goto fail; + if (!PageChecked(page)) + afs_dir_check_page(dir,page); + if (PageError(page)) + goto fail; + } + return page; + + fail: + afs_dir_put_page(page); + return ERR_PTR(-EIO); +} /* end afs_dir_get_page() */ + +/*****************************************************************************/ +/* + * open an AFS directory file + */ +static int afs_dir_open(struct inode *inode, struct file *file) +{ + _enter("{%lu}",inode->i_ino); + + if (sizeof(afs_dir_block_t) != 2048) BUG(); + if (sizeof(afs_dirent_t) != 32) BUG(); + + if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) + return -ENOENT; + + _leave(" = 0"); + return 0; + +} /* end afs_dir_open() */ + +/*****************************************************************************/ +/* + * deal with one block in an AFS directory + */ +static int afs_dir_iterate_block(unsigned *fpos, + afs_dir_block_t *block, + unsigned blkoff, + void *cookie, + filldir_t filldir) +{ + afs_dirent_t *dire; + unsigned offset, next, curr; + size_t nlen; + int tmp, ret; + + _enter("%u,%x,%p,,",*fpos,blkoff,block); + + curr = (*fpos - blkoff) / sizeof(afs_dirent_t); + + /* walk through the block, an entry at a time */ + for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries; + offset < AFS_DIRENT_PER_BLOCK; + offset = next + ) { + next = offset + 1; + + /* skip entries marked unused in the bitmap */ + if (!(block->pagehdr.bitmap[offset/8] & (1 << (offset % 8)))) { + _debug("ENT[%u.%u]: unused\n",blkoff/sizeof(afs_dir_block_t),offset); + if (offset>=curr) + *fpos = blkoff + next * sizeof(afs_dirent_t); + continue; + } + + /* got a valid entry */ + dire = &block->dirents[offset]; + nlen = strnlen(dire->name,sizeof(*block) - offset*sizeof(afs_dirent_t)); + + _debug("ENT[%u.%u]: %s %u \"%.*s\"\n", + blkoff/sizeof(afs_dir_block_t),offset, + offsetname); + + /* work out where the next possible entry is */ + for (tmp=nlen; tmp>15; tmp-=sizeof(afs_dirent_t)) { + if (next>=AFS_DIRENT_PER_BLOCK) { + _debug("ENT[%u.%u]:" + " %u travelled beyond end dir block (len %u/%u)\n", + blkoff/sizeof(afs_dir_block_t),offset,next,tmp,nlen); + return -EIO; + } + if (!(block->pagehdr.bitmap[next/8] & (1 << (next % 8)))) { + _debug("ENT[%u.%u]: %u unmarked extension (len %u/%u)\n", + blkoff/sizeof(afs_dir_block_t),offset,next,tmp,nlen); + return -EIO; + } + + _debug("ENT[%u.%u]: ext %u/%u\n", + blkoff/sizeof(afs_dir_block_t),next,tmp,nlen); + next++; + } + + /* skip if starts before the current position */ + if (offsetname, + nlen, + blkoff + offset * sizeof(afs_dirent_t), + ntohl(dire->vnode), + filldir==afs_dir_lookup_filldir ? dire->unique : DT_UNKNOWN); + if (ret<0) { + _leave(" = 0 [full]"); + return 0; + } + + *fpos = blkoff + next * sizeof(afs_dirent_t); + } + + _leave(" = 1 [more]"); + return 1; +} /* end afs_dir_iterate_block() */ + +/*****************************************************************************/ +/* + * read an AFS directory + */ +static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie, filldir_t filldir) +{ + afs_dir_block_t *dblock; + afs_dir_page_t *dbuf; + struct page *page; + unsigned blkoff, limit; + int ret; + + _enter("{%lu},%u,,",dir->i_ino,*fpos); + + if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { + _leave(" = -ESTALE"); + return -ESTALE; + } + + /* round the file position up to the next entry boundary */ + *fpos += sizeof(afs_dirent_t) - 1; + *fpos &= ~(sizeof(afs_dirent_t) - 1); + + /* walk through the blocks in sequence */ + ret = 0; + while (*fpos < dir->i_size) { + blkoff = *fpos & ~(sizeof(afs_dir_block_t) - 1); + + /* fetch the appropriate page from the directory */ + page = afs_dir_get_page(dir,blkoff/PAGE_SIZE); + if (IS_ERR(page)) { + ret = PTR_ERR(page); + break; + } + + limit = blkoff & ~(PAGE_SIZE-1); + + dbuf = page_address(page); + + /* deal with the individual blocks stashed on this page */ + do { + dblock = &dbuf->blocks[(blkoff % PAGE_SIZE) / sizeof(afs_dir_block_t)]; + ret = afs_dir_iterate_block(fpos,dblock,blkoff,cookie,filldir); + if (ret!=1) { + afs_dir_put_page(page); + goto out; + } + + blkoff += sizeof(afs_dir_block_t); + + } while (*fpos < dir->i_size && blkoff < limit); + + afs_dir_put_page(page); + ret = 0; + } + + out: + _leave(" = %d",ret); + return ret; +} /* end afs_dir_iterate() */ + +/*****************************************************************************/ +/* + * read an AFS directory + */ +static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) +{ + unsigned fpos; + int ret; + + _enter("{%Ld,{%lu}}",file->f_pos,file->f_dentry->d_inode->i_ino); + + fpos = file->f_pos; + ret = afs_dir_iterate(file->f_dentry->d_inode,&fpos,cookie,filldir); + file->f_pos = fpos; + + _leave(" = %d",ret); + return ret; +} /* end afs_dir_readdir() */ + +/*****************************************************************************/ +/* + * search the directory for a name + * - if afs_dir_iterate_block() spots this function, it'll pass the FID uniquifier through dtype + */ +static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, loff_t fpos, + ino_t ino, unsigned dtype) +{ + struct afs_dir_lookup_cookie *cookie = _cookie; + + _enter("{%s,%u},%s,%u,,%lu,%u",cookie->name,cookie->nlen,name,nlen,ino,ntohl(dtype)); + + if (cookie->nlen != nlen || memcmp(cookie->name,name,nlen)!=0) { + _leave(" = 0 [no]"); + return 0; + } + + cookie->fid.vnode = ino; + cookie->fid.unique = ntohl(dtype); + cookie->found = 1; + + _leave(" = -1 [found]"); + return -1; +} /* end afs_dir_lookup_filldir() */ + +/*****************************************************************************/ +/* + * look up an entry in a directory + */ +static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry) +{ + struct afs_dir_lookup_cookie cookie; + struct afs_super_info *as; + struct inode *inode; + afs_vnode_t *vnode; + unsigned fpos; + int ret; + + _enter("{%lu},{%s}",dir->i_ino,dentry->d_name.name); + + /* insanity checks first */ + if (sizeof(afs_dir_block_t) != 2048) BUG(); + if (sizeof(afs_dirent_t) != 32) BUG(); + + if (dentry->d_name.len > 255) { + _leave(" = -ENAMETOOLONG"); + return ERR_PTR(-ENAMETOOLONG); + } + + vnode = AFS_FS_I(dir); + if (vnode->flags & AFS_VNODE_DELETED) { + _leave(" = -ESTALE"); + return ERR_PTR(-ESTALE); + } + + as = dir->i_sb->u.generic_sbp; + + /* search the directory */ + cookie.name = dentry->d_name.name; + cookie.nlen = dentry->d_name.len; + cookie.fid.vid = as->volume->vid; + cookie.found = 0; + + fpos = 0; + ret = afs_dir_iterate(dir,&fpos,&cookie,afs_dir_lookup_filldir); + if (ret<0) { + _leave(" = %d",ret); + return ERR_PTR(ret); + } + + ret = -ENOENT; + if (!cookie.found) { + _leave(" = %d",ret); + return ERR_PTR(ret); + } + + /* instantiate the dentry */ + ret = afs_iget(dir->i_sb,&cookie.fid,&inode); + if (ret<0) { + _leave(" = %d",ret); + return ERR_PTR(ret); + } + + dentry->d_op = &afs_fs_dentry_operations; + dentry->d_fsdata = (void*) (unsigned) vnode->status.version; + + d_add(dentry,inode); + _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }", + cookie.fid.vnode, + cookie.fid.unique, + dentry->d_inode->i_ino, + dentry->d_inode->i_version); + + return NULL; +} /* end afs_dir_lookup() */ + +/*****************************************************************************/ +/* + * check that a dentry lookup hit has found a valid entry + * - NOTE! the hit can be a negative hit too, so we can't assume we have an inode + * (derived from nfs_lookup_revalidate) + */ +static int afs_d_revalidate(struct dentry *dentry, int flags) +{ + struct afs_dir_lookup_cookie cookie; + struct dentry *parent; + struct inode *inode, *dir; + unsigned fpos; + int ret; + + _enter("%s,%x",dentry->d_name.name,flags); + + /* lock down the parent dentry so we can peer at it */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + read_lock(&dparent_lock); + parent = dget(dentry->d_parent); + read_unlock(&dparent_lock); +#else + lock_kernel(); + parent = dget(dentry->d_parent); + unlock_kernel(); +#endif + + dir = parent->d_inode; + inode = dentry->d_inode; + + /* handle a negative inode */ + if (!inode) + goto out_bad; + + /* handle a bad inode */ + if (is_bad_inode(inode)) { + printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", + dentry->d_parent->d_name.name,dentry->d_name.name); + goto out_bad; + } + + /* force a full look up if the parent directory changed since last the server was consulted + * - otherwise this inode must still exist, even if the inode details themselves have + * changed + */ + if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED) + afs_vnode_fetch_status(AFS_FS_I(dir)); + + if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { + _debug("%s: parent dir deleted",dentry->d_name.name); + goto out_bad; + } + + if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) { + _debug("%s: file already deleted",dentry->d_name.name); + goto out_bad; + } + + if ((unsigned)dentry->d_fsdata != (unsigned)AFS_FS_I(dir)->status.version) { + _debug("%s: parent changed %u -> %u", + dentry->d_name.name, + (unsigned)dentry->d_fsdata, + (unsigned)AFS_FS_I(dir)->status.version); + + /* search the directory for this vnode */ + cookie.name = dentry->d_name.name; + cookie.nlen = dentry->d_name.len; + cookie.fid.vid = AFS_FS_I(inode)->volume->vid; + cookie.found = 0; + + fpos = 0; + ret = afs_dir_iterate(dir,&fpos,&cookie,afs_dir_lookup_filldir); + if (ret<0) { + _debug("failed to iterate dir %s: %d",parent->d_name.name,ret); + goto out_bad; + } + + if (!cookie.found) { + _debug("%s: dirent not found",dentry->d_name.name); + goto not_found; + } + + /* if the vnode ID has changed, then the dirent points to a different file */ + if (cookie.fid.vnode!=AFS_FS_I(inode)->fid.vnode) { + _debug("%s: dirent changed",dentry->d_name.name); + goto not_found; + } + + /* if the vnode ID uniqifier has changed, then the file has been deleted */ + if (cookie.fid.unique!=AFS_FS_I(inode)->fid.unique) { + _debug("%s: file deleted (uq %u -> %u I:%lu)", + dentry->d_name.name, + cookie.fid.unique, + AFS_FS_I(inode)->fid.unique, + inode->i_version); + spin_lock(&AFS_FS_I(inode)->lock); + AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED; + spin_unlock(&AFS_FS_I(inode)->lock); + invalidate_inode_pages(inode); + goto out_bad; + } + + dentry->d_fsdata = (void*) (unsigned) AFS_FS_I(dir)->status.version; + } + + out_valid: + dput(parent); + _leave(" = 1 [valid]"); + return 1; + + /* the dirent, if it exists, now points to a different vnode */ + not_found: + dentry->d_flags |= DCACHE_NFSFS_RENAMED; + + out_bad: + if (inode) { + /* don't unhash if we have submounts */ + if (have_submounts(dentry)) + goto out_valid; + } + + shrink_dcache_parent(dentry); + + _debug("dropping dentry %s/%s",dentry->d_parent->d_name.name,dentry->d_name.name); + d_drop(dentry); + + dput(parent); + + _leave(" = 0 [bad]"); + return 0; +} /* end afs_d_revalidate() */ + +/*****************************************************************************/ +/* + * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't sleep) + * - called from dput() when d_count is going to 0. + * - return 1 to request dentry be unhashed, 0 otherwise + */ +static int afs_d_delete(struct dentry *dentry) +{ + _enter("%s",dentry->d_name.name); + + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) + return 1; + + if (dentry->d_inode) { + if (AFS_FS_I(dentry->d_inode)->flags & AFS_VNODE_DELETED) + goto zap; + } + + _leave(" = 0 [keep]"); + return 0; + + zap: + _leave(" = 1 [zap]"); + return 1; +} /* end afs_d_delete() */ diff -urNp linux-5240/fs/afs/errors.h linux-5250/fs/afs/errors.h --- linux-5240/fs/afs/errors.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/errors.h @@ -0,0 +1,34 @@ +/* errors.h: AFS abort/error codes + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _H_DB712916_5113_11D6_9A6D_0002B3163499 +#define _H_DB712916_5113_11D6_9A6D_0002B3163499 + +#include "types.h" + +/* file server abort codes */ +typedef enum { + VSALVAGE = 101, /* volume needs salvaging */ + VNOVNODE = 102, /* no such file/dir (vnode) */ + VNOVOL = 103, /* no such volume or volume unavailable */ + VVOLEXISTS = 104, /* volume name already exists */ + VNOSERVICE = 105, /* volume not currently in service */ + VOFFLINE = 106, /* volume is currently offline (more info available [VVL-spec]) */ + VONLINE = 107, /* volume is already online */ + VDISKFULL = 108, /* disk partition is full */ + VOVERQUOTA = 109, /* volume's maximum quota exceeded */ + VBUSY = 110, /* volume is temporarily unavailable */ + VMOVED = 111, /* volume moved to new server - ask this FS where */ +} afs_rxfs_abort_t; + +extern int afs_abort_to_error(int abortcode); + +#endif /* _H_DB712916_5113_11D6_9A6D_0002B3163499 */ diff -urNp linux-5240/fs/afs/file.c linux-5250/fs/afs/file.c --- linux-5240/fs/afs/file.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/file.c @@ -0,0 +1,143 @@ +/* file.c: AFS filesystem file handling + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "volume.h" +#include "vnode.h" +#include +#include "internal.h" + +//static int afs_file_open(struct inode *inode, struct file *file); +//static int afs_file_release(struct inode *inode, struct file *file); + +static int afs_file_readpage(struct file *file, struct page *page); + +//static ssize_t afs_file_read(struct file *file, char *buf, size_t size, loff_t *off); + +static ssize_t afs_file_write(struct file *file, const char *buf, size_t size, loff_t *off); + +struct inode_operations afs_file_inode_operations = { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + getattr: afs_inode_getattr, +#else + revalidate: afs_inode_revalidate, +#endif +}; + +struct file_operations afs_file_file_operations = { +// open: afs_file_open, +// release: afs_file_release, + read: generic_file_read, //afs_file_read, + write: afs_file_write, + mmap: generic_file_mmap, +// fsync: afs_file_fsync, +}; + +struct address_space_operations afs_fs_aops = { + readpage: afs_file_readpage, +}; + +/*****************************************************************************/ +/* + * AFS file read + */ +#if 0 +static ssize_t afs_file_read(struct file *file, char *buf, size_t size, loff_t *off) +{ + struct afs_inode_info *ai; + + ai = AFS_FS_I(file->f_dentry->d_inode); + if (ai->flags & AFS_INODE_DELETED) + return -ESTALE; + + return -EIO; +} /* end afs_file_read() */ +#endif + +/*****************************************************************************/ +/* + * AFS file write + */ +static ssize_t afs_file_write(struct file *file, const char *buf, size_t size, loff_t *off) +{ + afs_vnode_t *vnode; + + vnode = AFS_FS_I(file->f_dentry->d_inode); + if (vnode->flags & AFS_VNODE_DELETED) + return -ESTALE; + + return -EIO; +} /* end afs_file_write() */ + +/*****************************************************************************/ +/* + * AFS read page from file (or symlink) + */ +static int afs_file_readpage(struct file *file, struct page *page) +{ + struct afs_rxfs_fetch_descriptor desc; + struct inode *inode; + afs_vnode_t *vnode; + int ret; + + inode = page->mapping->host; + + _enter("{%lu},{%lu}",inode->i_ino,page->index); + + vnode = AFS_FS_I(inode); + + if (!PageLocked(page)) + PAGE_BUG(page); + + ret = -ESTALE; + if (vnode->flags & AFS_VNODE_DELETED) + goto error; + + /* work out how much to get and from where */ + desc.fid = vnode->fid; + desc.offset = page->index << PAGE_CACHE_SHIFT; + desc.size = min((size_t)(inode->i_size - desc.offset),(size_t)PAGE_SIZE); + desc.buffer = kmap(page); + + clear_page(desc.buffer); + + /* read the contents of the file from the server into the page */ + ret = afs_vnode_fetch_data(vnode,&desc); + kunmap(page); + if (ret<0) { + if (ret==-ENOENT) { + _debug("got NOENT from server - marking file deleted and stale"); + vnode->flags |= AFS_VNODE_DELETED; + ret = -ESTALE; + } + goto error; + } + + SetPageUptodate(page); + unlock_page(page); + + _leave(" = 0"); + return 0; + + error: + SetPageError(page); + unlock_page(page); + + _leave(" = %d",ret); + return ret; + +} /* end afs_file_readpage() */ diff -urNp linux-5240/fs/afs/fsclient.c linux-5250/fs/afs/fsclient.c --- linux-5240/fs/afs/fsclient.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/fsclient.c @@ -0,0 +1,816 @@ +/* fsclient.c: AFS File Server client stubs + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "fsclient.h" +#include "cmservice.h" +#include "vnode.h" +#include "server.h" +#include "errors.h" +#include "internal.h" + +#define FSFETCHSTATUS 132 /* AFS Fetch file status */ +#define FSFETCHDATA 130 /* AFS Fetch file data */ +#define FSGIVEUPCALLBACKS 147 /* AFS Discard server callback promises */ +#define FSGETVOLUMEINFO 148 /* AFS Get root volume information */ +#define FSGETROOTVOLUME 151 /* AFS Get root volume name */ +#define FSLOOKUP 161 /* AFS lookup file in directory */ + +/*****************************************************************************/ +/* + * map afs abort codes to/from Linux error codes + * - called with call->lock held + */ +static void afs_rxfs_aemap(struct rxrpc_call *call) +{ + switch (call->app_err_state) { + case RXRPC_ESTATE_LOCAL_ABORT: + call->app_abort_code = -call->app_errno; + break; + case RXRPC_ESTATE_PEER_ABORT: + call->app_errno = afs_abort_to_error(call->app_abort_code); + break; + default: + break; + } +} /* end afs_rxfs_aemap() */ + +/*****************************************************************************/ +/* + * get the root volume name from a fileserver + * - this operation doesn't seem to work correctly in OpenAFS server 1.2.2 + */ +#if 0 +int afs_rxfs_get_root_volume(afs_server_t *server, char *buf, size_t *buflen) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct iovec piov[2]; + size_t sent; + int ret; + u32 param[1]; + + kenter("%p,%p,%u",server,buf,*buflen); + + /* get hold of the fileserver connection */ + ret = afs_server_get_fsconn(server,&conn); + if (ret<0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(conn,NULL,NULL,afs_rxfs_aemap,&call); + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + goto out_put_conn; + } + call->app_opcode = FSGETROOTVOLUME; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + param[0] = htonl(FSGETROOTVOLUME); + + piov[0].iov_len = sizeof(param); + piov[0].iov_base = param; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,1,piov,RXRPC_LAST_PACKET,GFP_NOFS,0,&sent); + if (ret<0) + goto abort; + + /* wait for the reply to completely arrive */ + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (call->app_call_state!=RXRPC_CSTATE_CLNT_RCV_REPLY || + signal_pending(current)) + break; + schedule(); + } + set_current_state(TASK_RUNNING); + + ret = -EINTR; + if (signal_pending(current)) + goto abort; + + switch (call->app_call_state) { + case RXRPC_CSTATE_ERROR: + ret = call->app_errno; + kdebug("Got Error: %d",ret); + goto out_unwait; + + case RXRPC_CSTATE_CLNT_GOT_REPLY: + /* read the reply */ + kdebug("Got Reply: qty=%d",call->app_ready_qty); + + ret = -EBADMSG; + if (call->app_ready_qty <= 4) + goto abort; + + ret = rxrpc_call_read_data(call,NULL,call->app_ready_qty,0); + if (ret<0) + goto abort; + +#if 0 + /* unmarshall the reply */ + bp = buffer; + for (loop=0; loop<65; loop++) + entry->name[loop] = ntohl(*bp++); + entry->name[64] = 0; + + entry->type = ntohl(*bp++); + entry->num_servers = ntohl(*bp++); + + for (loop=0; loop<8; loop++) + entry->servers[loop].addr.s_addr = *bp++; + + for (loop=0; loop<8; loop++) + entry->servers[loop].partition = ntohl(*bp++); + + for (loop=0; loop<8; loop++) + entry->servers[loop].flags = ntohl(*bp++); + + for (loop=0; loop<3; loop++) + entry->volume_ids[loop] = ntohl(*bp++); + + entry->clone_id = ntohl(*bp++); + entry->flags = ntohl(*bp); +#endif + + /* success */ + ret = 0; + goto out_unwait; + + default: + BUG(); + } + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call,ret); + schedule(); + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_fsconn(server,conn); + out: + kleave(""); + return ret; +} /* end afs_rxfs_get_root_volume() */ +#endif + +/*****************************************************************************/ +/* + * get information about a volume + */ +#if 0 +int afs_rxfs_get_volume_info(afs_server_t *server, + const char *name, + afs_volume_info_t *vinfo) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct iovec piov[3]; + size_t sent; + int ret; + u32 param[2], *bp, zero; + + _enter("%p,%s,%p",server,name,vinfo); + + /* get hold of the fileserver connection */ + ret = afs_server_get_fsconn(server,&conn); + if (ret<0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(conn,NULL,NULL,afs_rxfs_aemap,&call); + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + goto out_put_conn; + } + call->app_opcode = FSGETVOLUMEINFO; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + piov[1].iov_len = strlen(name); + piov[1].iov_base = (char*)name; + + zero = 0; + piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3; + piov[2].iov_base = &zero; + + param[0] = htonl(FSGETVOLUMEINFO); + param[1] = htonl(piov[1].iov_len); + + piov[0].iov_len = sizeof(param); + piov[0].iov_base = param; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,3,piov,RXRPC_LAST_PACKET,GFP_NOFS,0,&sent); + if (ret<0) + goto abort; + + /* wait for the reply to completely arrive */ + bp = rxrpc_call_alloc_scratch(call,64); + + ret = rxrpc_call_read_data(call,bp,64,RXRPC_CALL_READ_BLOCK|RXRPC_CALL_READ_ALL); + if (ret<0) { + if (ret==-ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + goto abort; + } + + /* unmarshall the reply */ + vinfo->vid = ntohl(*bp++); + vinfo->type = ntohl(*bp++); + + vinfo->type_vids[0] = ntohl(*bp++); + vinfo->type_vids[1] = ntohl(*bp++); + vinfo->type_vids[2] = ntohl(*bp++); + vinfo->type_vids[3] = ntohl(*bp++); + vinfo->type_vids[4] = ntohl(*bp++); + + vinfo->nservers = ntohl(*bp++); + vinfo->servers[0].addr.s_addr = *bp++; + vinfo->servers[1].addr.s_addr = *bp++; + vinfo->servers[2].addr.s_addr = *bp++; + vinfo->servers[3].addr.s_addr = *bp++; + vinfo->servers[4].addr.s_addr = *bp++; + vinfo->servers[5].addr.s_addr = *bp++; + vinfo->servers[6].addr.s_addr = *bp++; + vinfo->servers[7].addr.s_addr = *bp++; + + ret = -EBADMSG; + if (vinfo->nservers>8) + goto abort; + + /* success */ + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_fsconn(server,conn); + out: + _leave(""); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call,ret); + schedule(); + goto out_unwait; + +} /* end afs_rxfs_get_volume_info() */ +#endif + +/*****************************************************************************/ +/* + * fetch the status information for a file + */ +int afs_rxfs_fetch_file_status(afs_server_t *server, + afs_vnode_t *vnode, + afs_volsync_t *volsync) +{ + DECLARE_WAITQUEUE(myself,current); + + struct afs_server_callslot callslot; + struct rxrpc_call *call; + struct iovec piov[1]; + size_t sent; + int ret; + u32 *bp; + + _enter("%p,{%u,%u,%u}",server,vnode->fid.vid,vnode->fid.vnode,vnode->fid.unique); + + /* get hold of the fileserver connection */ + ret = afs_server_request_callslot(server,&callslot); + if (ret<0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(callslot.conn,NULL,NULL,afs_rxfs_aemap,&call); + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + goto out_put_conn; + } + call->app_opcode = FSFETCHSTATUS; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + bp = rxrpc_call_alloc_scratch(call,16); + bp[0] = htonl(FSFETCHSTATUS); + bp[1] = htonl(vnode->fid.vid); + bp[2] = htonl(vnode->fid.vnode); + bp[3] = htonl(vnode->fid.unique); + + piov[0].iov_len = 16; + piov[0].iov_base = bp; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,1,piov,RXRPC_LAST_PACKET,GFP_NOFS,0,&sent); + if (ret<0) + goto abort; + + /* wait for the reply to completely arrive */ + bp = rxrpc_call_alloc_scratch(call,120); + + ret = rxrpc_call_read_data(call,bp,120,RXRPC_CALL_READ_BLOCK|RXRPC_CALL_READ_ALL); + if (ret<0) { + if (ret==-ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + goto abort; + } + + /* unmarshall the reply */ + vnode->status.if_version = ntohl(*bp++); + vnode->status.type = ntohl(*bp++); + vnode->status.nlink = ntohl(*bp++); + vnode->status.size = ntohl(*bp++); + vnode->status.version = ntohl(*bp++); + vnode->status.author = ntohl(*bp++); + vnode->status.owner = ntohl(*bp++); + vnode->status.caller_access = ntohl(*bp++); + vnode->status.anon_access = ntohl(*bp++); + vnode->status.mode = ntohl(*bp++); + vnode->status.parent.vid = vnode->fid.vid; + vnode->status.parent.vnode = ntohl(*bp++); + vnode->status.parent.unique = ntohl(*bp++); + bp++; /* seg size */ + vnode->status.mtime_client = ntohl(*bp++); + vnode->status.mtime_server = ntohl(*bp++); + bp++; /* group */ + bp++; /* sync counter */ + vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + + vnode->cb_version = ntohl(*bp++); + vnode->cb_expiry = ntohl(*bp++); + vnode->cb_type = ntohl(*bp++); + + if (volsync) { + volsync->creation = ntohl(*bp++); + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + bp++; /* spare5 */ + bp++; /* spare6 */ + } + + /* success */ + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_callslot(server,&callslot); + out: + _leave(""); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call,ret); + schedule(); + goto out_unwait; +} /* end afs_rxfs_fetch_file_status() */ + +/*****************************************************************************/ +/* + * fetch the contents of a file or directory + */ +int afs_rxfs_fetch_file_data(afs_server_t *server, + afs_vnode_t *vnode, + struct afs_rxfs_fetch_descriptor *desc, + afs_volsync_t *volsync) +{ + DECLARE_WAITQUEUE(myself,current); + + struct afs_server_callslot callslot; + struct rxrpc_call *call; + struct iovec piov[1]; + size_t sent; + int ret; + u32 *bp; + + _enter("%p,{fid={%u,%u,%u},sz=%u,of=%lu}", + server, + desc->fid.vid, + desc->fid.vnode, + desc->fid.unique, + desc->size, + desc->offset); + + /* get hold of the fileserver connection */ + ret = afs_server_request_callslot(server,&callslot); + if (ret<0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(callslot.conn,NULL,NULL,afs_rxfs_aemap,&call); + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + goto out_put_conn; + } + call->app_opcode = FSFETCHDATA; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + bp = rxrpc_call_alloc_scratch(call,24); + bp[0] = htonl(FSFETCHDATA); + bp[1] = htonl(desc->fid.vid); + bp[2] = htonl(desc->fid.vnode); + bp[3] = htonl(desc->fid.unique); + bp[4] = htonl(desc->offset); + bp[5] = htonl(desc->size); + + piov[0].iov_len = 24; + piov[0].iov_base = bp; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,1,piov,RXRPC_LAST_PACKET,GFP_NOFS,0,&sent); + if (ret<0) + goto abort; + + /* wait for the data count to arrive */ + ret = rxrpc_call_read_data(call,bp,4,RXRPC_CALL_READ_BLOCK); + if (ret<0) + goto read_failed; + + desc->actual = ntohl(bp[0]); + if (desc->actual!=desc->size) { + ret = -EBADMSG; + goto abort; + } + + /* call the app to read the actual data */ + rxrpc_call_reset_scratch(call); + + ret = rxrpc_call_read_data(call,desc->buffer,desc->actual,RXRPC_CALL_READ_BLOCK); + if (ret<0) + goto read_failed; + + /* wait for the rest of the reply to completely arrive */ + rxrpc_call_reset_scratch(call); + bp = rxrpc_call_alloc_scratch(call,120); + + ret = rxrpc_call_read_data(call,bp,120,RXRPC_CALL_READ_BLOCK|RXRPC_CALL_READ_ALL); + if (ret<0) + goto read_failed; + + /* unmarshall the reply */ + vnode->status.if_version = ntohl(*bp++); + vnode->status.type = ntohl(*bp++); + vnode->status.nlink = ntohl(*bp++); + vnode->status.size = ntohl(*bp++); + vnode->status.version = ntohl(*bp++); + vnode->status.author = ntohl(*bp++); + vnode->status.owner = ntohl(*bp++); + vnode->status.caller_access = ntohl(*bp++); + vnode->status.anon_access = ntohl(*bp++); + vnode->status.mode = ntohl(*bp++); + vnode->status.parent.vid = desc->fid.vid; + vnode->status.parent.vnode = ntohl(*bp++); + vnode->status.parent.unique = ntohl(*bp++); + bp++; /* seg size */ + vnode->status.mtime_client = ntohl(*bp++); + vnode->status.mtime_server = ntohl(*bp++); + bp++; /* group */ + bp++; /* sync counter */ + vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + + vnode->cb_version = ntohl(*bp++); + vnode->cb_expiry = ntohl(*bp++); + vnode->cb_type = ntohl(*bp++); + + if (volsync) { + volsync->creation = ntohl(*bp++); + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + bp++; /* spare5 */ + bp++; /* spare6 */ + } + + /* success */ + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_callslot(server,&callslot); + out: + _leave(" = %d",ret); + return ret; + + read_failed: + if (ret==-ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call,ret); + schedule(); + goto out_unwait; + +} /* end afs_rxfs_fetch_file_data() */ + +/*****************************************************************************/ +/* + * ask the AFS fileserver to discard a callback request on a file + */ +int afs_rxfs_give_up_callback(afs_server_t *server, afs_vnode_t *vnode) +{ + DECLARE_WAITQUEUE(myself,current); + + struct afs_server_callslot callslot; + struct rxrpc_call *call; + struct iovec piov[1]; + size_t sent; + int ret; + u32 *bp; + + _enter("%p,{%u,%u,%u}",server,vnode->fid.vid,vnode->fid.vnode,vnode->fid.unique); + + /* get hold of the fileserver connection */ + ret = afs_server_request_callslot(server,&callslot); + if (ret<0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(callslot.conn,NULL,NULL,afs_rxfs_aemap,&call); + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + goto out_put_conn; + } + call->app_opcode = FSGIVEUPCALLBACKS; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + bp = rxrpc_call_alloc_scratch(call,(1+4+4)*4); + + piov[0].iov_len = (1+4+4)*4; + piov[0].iov_base = bp; + + *bp++ = htonl(FSGIVEUPCALLBACKS); + *bp++ = htonl(1); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(1); + *bp++ = htonl(vnode->cb_version); + *bp++ = htonl(vnode->cb_expiry); + *bp++ = htonl(vnode->cb_type); + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,1,piov,RXRPC_LAST_PACKET,GFP_NOFS,0,&sent); + if (ret<0) + goto abort; + + /* wait for the reply to completely arrive */ + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (call->app_call_state!=RXRPC_CSTATE_CLNT_RCV_REPLY || + signal_pending(current)) + break; + schedule(); + } + set_current_state(TASK_RUNNING); + + ret = -EINTR; + if (signal_pending(current)) + goto abort; + + switch (call->app_call_state) { + case RXRPC_CSTATE_ERROR: + ret = call->app_errno; + goto out_unwait; + + case RXRPC_CSTATE_CLNT_GOT_REPLY: + ret = 0; + goto out_unwait; + + default: + BUG(); + } + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_callslot(server,&callslot); + out: + _leave(""); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call,ret); + schedule(); + goto out_unwait; +} /* end afs_rxfs_give_up_callback() */ + +/*****************************************************************************/ +/* + * look a filename up in a directory + * - this operation doesn't seem to work correctly in OpenAFS server 1.2.2 + */ +#if 0 +int afs_rxfs_lookup(afs_server_t *server, + afs_vnode_t *dir, + const char *filename, + afs_vnode_t *vnode, + afs_volsync_t *volsync) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct iovec piov[3]; + size_t sent; + int ret; + u32 *bp, zero; + + kenter("%p,{%u,%u,%u},%s",server,fid->vid,fid->vnode,fid->unique,filename); + + /* get hold of the fileserver connection */ + ret = afs_server_get_fsconn(server,&conn); + if (ret<0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(conn,NULL,NULL,afs_rxfs_aemap,&call); + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + goto out_put_conn; + } + call->app_opcode = FSLOOKUP; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + bp = rxrpc_call_alloc_scratch(call,20); + + zero = 0; + + piov[0].iov_len = 20; + piov[0].iov_base = bp; + piov[1].iov_len = strlen(filename); + piov[1].iov_base = (char*) filename; + piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3; + piov[2].iov_base = &zero; + + *bp++ = htonl(FSLOOKUP); + *bp++ = htonl(dirfid->vid); + *bp++ = htonl(dirfid->vnode); + *bp++ = htonl(dirfid->unique); + *bp++ = htonl(piov[1].iov_len); + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,3,piov,RXRPC_LAST_PACKET,GFP_NOFS,0,&sent); + if (ret<0) + goto abort; + + /* wait for the reply to completely arrive */ + bp = rxrpc_call_alloc_scratch(call,220); + + ret = rxrpc_call_read_data(call,bp,220,RXRPC_CALL_READ_BLOCK|RXRPC_CALL_READ_ALL); + if (ret<0) { + if (ret==-ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + goto abort; + } + + /* unmarshall the reply */ + fid->vid = ntohl(*bp++); + fid->vnode = ntohl(*bp++); + fid->unique = ntohl(*bp++); + + vnode->status.if_version = ntohl(*bp++); + vnode->status.type = ntohl(*bp++); + vnode->status.nlink = ntohl(*bp++); + vnode->status.size = ntohl(*bp++); + vnode->status.version = ntohl(*bp++); + vnode->status.author = ntohl(*bp++); + vnode->status.owner = ntohl(*bp++); + vnode->status.caller_access = ntohl(*bp++); + vnode->status.anon_access = ntohl(*bp++); + vnode->status.mode = ntohl(*bp++); + vnode->status.parent.vid = dirfid->vid; + vnode->status.parent.vnode = ntohl(*bp++); + vnode->status.parent.unique = ntohl(*bp++); + bp++; /* seg size */ + vnode->status.mtime_client = ntohl(*bp++); + vnode->status.mtime_server = ntohl(*bp++); + bp++; /* group */ + bp++; /* sync counter */ + vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + + dir->status.if_version = ntohl(*bp++); + dir->status.type = ntohl(*bp++); + dir->status.nlink = ntohl(*bp++); + dir->status.size = ntohl(*bp++); + dir->status.version = ntohl(*bp++); + dir->status.author = ntohl(*bp++); + dir->status.owner = ntohl(*bp++); + dir->status.caller_access = ntohl(*bp++); + dir->status.anon_access = ntohl(*bp++); + dir->status.mode = ntohl(*bp++); + dir->status.parent.vid = dirfid->vid; + dir->status.parent.vnode = ntohl(*bp++); + dir->status.parent.unique = ntohl(*bp++); + bp++; /* seg size */ + dir->status.mtime_client = ntohl(*bp++); + dir->status.mtime_server = ntohl(*bp++); + bp++; /* group */ + bp++; /* sync counter */ + dir->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + + callback->fid = *fid; + callback->version = ntohl(*bp++); + callback->expiry = ntohl(*bp++); + callback->type = ntohl(*bp++); + + if (volsync) { + volsync->creation = ntohl(*bp++); + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + bp++; /* spare5 */ + bp++; /* spare6 */ + } + + /* success */ + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_fsconn(server,conn); + out: + kleave(""); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call,ret); + schedule(); + goto out_unwait; +} /* end afs_rxfs_lookup() */ +#endif diff -urNp linux-5240/fs/afs/fsclient.h linux-5250/fs/afs/fsclient.h --- linux-5240/fs/afs/fsclient.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/fsclient.h @@ -0,0 +1,53 @@ +/* fsclient.h: AFS File Server client stub declarations + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_FSCLIENT_H +#define _LINUX_AFS_FSCLIENT_H + +#include "server.h" + +extern int afs_rxfs_get_volume_info(afs_server_t *server, + const char *name, + afs_volume_info_t *vinfo); + +extern int afs_rxfs_fetch_file_status(afs_server_t *server, + afs_vnode_t *vnode, + afs_volsync_t *volsync); + +struct afs_rxfs_fetch_descriptor { + afs_fid_t fid; /* file ID to fetch */ + size_t size; /* total number of bytes to fetch */ + off_t offset; /* offset in file to start from */ + void *buffer; /* read buffer */ + size_t actual; /* actual size sent back by server */ +}; + +extern int afs_rxfs_fetch_file_data(afs_server_t *server, + afs_vnode_t *vnode, + struct afs_rxfs_fetch_descriptor *desc, + afs_volsync_t *volsync); + +extern int afs_rxfs_give_up_callback(afs_server_t *server, afs_vnode_t *vnode); + +/* this doesn't appear to work in OpenAFS server */ +extern int afs_rxfs_lookup(afs_server_t *server, + afs_vnode_t *dir, + const char *filename, + afs_vnode_t *vnode, + afs_volsync_t *volsync); + +/* this is apparently mis-implemented in OpenAFS server */ +extern int afs_rxfs_get_root_volume(afs_server_t *server, + char *buf, + size_t *buflen); + + +#endif /* _LINUX_AFS_FSCLIENT_H */ diff -urNp linux-5240/fs/afs/inode.c linux-5250/fs/afs/inode.c --- linux-5240/fs/afs/inode.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/inode.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2002 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: David Woodhouse + * David Howells + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "volume.h" +#include "vnode.h" +#include "cache.h" +#include "super.h" +#include "internal.h" + +struct afs_iget_data { + afs_fid_t fid; + afs_volume_t *volume; /* volume on which resides */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + afs_vnode_t *new_vnode; /* new vnode record */ +#endif +}; + +/*****************************************************************************/ +/* + * map the AFS file status to the inode member variables + */ +static int afs_inode_map_status(afs_vnode_t *vnode) +{ + struct inode *inode = AFS_VNODE_TO_I(vnode); + + _debug("FS: ft=%d lk=%d sz=%u ver=%Lu mod=%hu", + vnode->status.type, + vnode->status.nlink, + vnode->status.size, + vnode->status.version, + vnode->status.mode); + + switch (vnode->status.type) { + case AFS_FTYPE_FILE: + inode->i_mode = S_IFREG | vnode->status.mode; + inode->i_op = &afs_file_inode_operations; + inode->i_fop = &afs_file_file_operations; + break; + case AFS_FTYPE_DIR: + inode->i_mode = S_IFDIR | vnode->status.mode; + inode->i_op = &afs_dir_inode_operations; + inode->i_fop = &afs_dir_file_operations; + break; + case AFS_FTYPE_SYMLINK: + inode->i_mode = S_IFLNK | vnode->status.mode; + inode->i_op = &page_symlink_inode_operations; + break; + default: + printk("kAFS: AFS vnode with undefined type\n"); + return -EBADMSG; + } + + inode->i_nlink = vnode->status.nlink; + inode->i_uid = vnode->status.owner; + inode->i_gid = 0; + inode->i_rdev = NODEV; + inode->i_size = vnode->status.size; + inode->i_atime = inode->i_mtime = inode->i_ctime = vnode->status.mtime_server; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_version = vnode->fid.unique; + inode->i_mapping->a_ops = &afs_fs_aops; + + /* check to see whether a symbolic link is really a mountpoint */ + if (vnode->status.type==AFS_FTYPE_SYMLINK) { + afs_mntpt_check_symlink(vnode); + + if (vnode->flags & AFS_VNODE_MOUNTPOINT) { + inode->i_mode = S_IFDIR | vnode->status.mode; + inode->i_op = &afs_mntpt_inode_operations; + inode->i_fop = &afs_mntpt_file_operations; + } + } + + return 0; +} /* end afs_inode_map_status() */ + +/*****************************************************************************/ +/* + * attempt to fetch the status of an inode, coelescing multiple simultaneous fetches + */ +int afs_inode_fetch_status(struct inode *inode) +{ + afs_vnode_t *vnode; + int ret; + + vnode = AFS_FS_I(inode); + + ret = afs_vnode_fetch_status(vnode); + + if (ret==0) + ret = afs_inode_map_status(vnode); + + return ret; + +} /* end afs_inode_fetch_status() */ + +/*****************************************************************************/ +/* + * iget5() comparator + */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static int afs_iget5_test(struct inode *inode, void *opaque) +{ + struct afs_iget_data *data = opaque; + + /* only match inodes with the same version number */ + return inode->i_ino==data->fid.vnode && inode->i_version==data->fid.unique; +} /* end afs_iget5_test() */ +#endif + +/*****************************************************************************/ +/* + * iget5() inode initialiser + */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static int afs_iget5_set(struct inode *inode, void *opaque) +{ + struct afs_iget_data *data = opaque; + afs_vnode_t *vnode = AFS_FS_I(inode); + + inode->i_ino = data->fid.vnode; + inode->i_version = data->fid.unique; + vnode->fid = data->fid; + vnode->volume = data->volume; + + return 0; +} /* end afs_iget5_set() */ +#endif + +/*****************************************************************************/ +/* + * iget4() comparator + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +static int afs_iget4_test(struct inode *inode, ino_t ino, void *opaque) +{ + struct afs_iget_data *data = opaque; + + /* only match inodes with the same version number */ + return inode->i_ino==data->fid.vnode && inode->i_version==data->fid.unique; +} /* end afs_iget4_test() */ +#endif + +/*****************************************************************************/ +/* + * read an inode (2.4 only) + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +void afs_read_inode2(struct inode *inode, void *opaque) +{ + struct afs_iget_data *data = opaque; + afs_vnode_t *vnode; + int ret; + + kenter(",{{%u,%u,%u},%p}",data->fid.vid,data->fid.vnode,data->fid.unique,data->volume); + + if (inode->u.generic_ip) BUG(); + + /* attach a pre-allocated vnode record */ + inode->u.generic_ip = vnode = data->new_vnode; + data->new_vnode = NULL; + + memset(vnode,0,sizeof(*vnode)); + vnode->inode = inode; + init_waitqueue_head(&vnode->update_waitq); + spin_lock_init(&vnode->lock); + INIT_LIST_HEAD(&vnode->cb_link); + INIT_LIST_HEAD(&vnode->cb_hash_link); + afs_timer_init(&vnode->cb_timeout,&afs_vnode_cb_timed_out_ops); + vnode->flags |= AFS_VNODE_CHANGED; + vnode->volume = data->volume; + vnode->fid = data->fid; + + /* ask the server for a status check */ + ret = afs_vnode_fetch_status(vnode); + if (ret<0) { + make_bad_inode(inode); + kleave(" [bad inode]"); + return; + } + + ret = afs_inode_map_status(vnode); + if (ret<0) { + make_bad_inode(inode); + kleave(" [bad inode]"); + return; + } + + kleave(""); + return; +} /* end afs_read_inode2() */ +#endif + +/*****************************************************************************/ +/* + * inode retrieval + */ +inline int afs_iget(struct super_block *sb, afs_fid_t *fid, struct inode **_inode) +{ + struct afs_iget_data data = { fid: *fid }; + struct afs_super_info *as; + struct inode *inode; + afs_vnode_t *vnode; + int ret; + + kenter(",{%u,%u,%u},,",fid->vid,fid->vnode,fid->unique); + + as = sb->u.generic_sbp; + data.volume = as->volume; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + inode = iget5_locked(sb,fid->vnode,afs_iget5_test,afs_iget5_set,&data); + if (!inode) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + vnode = AFS_FS_I(inode); + + /* deal with an existing inode */ + if (!(inode->i_state & I_NEW)) { + ret = afs_vnode_fetch_status(vnode); + if (ret==0) + *_inode = inode; + else + iput(inode); + _leave(" = %d",ret); + return ret; + } + + /* okay... it's a new inode */ + vnode->flags |= AFS_VNODE_CHANGED; + ret = afs_inode_fetch_status(inode); + if (ret<0) + goto bad_inode; + +#if 0 + /* find a cache entry for it */ + ret = afs_cache_lookup_vnode(as->volume,vnode); + if (ret<0) + goto bad_inode; +#endif + + /* success */ + unlock_new_inode(inode); + + *_inode = inode; + _leave(" = 0 [CB { v=%u x=%lu t=%u nix=%u }]", + vnode->cb_version, + vnode->cb_timeout.timo_jif, + vnode->cb_type, + vnode->nix + ); + return 0; + + /* failure */ + bad_inode: + make_bad_inode(inode); + unlock_new_inode(inode); + iput(inode); + + _leave(" = %d [bad]",ret); + return ret; + +#else + + /* pre-allocate a vnode record so that afs_read_inode2() doesn't have to return an inode + * without one attached + */ + data.new_vnode = kmalloc(sizeof(afs_vnode_t),GFP_KERNEL); + if (!data.new_vnode) { + kleave(" = -ENOMEM"); + return -ENOMEM; + } + + inode = iget4(sb,fid->vnode,afs_iget4_test,&data); + if (data.new_vnode) kfree(data.new_vnode); + if (!inode) { + kleave(" = -ENOMEM"); + return -ENOMEM; + } + + vnode = AFS_FS_I(inode); + *_inode = inode; + kleave(" = 0 [CB { v=%u x=%lu t=%u nix=%u }]", + vnode->cb_version, + vnode->cb_timeout.timo_jif, + vnode->cb_type, + vnode->nix + ); + return 0; +#endif +} /* end afs_iget() */ + +/*****************************************************************************/ +/* + * read the attributes of an inode + */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode; + afs_vnode_t *vnode; + int ret; + + inode = dentry->d_inode; + + _enter("{ ino=%lu v=%lu }",inode->i_ino,inode->i_version); + + vnode = AFS_FS_I(inode); + + ret = afs_inode_fetch_status(inode); + if (ret==-ENOENT) { + _leave(" = %d [%d %p]",ret,atomic_read(&dentry->d_count),dentry->d_inode); + return ret; + } + else if (ret<0) { + make_bad_inode(inode); + _leave(" = %d",ret); + return ret; + } + + /* transfer attributes from the inode structure to the stat structure */ + generic_fillattr(inode,stat); + + _leave(" = 0 CB { v=%u x=%u t=%u }", + vnode->callback.version, + vnode->callback.expiry, + vnode->callback.type); + + return 0; +} /* end afs_inode_getattr() */ +#endif + +/*****************************************************************************/ +/* + * revalidate the inode + */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) +int afs_inode_revalidate(struct dentry *dentry) +{ + struct inode *inode; + afs_vnode_t *vnode; + int ret; + + inode = dentry->d_inode; + + _enter("{ ino=%lu v=%lu }",inode->i_ino,inode->i_version); + + vnode = AFS_FS_I(inode); + + ret = afs_inode_fetch_status(inode); + if (ret==-ENOENT) { + _leave(" = %d [%d %p]",ret,atomic_read(&dentry->d_count),dentry->d_inode); + return ret; + } + else if (ret<0) { + make_bad_inode(inode); + _leave(" = %d",ret); + return ret; + } + + _leave(" = 0 CB { v=%u x=%u t=%u }", + vnode->cb_version, + vnode->cb_expiry, + vnode->cb_type); + + return 0; +} /* end afs_inode_revalidate() */ +#endif + +/*****************************************************************************/ +/* + * clear an AFS inode + */ +void afs_clear_inode(struct inode *inode) +{ + afs_vnode_t *vnode; + + vnode = AFS_FS_I(inode); + + _enter("(ino=%lu { v=%u x=%u t=%u })", + inode->i_ino, + vnode->cb_version, + vnode->cb_expiry, + vnode->cb_type + ); + + afs_vnode_give_up_callback(vnode); + + _leave(""); +} /* end afs_clear_inode() */ diff -urNp linux-5240/fs/afs/internal.h linux-5250/fs/afs/internal.h --- linux-5240/fs/afs/internal.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/internal.h @@ -0,0 +1,115 @@ +/* internal.h: internal AFS stuff + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef AFS_INTERNAL_H +#define AFS_INTERNAL_H + +#include +#include +#include +#include + +/* + * debug tracing + */ +#define kenter(FMT,...) printk("==> %s("FMT")\n",__FUNCTION__,##__VA_ARGS__) +#define kleave(FMT,...) printk("<== %s()"FMT"\n",__FUNCTION__,##__VA_ARGS__) +#define kdebug(FMT,...) printk(FMT"\n",##__VA_ARGS__) +#define kproto(FMT,...) printk("### "FMT"\n",##__VA_ARGS__) +#define knet(FMT,...) printk(FMT"\n",##__VA_ARGS__) + +#if 0 +#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__) +#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__) +#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__) +#define _proto(FMT,...) kproto(FMT,##__VA_ARGS__) +#define _net(FMT,...) knet(FMT,##__VA_ARGS__) +#else +#define _enter(FMT,...) do { } while(0) +#define _leave(FMT,...) do { } while(0) +#define _debug(FMT,...) do { } while(0) +#define _proto(FMT,...) do { } while(0) +#define _net(FMT,...) do { } while(0) +#endif + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) +#define wait_on_page_locked wait_on_page +#define PageUptodate Page_Uptodate + +static inline struct proc_dir_entry *PDE(const struct inode *inode) +{ + return (struct proc_dir_entry *)inode->u.generic_ip; +} +#endif + +/* + * cell.c + */ +extern struct rw_semaphore afs_proc_cells_sem; +extern struct list_head afs_proc_cells; + +/* + * dir.c + */ +extern struct inode_operations afs_dir_inode_operations; +extern struct file_operations afs_dir_file_operations; + +/* + * file.c + */ +extern struct address_space_operations afs_fs_aops; +extern struct inode_operations afs_file_inode_operations; +extern struct file_operations afs_file_file_operations; + +/* + * inode.c + */ +extern int afs_iget(struct super_block *sb, afs_fid_t *fid, struct inode **_inode); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +extern int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); +#else +extern void afs_read_inode2(struct inode *inode, void *opaque); +extern int afs_inode_revalidate(struct dentry *dentry); +#endif +extern void afs_clear_inode(struct inode *inode); + +/* + * mntpt.c + */ +extern struct inode_operations afs_mntpt_inode_operations; +extern struct file_operations afs_mntpt_file_operations; + +extern int afs_mntpt_check_symlink(afs_vnode_t *vnode); + +/* + * super.c + */ +extern int afs_fs_init(void); +extern void afs_fs_exit(void); + +#define AFS_CB_HASH_COUNT (PAGE_SIZE/sizeof(struct list_head)) + +extern struct list_head afs_cb_hash_tbl[]; +extern spinlock_t afs_cb_hash_lock; + +#define afs_cb_hash(SRV,FID) \ + afs_cb_hash_tbl[((unsigned)(SRV) + (FID)->vid + (FID)->vnode + (FID)->unique) % \ + AFS_CB_HASH_COUNT] + +/* + * proc.c + */ +extern int afs_proc_init(void); +extern void afs_proc_cleanup(void); +extern int afs_proc_cell_setup(afs_cell_t *cell); +extern void afs_proc_cell_remove(afs_cell_t *cell); + +#endif /* AFS_INTERNAL_H */ diff -urNp linux-5240/fs/afs/kafsasyncd.c linux-5250/fs/afs/kafsasyncd.c --- linux-5240/fs/afs/kafsasyncd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/kafsasyncd.c @@ -0,0 +1,266 @@ +/* kafsasyncd.c: AFS asynchronous operation daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * + * The AFS async daemon is used to the following: + * - probe "dead" servers to see whether they've come back to life yet. + * - probe "live" servers that we haven't talked to for a while to see if they are better + * candidates for serving than what we're currently using + * - poll volume location servers to keep up to date volume location lists + */ + +#include +#include +#include +#include +#include +#include "cell.h" +#include "server.h" +#include "volume.h" +#include "kafsasyncd.h" +#include "kafstimod.h" +#include +#include +#include "internal.h" + +static DECLARE_COMPLETION(kafsasyncd_alive); +static DECLARE_COMPLETION(kafsasyncd_dead); +static DECLARE_WAIT_QUEUE_HEAD(kafsasyncd_sleepq); +static struct task_struct *kafsasyncd_task; +static int kafsasyncd_die; + +static int kafsasyncd(void *arg); + +static LIST_HEAD(kafsasyncd_async_attnq); +static LIST_HEAD(kafsasyncd_async_busyq); +static spinlock_t kafsasyncd_async_lock = SPIN_LOCK_UNLOCKED; + +static void kafsasyncd_null_call_attn_func(struct rxrpc_call *call) +{ +} + +static void kafsasyncd_null_call_error_func(struct rxrpc_call *call) +{ +} + +/*****************************************************************************/ +/* + * start the async daemon + */ +int afs_kafsasyncd_start(void) +{ + int ret; + + ret = kernel_thread(kafsasyncd,NULL,0); + if (ret<0) + return ret; + + wait_for_completion(&kafsasyncd_alive); + + return ret; +} /* end afs_kafsasyncd_start() */ + +/*****************************************************************************/ +/* + * stop the async daemon + */ +void afs_kafsasyncd_stop(void) +{ + /* get rid of my daemon */ + kafsasyncd_die = 1; + wake_up(&kafsasyncd_sleepq); + wait_for_completion(&kafsasyncd_dead); + +} /* end afs_kafsasyncd_stop() */ + +/*****************************************************************************/ +/* + * probing daemon + */ +static int kafsasyncd(void *arg) +{ + DECLARE_WAITQUEUE(myself,current); + + struct list_head *_p; + int die; + + kafsasyncd_task = current; + + printk("kAFS: Started kafsasyncd %d\n",current->pid); + strcpy(current->comm,"kafsasyncd"); + + daemonize(); + + complete(&kafsasyncd_alive); + + /* only certain signals are of interest */ + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked,0); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3) + recalc_sigpending(); +#else + recalc_sigpending(current); +#endif + spin_unlock_irq(¤t->sigmask_lock); + + /* loop around looking for things to attend to */ + do { + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&kafsasyncd_sleepq,&myself); + + for (;;) { + if (!list_empty(&kafsasyncd_async_attnq) || + signal_pending(current) || + kafsasyncd_die) + break; + + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + + remove_wait_queue(&kafsasyncd_sleepq,&myself); + set_current_state(TASK_RUNNING); + + /* discard pending signals */ + while (signal_pending(current)) { + siginfo_t sinfo; + + spin_lock_irq(¤t->sigmask_lock); + dequeue_signal(¤t->blocked,&sinfo); + spin_unlock_irq(¤t->sigmask_lock); + } + + die = kafsasyncd_die; + + /* deal with the next asynchronous operation requiring attention */ + if (!list_empty(&kafsasyncd_async_attnq)) { + struct afs_async_op *op; + + _debug("@@@ Begin Asynchronous Operation"); + + op = NULL; + spin_lock(&kafsasyncd_async_lock); + + if (!list_empty(&kafsasyncd_async_attnq)) { + op = list_entry(kafsasyncd_async_attnq.next,afs_async_op_t,link); + list_del(&op->link); + list_add_tail(&op->link,&kafsasyncd_async_busyq); + } + + spin_unlock(&kafsasyncd_async_lock); + + _debug("@@@ Operation %p {%p}\n",op,op?op->ops:NULL); + + if (op) + op->ops->attend(op); + + _debug("@@@ End Asynchronous Operation"); + } + + } while(!die); + + /* need to kill all outstanding asynchronous operations before exiting */ + kafsasyncd_task = NULL; + spin_lock(&kafsasyncd_async_lock); + + /* fold the busy and attention queues together */ + list_splice(&kafsasyncd_async_busyq,&kafsasyncd_async_attnq); + list_del_init(&kafsasyncd_async_busyq); + + /* dequeue kafsasyncd from all their wait queues */ + list_for_each(_p,&kafsasyncd_async_attnq) { + afs_async_op_t *op = list_entry(_p,afs_async_op_t,link); + + op->call->app_attn_func = kafsasyncd_null_call_attn_func; + op->call->app_error_func = kafsasyncd_null_call_error_func; + remove_wait_queue(&op->call->waitq,&op->waiter); + } + + spin_unlock(&kafsasyncd_async_lock); + + /* abort all the operations */ + while (!list_empty(&kafsasyncd_async_attnq)) { + afs_async_op_t *op = list_entry(_p,afs_async_op_t,link); + list_del_init(&op->link); + + rxrpc_call_abort(op->call,-EIO); + rxrpc_put_call(op->call); + op->call = NULL; + + op->ops->discard(op); + } + + /* and that's all */ + _leave(""); + complete_and_exit(&kafsasyncd_dead,0); + +} /* end kafsasyncd() */ + +/*****************************************************************************/ +/* + * begin an operation + * - place operation on busy queue + */ +void afs_kafsasyncd_begin_op(afs_async_op_t *op) +{ + _enter(""); + + spin_lock(&kafsasyncd_async_lock); + + init_waitqueue_entry(&op->waiter,kafsasyncd_task); + + list_del(&op->link); + list_add_tail(&op->link,&kafsasyncd_async_busyq); + + spin_unlock(&kafsasyncd_async_lock); + + _leave(""); +} /* end afs_kafsasyncd_begin_op() */ + +/*****************************************************************************/ +/* + * request attention for an operation + * - move to attention queue + */ +void afs_kafsasyncd_attend_op(afs_async_op_t *op) +{ + _enter(""); + + spin_lock(&kafsasyncd_async_lock); + + list_del(&op->link); + list_add_tail(&op->link,&kafsasyncd_async_attnq); + + spin_unlock(&kafsasyncd_async_lock); + + wake_up(&kafsasyncd_sleepq); + + _leave(""); +} /* end afs_kafsasyncd_attend_op() */ + +/*****************************************************************************/ +/* + * terminate an operation + * - remove from either queue + */ +void afs_kafsasyncd_terminate_op(afs_async_op_t *op) +{ + _enter(""); + + spin_lock(&kafsasyncd_async_lock); + + list_del_init(&op->link); + + spin_unlock(&kafsasyncd_async_lock); + + wake_up(&kafsasyncd_sleepq); + + _leave(""); +} /* end afs_kafsasyncd_terminate_op() */ diff -urNp linux-5240/fs/afs/kafsasyncd.h linux-5250/fs/afs/kafsasyncd.h --- linux-5240/fs/afs/kafsasyncd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/kafsasyncd.h @@ -0,0 +1,49 @@ +/* kafsasyncd.h: AFS asynchronous operation daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_KAFSASYNCD_H +#define _LINUX_AFS_KAFSASYNCD_H + +#include "types.h" + +struct afs_async_op_ops { + void (*attend)(afs_async_op_t *op); + void (*discard)(afs_async_op_t *op); +}; + +/*****************************************************************************/ +/* + * asynchronous operation record + */ +struct afs_async_op +{ + struct list_head link; + afs_server_t *server; /* server being contacted */ + struct rxrpc_call *call; /* RxRPC call performing op */ + wait_queue_t waiter; /* wait queue for kafsasyncd */ + const struct afs_async_op_ops *ops; /* operations */ +}; + +static inline void afs_async_op_init(afs_async_op_t *op, const struct afs_async_op_ops *ops) +{ + INIT_LIST_HEAD(&op->link); + op->call = NULL; + op->ops = ops; +} + +extern int afs_kafsasyncd_start(void); +extern void afs_kafsasyncd_stop(void); + +extern void afs_kafsasyncd_begin_op(afs_async_op_t *op); +extern void afs_kafsasyncd_attend_op(afs_async_op_t *op); +extern void afs_kafsasyncd_terminate_op(afs_async_op_t *op); + +#endif /* _LINUX_AFS_KAFSASYNCD_H */ diff -urNp linux-5240/fs/afs/kafstimod.c linux-5250/fs/afs/kafstimod.c --- linux-5240/fs/afs/kafstimod.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/kafstimod.c @@ -0,0 +1,217 @@ +/* kafstimod.c: AFS timeout daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "cell.h" +#include "volume.h" +#include "kafstimod.h" +#include +#include "internal.h" + +static DECLARE_COMPLETION(kafstimod_alive); +static DECLARE_COMPLETION(kafstimod_dead); +static DECLARE_WAIT_QUEUE_HEAD(kafstimod_sleepq); +static int kafstimod_die; + +static LIST_HEAD(kafstimod_list); +static spinlock_t kafstimod_lock = SPIN_LOCK_UNLOCKED; + +static int kafstimod(void *arg); + +/*****************************************************************************/ +/* + * start the timeout daemon + */ +int afs_kafstimod_start(void) +{ + int ret; + + ret = kernel_thread(kafstimod,NULL,0); + if (ret<0) + return ret; + + wait_for_completion(&kafstimod_alive); + + return ret; +} /* end afs_kafstimod_start() */ + +/*****************************************************************************/ +/* + * stop the timeout daemon + */ +void afs_kafstimod_stop(void) +{ + /* get rid of my daemon */ + kafstimod_die = 1; + wake_up(&kafstimod_sleepq); + wait_for_completion(&kafstimod_dead); + +} /* end afs_kafstimod_stop() */ + +/*****************************************************************************/ +/* + * timeout processing daemon + */ +static int kafstimod(void *arg) +{ + DECLARE_WAITQUEUE(myself,current); + + afs_timer_t *timer; + + printk("kAFS: Started kafstimod %d\n",current->pid); + strcpy(current->comm,"kafstimod"); + + daemonize(); + + complete(&kafstimod_alive); + + /* only certain signals are of interest */ + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked,0); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3) + recalc_sigpending(); +#else + recalc_sigpending(current); +#endif + spin_unlock_irq(¤t->sigmask_lock); + + /* loop around looking for things to attend to */ + loop: + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&kafstimod_sleepq,&myself); + + for (;;) { + unsigned long jif; + signed long timeout; + + /* deal with the server being asked to die */ + if (kafstimod_die) { + remove_wait_queue(&kafstimod_sleepq,&myself); + _leave(""); + complete_and_exit(&kafstimod_dead,0); + } + + /* discard pending signals */ + while (signal_pending(current)) { + siginfo_t sinfo; + + spin_lock_irq(¤t->sigmask_lock); + dequeue_signal(¤t->blocked,&sinfo); + spin_unlock_irq(¤t->sigmask_lock); + } + + /* work out the time to elapse before the next event */ + spin_lock(&kafstimod_lock); + if (list_empty(&kafstimod_list)) { + timeout = MAX_SCHEDULE_TIMEOUT; + } + else { + timer = list_entry(kafstimod_list.next,afs_timer_t,link); + timeout = timer->timo_jif; + jif = jiffies; + + if (time_before_eq(timeout,jif)) + goto immediate; + + else { + timeout = (long)timeout - (long)jiffies; + } + } + spin_unlock(&kafstimod_lock); + + schedule_timeout(timeout); + + set_current_state(TASK_INTERRUPTIBLE); + } + + /* the thing on the front of the queue needs processing + * - we come here with the lock held and timer pointing to the expired entry + */ + immediate: + remove_wait_queue(&kafstimod_sleepq,&myself); + set_current_state(TASK_RUNNING); + + _debug("@@@ Begin Timeout of %p",timer); + + /* dequeue the timer */ + list_del_init(&timer->link); + spin_unlock(&kafstimod_lock); + + /* call the timeout function */ + timer->ops->timed_out(timer); + + _debug("@@@ End Timeout"); + goto loop; + +} /* end kafstimod() */ + +/*****************************************************************************/ +/* + * (re-)queue a timer + */ +void afs_kafstimod_add_timer(afs_timer_t *timer, unsigned long timeout) +{ + struct list_head *_p; + afs_timer_t *ptimer; + + _enter("%p,%lu",timer,timeout); + + spin_lock(&kafstimod_lock); + + list_del(&timer->link); + + /* the timer was deferred or reset - put it back in the queue at the right place */ + timer->timo_jif = jiffies + timeout; + + list_for_each(_p,&kafstimod_list) { + ptimer = list_entry(_p,afs_timer_t,link); + if (time_before(timer->timo_jif,ptimer->timo_jif)) + break; + } + + list_add_tail(&timer->link,_p); /* insert before stopping point */ + + spin_unlock(&kafstimod_lock); + + wake_up(&kafstimod_sleepq); + + _leave(""); +} /* end afs_kafstimod_queue_vlocation() */ + +/*****************************************************************************/ +/* + * dequeue a timer + * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued + */ +int afs_kafstimod_del_timer(afs_timer_t *timer) +{ + int ret = 0; + + _enter("%p",timer); + + spin_lock(&kafstimod_lock); + + if (list_empty(&timer->link)) + ret = -ENOENT; + else + list_del_init(&timer->link); + + spin_unlock(&kafstimod_lock); + + wake_up(&kafstimod_sleepq); + + _leave(" = %d",ret); + return ret; +} /* end afs_kafstimod_del_timer() */ diff -urNp linux-5240/fs/afs/kafstimod.h linux-5250/fs/afs/kafstimod.h --- linux-5240/fs/afs/kafstimod.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/kafstimod.h @@ -0,0 +1,45 @@ +/* kafstimod.h: AFS timeout daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_KAFSTIMOD_H +#define _LINUX_AFS_KAFSTIMOD_H + +#include "types.h" + +struct afs_timer_ops { + /* called when the front of the timer queue has timed out */ + void (*timed_out)(struct afs_timer *timer); +}; + +/*****************************************************************************/ +/* + * AFS timer/timeout record + */ +struct afs_timer +{ + struct list_head link; /* link in timer queue */ + unsigned long timo_jif; /* timeout time */ + const struct afs_timer_ops *ops; /* timeout expiry function */ +}; + +static inline void afs_timer_init(afs_timer_t *timer, const struct afs_timer_ops *ops) +{ + INIT_LIST_HEAD(&timer->link); + timer->ops = ops; +} + +extern int afs_kafstimod_start(void); +extern void afs_kafstimod_stop(void); + +extern void afs_kafstimod_add_timer(afs_timer_t *timer, unsigned long timeout); +extern int afs_kafstimod_del_timer(afs_timer_t *timer); + +#endif /* _LINUX_AFS_KAFSTIMOD_H */ diff -urNp linux-5240/fs/afs/main.c linux-5250/fs/afs/main.c --- linux-5240/fs/afs/main.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/main.c @@ -0,0 +1,193 @@ +/* main.c: AFS client file system + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cell.h" +#include "server.h" +#include "fsclient.h" +#include "cmservice.h" +#include "kafstimod.h" +#include "kafsasyncd.h" +#include "internal.h" + +struct rxrpc_transport *afs_transport; + +static int afs_init(void); +static void afs_exit(void); +static int afs_adding_peer(struct rxrpc_peer *peer); +static void afs_discarding_peer(struct rxrpc_peer *peer); + +module_init(afs_init); +module_exit(afs_exit); + +MODULE_DESCRIPTION("AFS Client File System"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +static struct rxrpc_peer_ops afs_peer_ops = { + adding: afs_adding_peer, + discarding: afs_discarding_peer, +}; + +struct list_head afs_cb_hash_tbl[AFS_CB_HASH_COUNT]; +spinlock_t afs_cb_hash_lock = SPIN_LOCK_UNLOCKED; + +/*****************************************************************************/ +/* + * initialise the AFS client FS module + */ +static int afs_init(void) +{ + int loop, ret; + + printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n"); + + /* initialise the callback hash table */ + spin_lock_init(&afs_cb_hash_lock); + for (loop=AFS_CB_HASH_COUNT-1; loop>=0; loop--) + INIT_LIST_HEAD(&afs_cb_hash_tbl[loop]); + + /* register the /proc stuff */ + ret = afs_proc_init(); + if (ret<0) + return ret; + + /* initialise the cell DB */ + ret = afs_cell_init(); + if (ret<0) + goto error; + + /* start the timeout daemon */ + ret = afs_kafstimod_start(); + if (ret<0) + goto error; + + /* start the async operation daemon */ + ret = afs_kafsasyncd_start(); + if (ret<0) + goto error_kafstimod; + + /* create the RxRPC transport */ + ret = rxrpc_create_transport(7001,&afs_transport); + if (ret<0) + goto error_kafsasyncd; + + afs_transport->peer_ops = &afs_peer_ops; + + /* register the filesystems */ + ret = afs_fs_init(); + if (ret<0) + goto error_transport; + + return ret; + + error_transport: + rxrpc_put_transport(afs_transport); + error_kafsasyncd: + afs_kafsasyncd_stop(); + error_kafstimod: + afs_kafstimod_stop(); + error: + afs_cell_purge(); + afs_proc_cleanup(); + printk(KERN_ERR "kAFS: failed to register: %d\n",ret); + return ret; +} /* end afs_init() */ + +/*****************************************************************************/ +/* + * clean up on module removal + */ +static void afs_exit(void) +{ + printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n"); + + afs_fs_exit(); + rxrpc_put_transport(afs_transport); + afs_kafstimod_stop(); + afs_kafsasyncd_stop(); + afs_cell_purge(); + afs_proc_cleanup(); + +} /* end afs_exit() */ + +/*****************************************************************************/ +/* + * notification that new peer record is being added + * - called from krxsecd + * - return an error to induce an abort + * - mustn't sleep (caller holds an rwlock) + */ +static int afs_adding_peer(struct rxrpc_peer *peer) +{ + afs_server_t *server; + int ret; + + _debug("kAFS: Adding new peer %08x\n",ntohl(peer->addr.s_addr)); + + /* determine which server the peer resides in (if any) */ + ret = afs_server_find_by_peer(peer,&server); + if (ret<0) + return ret; /* none that we recognise, so abort */ + + _debug("Server %p{u=%d}\n",server,atomic_read(&server->usage)); + + _debug("Cell %p{u=%d}\n",server->cell,atomic_read(&server->cell->usage)); + + /* cross-point the structs under a global lock */ + spin_lock(&afs_server_peer_lock); + peer->user = server; + server->peer = peer; + spin_unlock(&afs_server_peer_lock); + + afs_put_server(server); + + return 0; +} /* end afs_adding_peer() */ + +/*****************************************************************************/ +/* + * notification that a peer record is being discarded + * - called from krxiod or krxsecd + */ +static void afs_discarding_peer(struct rxrpc_peer *peer) +{ + afs_server_t *server; + + _enter("%p",peer); + + _debug("Discarding peer %08x (rtt=%lu.%lumS)\n", + ntohl(peer->addr.s_addr), + peer->rtt/1000, + peer->rtt%1000); + + /* uncross-point the structs under a global lock */ + spin_lock(&afs_server_peer_lock); + server = peer->user; + if (server) { + peer->user = NULL; + server->peer = NULL; + + //_debug("Server %p{u=%d}\n",server,atomic_read(&server->usage)); + //_debug("Cell %p{u=%d}\n",server->cell,atomic_read(&server->cell->usage)); + } + spin_unlock(&afs_server_peer_lock); + + _leave(""); + +} /* end afs_discarding_peer() */ diff -urNp linux-5240/fs/afs/Makefile linux-5250/fs/afs/Makefile --- linux-5240/fs/afs/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/Makefile @@ -0,0 +1,34 @@ +# +# Makefile for Red Hat Linux AFS client. +# + +kafs-objs := \ + callback.o \ + cell.o \ + cmservice.o \ + dir.o \ + file.o \ + fsclient.o \ + inode.o \ + kafsasyncd.o \ + kafstimod.o \ + main.o \ + misc.o \ + mntpt.o \ + proc.o \ + server.o \ + super.o \ + vlclient.o \ + vlocation.o \ + vnode.o \ + volume.o + +# cache.o + +obj-m := kafs.o + +# superfluous for 2.5, but needed for 2.4.. +kafs.o: $(kafs-objs) + $(LD) -r -o kafs.o $(kafs-objs) + +include $(TOPDIR)/Rules.make diff -urNp linux-5240/fs/afs/misc.c linux-5250/fs/afs/misc.c --- linux-5240/fs/afs/misc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/misc.c @@ -0,0 +1,39 @@ +/* misc.c: miscellaneous bits + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include "errors.h" +#include "internal.h" + +/*****************************************************************************/ +/* + * convert an AFS abort code to a Linux error number + */ +int afs_abort_to_error(int abortcode) +{ + switch (abortcode) { + case VSALVAGE: return -EIO; + case VNOVNODE: return -ENOENT; + case VNOVOL: return -ENXIO; + case VVOLEXISTS: return -EEXIST; + case VNOSERVICE: return -EIO; + case VOFFLINE: return -ENOENT; + case VONLINE: return -EEXIST; + case VDISKFULL: return -ENOSPC; + case VOVERQUOTA: return -EDQUOT; + case VBUSY: return -EBUSY; + case VMOVED: return -ENXIO; + default: return -EIO; + } + +} /* end afs_abort_to_error() */ diff -urNp linux-5240/fs/afs/mntpt.c linux-5250/fs/afs/mntpt.c --- linux-5240/fs/afs/mntpt.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/mntpt.c @@ -0,0 +1,114 @@ +/* mntpt.c: mountpoint management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "volume.h" +#include "vnode.h" +#include "cache.h" +#include "internal.h" + + +static struct dentry *afs_mntpt_lookup(struct inode *dir, struct dentry *dentry); +static int afs_mntpt_open(struct inode *inode, struct file *file); +//static int afs_mntpt_readlink(struct dentry *dentry, char *buf, int size); + +struct file_operations afs_mntpt_file_operations = { + open: afs_mntpt_open, +}; + +struct inode_operations afs_mntpt_inode_operations = { + lookup: afs_mntpt_lookup, + readlink: page_readlink, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + getattr: afs_inode_getattr, +#else + revalidate: afs_inode_revalidate, +#endif +}; + +/*****************************************************************************/ +/* + * check a symbolic link to see whether it actually encodes a mountpoint + * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately + */ +int afs_mntpt_check_symlink(afs_vnode_t *vnode) +{ + struct page *page; + size_t size; + char *buf; + int ret; + + _enter("{%u,%u}",vnode->fid.vnode,vnode->fid.unique); + + /* read the contents of the symlink into the pagecache */ + page = read_cache_page(AFS_VNODE_TO_I(vnode)->i_mapping,0, + (filler_t*)AFS_VNODE_TO_I(vnode)->i_mapping->a_ops->readpage,NULL); + if (IS_ERR(page)) { + ret = PTR_ERR(page); + goto out; + } + + ret = -EIO; + wait_on_page_locked(page); + buf = kmap(page); + if (!PageUptodate(page)) + goto out_free; + if (PageError(page)) + goto out_free; + + /* examine the symlink's contents */ + size = vnode->status.size; + _debug("symlink to %*.*s",size,size,buf); + + if (size>2 && + (buf[0]=='%' || buf[0]=='#') && + buf[size-1]=='.' + ) { + _debug("symlink is a mountpoint"); + spin_lock(&vnode->lock); + vnode->flags |= AFS_VNODE_MOUNTPOINT; + spin_unlock(&vnode->lock); + } + + ret = 0; + + out_free: + kunmap(page); + page_cache_release(page); + out: + _leave(" = %d",ret); + return ret; + +} /* end afs_mntpt_check_symlink() */ + +/*****************************************************************************/ +/* + * no valid lookup procedure on this sort of dir + */ +static struct dentry *afs_mntpt_lookup(struct inode *dir, struct dentry *dentry) +{ + return ERR_PTR(-EREMOTE); +} /* end afs_mntpt_lookup() */ + +/*****************************************************************************/ +/* + * no valid open procedure on this sort of dir + */ +static int afs_mntpt_open(struct inode *inode, struct file *file) +{ + return -EREMOTE; +} /* end afs_mntpt_open() */ diff -urNp linux-5240/fs/afs/mount.h linux-5250/fs/afs/mount.h --- linux-5240/fs/afs/mount.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/mount.h @@ -0,0 +1,23 @@ +/* mount.h: mount parameters + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_MOUNT_H +#define _LINUX_AFS_MOUNT_H + +struct afs_mountdata { + const char *volume; /* name of volume */ + const char *cell; /* name of cell containing volume */ + const char *cache; /* name of cache block device */ + size_t nservers; /* number of server addresses listed */ + u_int32_t servers[10]; /* IP addresses of servers in this cell */ +}; + +#endif /* _LINUX_AFS_MOUNT_H */ diff -urNp linux-5240/fs/afs/proc.c linux-5250/fs/afs/proc.c --- linux-5240/fs/afs/proc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/proc.c @@ -0,0 +1,744 @@ +/* proc.c: /proc interface for AFS + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "cell.h" +#include "volume.h" +#include +#include "internal.h" + +static struct proc_dir_entry *proc_afs; + + +static int afs_proc_cells_open(struct inode *inode, struct file *file); +static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos); +static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos); +static void afs_proc_cells_stop(struct seq_file *p, void *v); +static int afs_proc_cells_show(struct seq_file *m, void *v); +static ssize_t afs_proc_cells_write(struct file *file, const char *buf, size_t size, loff_t *_pos); + +static struct seq_operations afs_proc_cells_ops = { + start: afs_proc_cells_start, + next: afs_proc_cells_next, + stop: afs_proc_cells_stop, + show: afs_proc_cells_show, +}; + +static struct file_operations afs_proc_cells_fops = { + open: afs_proc_cells_open, + read: seq_read, + write: afs_proc_cells_write, + llseek: seq_lseek, + release: seq_release, +}; + +static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file); +static int afs_proc_cell_volumes_release(struct inode *inode, struct file *file); +static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos); +static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, loff_t *pos); +static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v); +static int afs_proc_cell_volumes_show(struct seq_file *m, void *v); + +static struct seq_operations afs_proc_cell_volumes_ops = { + start: afs_proc_cell_volumes_start, + next: afs_proc_cell_volumes_next, + stop: afs_proc_cell_volumes_stop, + show: afs_proc_cell_volumes_show, +}; + +static struct file_operations afs_proc_cell_volumes_fops = { + open: afs_proc_cell_volumes_open, + read: seq_read, + llseek: seq_lseek, + release: afs_proc_cell_volumes_release, +}; + +static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file); +static int afs_proc_cell_vlservers_release(struct inode *inode, struct file *file); +static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos); +static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, loff_t *pos); +static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v); +static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v); + +static struct seq_operations afs_proc_cell_vlservers_ops = { + start: afs_proc_cell_vlservers_start, + next: afs_proc_cell_vlservers_next, + stop: afs_proc_cell_vlservers_stop, + show: afs_proc_cell_vlservers_show, +}; + +static struct file_operations afs_proc_cell_vlservers_fops = { + open: afs_proc_cell_vlservers_open, + read: seq_read, + llseek: seq_lseek, + release: afs_proc_cell_vlservers_release, +}; + +static int afs_proc_cell_servers_open(struct inode *inode, struct file *file); +static int afs_proc_cell_servers_release(struct inode *inode, struct file *file); +static void *afs_proc_cell_servers_start(struct seq_file *p, loff_t *pos); +static void *afs_proc_cell_servers_next(struct seq_file *p, void *v, loff_t *pos); +static void afs_proc_cell_servers_stop(struct seq_file *p, void *v); +static int afs_proc_cell_servers_show(struct seq_file *m, void *v); + +static struct seq_operations afs_proc_cell_servers_ops = { + start: afs_proc_cell_servers_start, + next: afs_proc_cell_servers_next, + stop: afs_proc_cell_servers_stop, + show: afs_proc_cell_servers_show, +}; + +static struct file_operations afs_proc_cell_servers_fops = { + open: afs_proc_cell_servers_open, + read: seq_read, + llseek: seq_lseek, + release: afs_proc_cell_servers_release, +}; + +/*****************************************************************************/ +/* + * initialise the /proc/fs/afs/ directory + */ +int afs_proc_init(void) +{ + struct proc_dir_entry *p; + + _enter(""); + + proc_afs = proc_mkdir("fs/afs",NULL); + if (!proc_afs) + goto error; + proc_afs->owner = THIS_MODULE; + + p = create_proc_entry("cells",0,proc_afs); + if (!p) + goto error_proc; + p->proc_fops = &afs_proc_cells_fops; + p->owner = THIS_MODULE; + + _leave(" = 0"); + return 0; + +#if 0 + error_cells: + remove_proc_entry("cells",proc_afs); +#endif + error_proc: + remove_proc_entry("fs/afs",NULL); + error: + _leave(" = -ENOMEM"); + return -ENOMEM; + +} /* end afs_proc_init() */ + +/*****************************************************************************/ +/* + * clean up the /proc/fs/afs/ directory + */ +void afs_proc_cleanup(void) +{ + remove_proc_entry("cells",proc_afs); + + remove_proc_entry("fs/afs",NULL); + +} /* end afs_proc_cleanup() */ + +/*****************************************************************************/ +/* + * open "/proc/fs/afs/cells" which provides a summary of extant cells + */ +static int afs_proc_cells_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file,&afs_proc_cells_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = PDE(inode)->data; + + return 0; +} /* end afs_proc_cells_open() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the cells list and return the first item + */ +static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + /* lock the list against modification */ + down_read(&afs_proc_cells_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&afs_proc_cells) + if (!pos--) + break; + + return _p!=&afs_proc_cells ? _p : NULL; +} /* end afs_proc_cells_start() */ + +/*****************************************************************************/ +/* + * move to next cell in cells list + */ +static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = v==(void*)1 ? afs_proc_cells.next : _p->next; + + return _p!=&afs_proc_cells ? _p : NULL; +} /* end afs_proc_cells_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the cells list + */ +static void afs_proc_cells_stop(struct seq_file *p, void *v) +{ + up_read(&afs_proc_cells_sem); + +} /* end afs_proc_cells_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of cell lines + */ +static int afs_proc_cells_show(struct seq_file *m, void *v) +{ + afs_cell_t *cell = list_entry(v,afs_cell_t,proc_link); + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m, "USE IX NAME\n"); + return 0; + } + + /* display one cell per line on subsequent lines */ + seq_printf(m,"%3d %3u %s\n", + atomic_read(&cell->usage), + cell->cache_ix, + cell->name + ); + + return 0; +} /* end afs_proc_cells_show() */ + +/*****************************************************************************/ +/* + * handle writes to /proc/fs/afs/cells + * - to add cells: echo "add [:][:]* + */ +static ssize_t afs_proc_cells_write(struct file *file, const char *buf, size_t size, loff_t *_pos) +{ + char *kbuf, *name, *args; + int ret; + + /* start by dragging the command into memory */ + if (size<=1 || size>=PAGE_SIZE) + return -EINVAL; + + kbuf = kmalloc(size+1,GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + ret = -EFAULT; + if (copy_from_user(kbuf,buf,size)!=0) + goto done; + kbuf[size] = 0; + + /* trim to first NL */ + name = memchr(kbuf,'\n',size); + if (name) *name = 0; + + /* split into command, name and argslist */ + name = strchr(kbuf,' '); + if (!name) goto inval; + do { *name++ = 0; } while(*name==' '); + if (!*name) goto inval; + + args = strchr(name,' '); + if (!args) goto inval; + do { *args++ = 0; } while(*args==' '); + if (!*args) goto inval; + + /* determine command to perform */ + _debug("cmd=%s name=%s args=%s",kbuf,name,args); + + if (strcmp(kbuf,"add")==0) { + afs_cell_t *cell; + ret = afs_cell_create(name,args,&cell); + if (ret<0) + goto done; + + printk("kAFS: Added new cell '%s'\n",name); + } + else { + goto inval; + } + + ret = size; + + done: + kfree(kbuf); + _leave(" = %d",ret); + return ret; + + inval: + ret = -EINVAL; + printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n"); + goto done; +} /* end afs_proc_cells_write() */ + +/*****************************************************************************/ +/* + * initialise /proc/fs/afs// + */ +int afs_proc_cell_setup(afs_cell_t *cell) +{ + struct proc_dir_entry *p; + + _enter("%p{%s}",cell,cell->name); + + cell->proc_dir = proc_mkdir(cell->name,proc_afs); + if (!cell->proc_dir) + return -ENOMEM; + + p = create_proc_entry("servers",0,cell->proc_dir); + if (!p) + goto error_proc; + p->proc_fops = &afs_proc_cell_servers_fops; + p->owner = THIS_MODULE; + p->data = cell; + + p = create_proc_entry("vlservers",0,cell->proc_dir); + if (!p) + goto error_servers; + p->proc_fops = &afs_proc_cell_vlservers_fops; + p->owner = THIS_MODULE; + p->data = cell; + + p = create_proc_entry("volumes",0,cell->proc_dir); + if (!p) + goto error_vlservers; + p->proc_fops = &afs_proc_cell_volumes_fops; + p->owner = THIS_MODULE; + p->data = cell; + + _leave(" = 0"); + return 0; + + error_vlservers: + remove_proc_entry("vlservers",cell->proc_dir); + error_servers: + remove_proc_entry("servers",cell->proc_dir); + error_proc: + remove_proc_entry(cell->name,proc_afs); + _leave(" = -ENOMEM"); + return -ENOMEM; +} /* end afs_proc_cell_setup() */ + +/*****************************************************************************/ +/* + * remove /proc/fs/afs// + */ +void afs_proc_cell_remove(afs_cell_t *cell) +{ + _enter(""); + + remove_proc_entry("volumes",cell->proc_dir); + remove_proc_entry("vlservers",cell->proc_dir); + remove_proc_entry("servers",cell->proc_dir); + remove_proc_entry(cell->name,proc_afs); + + _leave(""); +} /* end afs_proc_cell_remove() */ + +/*****************************************************************************/ +/* + * open "/proc/fs/afs//volumes" which provides a summary of extant cells + */ +static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + afs_cell_t *cell; + int ret; + + cell = afs_get_cell_maybe((afs_cell_t**)&PDE(inode)->data); + if (!cell) + return -ENOENT; + + ret = seq_open(file,&afs_proc_cell_volumes_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = cell; + + return 0; +} /* end afs_proc_cell_volumes_open() */ + +/*****************************************************************************/ +/* + * close the file and release the ref to the cell + */ +static int afs_proc_cell_volumes_release(struct inode *inode, struct file *file) +{ + afs_cell_t *cell = PDE(inode)->data; + int ret; + + ret = seq_release(inode,file); + + afs_put_cell(cell); + +} /* end afs_proc_cell_volumes_release() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the cells list and return the first item + */ +static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + afs_cell_t *cell = m->private; + loff_t pos = *_pos; + + _enter("cell=%p pos=%Ld",cell,*_pos); + + /* lock the list against modification */ + down_read(&cell->vl_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&cell->vl_list) + if (!pos--) + break; + + return _p!=&cell->vl_list ? _p : NULL; +} /* end afs_proc_cell_volumes_start() */ + +/*****************************************************************************/ +/* + * move to next cell in cells list + */ +static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, loff_t *_pos) +{ + struct list_head *_p; + afs_cell_t *cell = p->private; + + _enter("cell=%p pos=%Ld",cell,*_pos); + + (*_pos)++; + + _p = v; + _p = v==(void*)1 ? cell->vl_list.next : _p->next; + + return _p!=&cell->vl_list ? _p : NULL; +} /* end afs_proc_cell_volumes_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the cells list + */ +static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v) +{ + afs_cell_t *cell = p->private; + + up_read(&cell->vl_sem); + +} /* end afs_proc_cell_volumes_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of volume lines + */ +static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) +{ + afs_vlocation_t *vlocation = list_entry(v,afs_vlocation_t,link); + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m, "USE IX VLID[0] VLID[1] VLID[2] NAME\n"); + return 0; + } + + /* display one cell per line on subsequent lines */ + seq_printf(m,"%3d %3hu %08x %08x %08x %s\n", + atomic_read(&vlocation->usage), + vlocation->vix.index, + vlocation->vldb.vid[0], + vlocation->vldb.vid[1], + vlocation->vldb.vid[2], + vlocation->vldb.name + ); + + return 0; +} /* end afs_proc_cell_volumes_show() */ + +/*****************************************************************************/ +/* + * open "/proc/fs/afs//vlservers" which provides a list of volume location server + */ +static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + afs_cell_t *cell; + int ret; + + cell = afs_get_cell_maybe((afs_cell_t**)&PDE(inode)->data); + if (!cell) + return -ENOENT; + + ret = seq_open(file,&afs_proc_cell_vlservers_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = cell; + + return 0; +} /* end afs_proc_cell_vlservers_open() */ + +/*****************************************************************************/ +/* + * close the file and release the ref to the cell + */ +static int afs_proc_cell_vlservers_release(struct inode *inode, struct file *file) +{ + afs_cell_t *cell = PDE(inode)->data; + int ret; + + ret = seq_release(inode,file); + + afs_put_cell(cell); + +} /* end afs_proc_cell_vlservers_release() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the cells list and return the first item + */ +static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) +{ + afs_cell_t *cell = m->private; + loff_t pos = *_pos; + + _enter("cell=%p pos=%Ld",cell,*_pos); + + /* lock the list against modification */ + down_read(&cell->vl_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + if (pos>=cell->vl_naddrs) + return NULL; + + return &cell->vl_addrs[pos]; +} /* end afs_proc_cell_vlservers_start() */ + +/*****************************************************************************/ +/* + * move to next cell in cells list + */ +static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, loff_t *_pos) +{ + afs_cell_t *cell = p->private; + loff_t pos; + + _enter("cell=%p{nad=%u} pos=%Ld",cell,cell->vl_naddrs,*_pos); + + pos = *_pos; + (*_pos)++; + if (pos>=cell->vl_naddrs) + return NULL; + + return &cell->vl_addrs[pos]; +} /* end afs_proc_cell_vlservers_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the cells list + */ +static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v) +{ + afs_cell_t *cell = p->private; + + up_read(&cell->vl_sem); + +} /* end afs_proc_cell_vlservers_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of volume lines + */ +static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v) +{ + struct in_addr *addr = v; + + /* display header on line 1 */ + if (v == (struct in_addr *)1) { + seq_puts(m,"ADDRESS\n"); + return 0; + } + + /* display one cell per line on subsequent lines */ + seq_printf(m,"%u.%u.%u.%u\n",NIPQUAD(addr->s_addr)); + + return 0; +} /* end afs_proc_cell_vlservers_show() */ + +/*****************************************************************************/ +/* + * open "/proc/fs/afs//servers" which provides a summary of active servers + */ +static int afs_proc_cell_servers_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + afs_cell_t *cell; + int ret; + + cell = afs_get_cell_maybe((afs_cell_t**)&PDE(inode)->data); + if (!cell) + return -ENOENT; + + ret = seq_open(file,&afs_proc_cell_servers_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = cell; + + return 0; +} /* end afs_proc_cell_servers_open() */ + +/*****************************************************************************/ +/* + * close the file and release the ref to the cell + */ +static int afs_proc_cell_servers_release(struct inode *inode, struct file *file) +{ + afs_cell_t *cell = PDE(inode)->data; + int ret; + + ret = seq_release(inode,file); + + afs_put_cell(cell); + +} /* end afs_proc_cell_servers_release() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the cells list and return the first item + */ +static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + afs_cell_t *cell = m->private; + loff_t pos = *_pos; + + _enter("cell=%p pos=%Ld",cell,*_pos); + + /* lock the list against modification */ + read_lock(&cell->sv_lock); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&cell->sv_list) + if (!pos--) + break; + + return _p!=&cell->sv_list ? _p : NULL; +} /* end afs_proc_cell_servers_start() */ + +/*****************************************************************************/ +/* + * move to next cell in cells list + */ +static void *afs_proc_cell_servers_next(struct seq_file *p, void *v, loff_t *_pos) +{ + struct list_head *_p; + afs_cell_t *cell = p->private; + + _enter("cell=%p pos=%Ld",cell,*_pos); + + (*_pos)++; + + _p = v; + _p = v==(void*)1 ? cell->sv_list.next : _p->next; + + return _p!=&cell->sv_list ? _p : NULL; +} /* end afs_proc_cell_servers_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the cells list + */ +static void afs_proc_cell_servers_stop(struct seq_file *p, void *v) +{ + afs_cell_t *cell = p->private; + + read_unlock(&cell->sv_lock); + +} /* end afs_proc_cell_servers_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of volume lines + */ +static int afs_proc_cell_servers_show(struct seq_file *m, void *v) +{ + afs_server_t *server = list_entry(v,afs_server_t,link); + char ipaddr[20]; + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m, "USE ADDR STATE\n"); + return 0; + } + + /* display one cell per line on subsequent lines */ + sprintf(ipaddr,"%u.%u.%u.%u",NIPQUAD(server->addr)); + seq_printf(m,"%3d %-15.15s %5d\n", + atomic_read(&server->usage), + ipaddr, + server->fs_state + ); + + return 0; +} /* end afs_proc_cell_servers_show() */ diff -urNp linux-5240/fs/afs/server.c linux-5250/fs/afs/server.c --- linux-5240/fs/afs/server.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/server.c @@ -0,0 +1,490 @@ +/* server.c: AFS server record management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include "volume.h" +#include "cell.h" +#include "server.h" +#include "transport.h" +#include "cache.h" +#include "vlclient.h" +#include "kafstimod.h" +#include "internal.h" + +spinlock_t afs_server_peer_lock = SPIN_LOCK_UNLOCKED; + +#define FS_SERVICE_ID 1 /* AFS Volume Location Service ID */ +#define VL_SERVICE_ID 52 /* AFS Volume Location Service ID */ + +static void __afs_server_timeout(afs_timer_t *timer) +{ + afs_server_t *server = list_entry(timer,afs_server_t,timeout); + + _debug("SERVER TIMEOUT [%p{u=%d}]",server,atomic_read(&server->usage)); + + afs_server_do_timeout(server); +} + +static const struct afs_timer_ops afs_server_timer_ops = { + timed_out: __afs_server_timeout, +}; + +/*****************************************************************************/ +/* + * lookup a server record in a cell + * - TODO: search the cell's server list + */ +int afs_server_lookup(afs_cell_t *cell, const struct in_addr *addr, afs_server_t **_server) +{ + struct list_head *_p; + afs_server_t *server, *active, *zombie; + int loop; + + _enter("%p,%08x,",cell,ntohl(addr->s_addr)); + + /* allocate and initialise a server record */ + server = kmalloc(sizeof(afs_server_t),GFP_KERNEL); + if (!server) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(server,0,sizeof(afs_server_t)); + atomic_set(&server->usage,1); + + INIT_LIST_HEAD(&server->link); + init_rwsem(&server->sem); + INIT_LIST_HEAD(&server->fs_callq); + spin_lock_init(&server->fs_lock); + INIT_LIST_HEAD(&server->cb_promises); + spin_lock_init(&server->cb_lock); + + for (loop=0; loopfs_conn_cnt[loop] = 4; + + memcpy(&server->addr,addr,sizeof(struct in_addr)); + server->addr.s_addr = addr->s_addr; + + afs_timer_init(&server->timeout,&afs_server_timer_ops); + + /* add to the cell */ + write_lock(&cell->sv_lock); + + /* check the active list */ + list_for_each(_p,&cell->sv_list) { + active = list_entry(_p,afs_server_t,link); + + if (active->addr.s_addr==addr->s_addr) + goto use_active_server; + } + + /* check the inactive list */ + spin_lock(&cell->sv_gylock); + list_for_each(_p,&cell->sv_graveyard) { + zombie = list_entry(_p,afs_server_t,link); + + if (zombie->addr.s_addr==addr->s_addr) + goto resurrect_server; + } + spin_unlock(&cell->sv_gylock); + + afs_get_cell(cell); + server->cell = cell; + list_add_tail(&server->link,&cell->sv_list); + + write_unlock(&cell->sv_lock); + + *_server = server; + _leave(" = 0 (%p)",server); + return 0; + + /* found a matching active server */ + use_active_server: + _debug("active server"); + afs_get_server(active); + write_unlock(&cell->sv_lock); + + kfree(server); + + *_server = active; + _leave(" = 0 (%p)",active); + return 0; + + /* found a matching server in the graveyard, so resurrect it and dispose of the new rec */ + resurrect_server: + _debug("resurrecting server"); + + list_del(&zombie->link); + list_add_tail(&zombie->link,&cell->sv_list); + afs_get_server(zombie); + afs_kafstimod_del_timer(&zombie->timeout); + spin_unlock(&cell->sv_gylock); + write_unlock(&cell->sv_lock); + + kfree(server); + + *_server = zombie; + _leave(" = 0 (%p)",zombie); + return 0; + +} /* end afs_server_lookup() */ + +/*****************************************************************************/ +/* + * destroy a server record + * - removes from the cell list + */ +void afs_put_server(afs_server_t *server) +{ + afs_cell_t *cell; + + _enter("%p",server); + + cell = server->cell; + + /* sanity check */ + if (atomic_read(&server->usage)<=0) + BUG(); + + /* to prevent a race, the decrement and the dequeue must be effectively atomic */ + write_lock(&cell->sv_lock); + + if (likely(!atomic_dec_and_test(&server->usage))) { + write_unlock(&cell->sv_lock); + _leave(""); + return; + } + + spin_lock(&cell->sv_gylock); + list_del(&server->link); + list_add_tail(&server->link,&cell->sv_graveyard); + + /* time out in 10 secs */ + afs_kafstimod_add_timer(&server->timeout,10*HZ); + + spin_unlock(&cell->sv_gylock); + write_unlock(&cell->sv_lock); + + _leave(" [killed]"); +} /* end afs_put_server() */ + +/*****************************************************************************/ +/* + * timeout server record + * - removes from the cell's graveyard if the usage count is zero + */ +void afs_server_do_timeout(afs_server_t *server) +{ + struct rxrpc_peer *peer; + afs_cell_t *cell; + int loop; + + _enter("%p",server); + + cell = server->cell; + + if (atomic_read(&server->usage)<0) BUG(); + + /* remove from graveyard if still dead */ + spin_lock(&cell->vl_gylock); + if (atomic_read(&server->usage)==0) + list_del_init(&server->link); + else + server = NULL; + spin_unlock(&cell->vl_gylock); + + if (!server) { + _leave(""); + return; /* resurrected */ + } + + /* we can now destroy it properly */ + afs_put_cell(cell); + + /* uncross-point the structs under a global lock */ + spin_lock(&afs_server_peer_lock); + peer = server->peer; + if (peer) { + server->peer = NULL; + peer->user = NULL; + } + spin_unlock(&afs_server_peer_lock); + + /* finish cleaning up the server */ + for (loop=AFS_SERVER_CONN_LIST_SIZE-1; loop>=0; loop--) + if (server->fs_conn[loop]) + rxrpc_put_connection(server->fs_conn[loop]); + + if (server->vlserver) + rxrpc_put_connection(server->vlserver); + + kfree(server); + + _leave(" [destroyed]"); +} /* end afs_server_do_timeout() */ + +/*****************************************************************************/ +/* + * get a callslot on a connection to the fileserver on the specified server + */ +int afs_server_request_callslot(afs_server_t *server, struct afs_server_callslot *callslot) +{ + struct afs_server_callslot *pcallslot; + struct rxrpc_connection *conn; + int nconn, ret; + + _enter("%p,",server); + + INIT_LIST_HEAD(&callslot->link); + callslot->task = current; + callslot->conn = NULL; + callslot->nconn = -1; + callslot->ready = 0; + + ret = 0; + conn = NULL; + + /* get hold of a callslot first */ + spin_lock(&server->fs_lock); + + /* resurrect the server if it's death timeout has expired */ + if (server->fs_state) { + if (time_before(jiffies,server->fs_dead_jif)) { + ret = server->fs_state; + spin_unlock(&server->fs_lock); + _leave(" = %d [still dead]",ret); + return ret; + } + + server->fs_state = 0; + } + + /* try and find a connection that has spare callslots */ + for (nconn=0; nconnfs_conn_cnt[nconn]>0) { + server->fs_conn_cnt[nconn]--; + spin_unlock(&server->fs_lock); + callslot->nconn = nconn; + goto obtained_slot; + } + } + + /* none were available - wait interruptibly for one to become available */ + set_current_state(TASK_INTERRUPTIBLE); + list_add_tail(&callslot->link,&server->fs_callq); + spin_unlock(&server->fs_lock); + + while (!callslot->ready && !signal_pending(current)) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + + set_current_state(TASK_RUNNING); + + /* even if we were interrupted we may still be queued */ + if (!callslot->ready) { + spin_lock(&server->fs_lock); + list_del_init(&callslot->link); + spin_unlock(&server->fs_lock); + } + + nconn = callslot->nconn; + + /* if interrupted, we must release any slot we also got before returning an error */ + if (signal_pending(current)) { + ret = -EINTR; + goto error_release; + } + + /* if we were woken up with an error, then pass that error back to the called */ + if (nconn<0) { + _leave(" = %d",callslot->errno); + return callslot->errno; + } + + /* were we given a connection directly? */ + if (callslot->conn) { + /* yes - use it */ + _leave(" = 0 (nc=%d)",nconn); + return 0; + } + + /* got a callslot, but no connection */ + obtained_slot: + + /* need to get hold of the RxRPC connection */ + down_write(&server->sem); + + /* quick check to see if there's an outstanding error */ + ret = server->fs_state; + if (ret) + goto error_release_upw; + + if (server->fs_conn[nconn]) { + /* reuse an existing connection */ + rxrpc_get_connection(server->fs_conn[nconn]); + callslot->conn = server->fs_conn[nconn]; + } + else { + /* create a new connection */ + ret = rxrpc_create_connection(afs_transport, + htons(7000), + server->addr.s_addr, + FS_SERVICE_ID, + NULL, + &server->fs_conn[nconn]); + + if (ret<0) + goto error_release_upw; + + callslot->conn = server->fs_conn[0]; + rxrpc_get_connection(callslot->conn); + } + + up_write(&server->sem); + + _leave(" = 0"); + return 0; + + /* handle an error occurring */ + error_release_upw: + up_write(&server->sem); + + error_release: + /* either release the callslot or pass it along to another deserving task */ + spin_lock(&server->fs_lock); + + if (nconn<0) { + /* no callslot allocated */ + } + else if (list_empty(&server->fs_callq)) { + /* no one waiting */ + server->fs_conn_cnt[nconn]++; + spin_unlock(&server->fs_lock); + } + else { + /* someone's waiting - dequeue them and wake them up */ + pcallslot = list_entry(server->fs_callq.next,struct afs_server_callslot,link); + list_del_init(&pcallslot->link); + + pcallslot->errno = server->fs_state; + if (!pcallslot->errno) { + /* pass them out callslot details */ + callslot->conn = xchg(&pcallslot->conn,callslot->conn); + pcallslot->nconn = nconn; + callslot->nconn = nconn = -1; + } + pcallslot->ready = 1; + wake_up_process(pcallslot->task); + spin_unlock(&server->fs_lock); + } + + if (callslot->conn) rxrpc_put_connection(callslot->conn); + callslot->conn = NULL; + + _leave(" = %d",ret); + return ret; + +} /* end afs_server_request_callslot() */ + +/*****************************************************************************/ +/* + * release a callslot back to the server + * - transfers the RxRPC connection to the next pending callslot if possible + */ +void afs_server_release_callslot(afs_server_t *server, struct afs_server_callslot *callslot) +{ + struct afs_server_callslot *pcallslot; + + _enter("{ad=%08x,cnt=%u},{%d}", + ntohl(server->addr.s_addr), + server->fs_conn_cnt[callslot->nconn], + callslot->nconn); + + if (callslot->nconn<0) BUG(); + + spin_lock(&server->fs_lock); + + if (list_empty(&server->fs_callq)) { + /* no one waiting */ + server->fs_conn_cnt[callslot->nconn]++; + spin_unlock(&server->fs_lock); + } + else { + /* someone's waiting - dequeue them and wake them up */ + pcallslot = list_entry(server->fs_callq.next,struct afs_server_callslot,link); + list_del_init(&pcallslot->link); + + pcallslot->errno = server->fs_state; + if (!pcallslot->errno) { + /* pass them out callslot details */ + callslot->conn = xchg(&pcallslot->conn,callslot->conn); + pcallslot->nconn = callslot->nconn; + callslot->nconn = -1; + } + + pcallslot->ready = 1; + wake_up_process(pcallslot->task); + spin_unlock(&server->fs_lock); + } + + if (callslot->conn) rxrpc_put_connection(callslot->conn); + + _leave(""); +} /* end afs_server_release_callslot() */ + +/*****************************************************************************/ +/* + * get a handle to a connection to the vlserver (volume location) on the specified server + */ +int afs_server_get_vlconn(afs_server_t *server, struct rxrpc_connection **_conn) +{ + struct rxrpc_connection *conn; + int ret; + + _enter("%p,",server); + + ret = 0; + conn = NULL; + down_read(&server->sem); + + if (server->vlserver) { + /* reuse an existing connection */ + rxrpc_get_connection(server->vlserver); + conn = server->vlserver; + up_read(&server->sem); + } + else { + /* create a new connection */ + up_read(&server->sem); + down_write(&server->sem); + if (!server->vlserver) { + ret = rxrpc_create_connection(afs_transport, + htons(7003), + server->addr.s_addr, + VL_SERVICE_ID, + NULL, + &server->vlserver); + } + if (ret==0) { + rxrpc_get_connection(server->vlserver); + conn = server->vlserver; + } + up_write(&server->sem); + } + + *_conn = conn; + _leave(" = %d",ret); + return ret; +} /* end afs_server_get_vlconn() */ diff -urNp linux-5240/fs/afs/server.h linux-5250/fs/afs/server.h --- linux-5240/fs/afs/server.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/server.h @@ -0,0 +1,97 @@ +/* server.h: AFS server record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_SERVER_H +#define _LINUX_AFS_SERVER_H + +#include "types.h" +#include "kafstimod.h" +#include +#include + +extern spinlock_t afs_server_peer_lock; + +/*****************************************************************************/ +/* + * AFS server record + */ +struct afs_server +{ + atomic_t usage; + afs_cell_t *cell; /* cell in which server resides */ + struct list_head link; /* link in cell's server list */ + struct rw_semaphore sem; /* access lock */ + afs_timer_t timeout; /* graveyard timeout */ + struct in_addr addr; /* server address */ + struct rxrpc_peer *peer; /* peer record for this server */ + struct rxrpc_connection *vlserver; /* connection to the volume location service */ + + /* file service access */ +#define AFS_SERVER_CONN_LIST_SIZE 2 + struct rxrpc_connection *fs_conn[AFS_SERVER_CONN_LIST_SIZE]; /* FS connections */ + unsigned fs_conn_cnt[AFS_SERVER_CONN_LIST_SIZE]; /* per conn call count */ + struct list_head fs_callq; /* queue of processes waiting to make a call */ + spinlock_t fs_lock; /* access lock */ + int fs_state; /* 0 or reason FS currently marked dead (-errno) */ + unsigned fs_rtt; /* FS round trip time */ + unsigned long fs_act_jif; /* time at which last activity occurred */ + unsigned long fs_dead_jif; /* time at which no longer to be considered dead */ + + /* callback promise management */ + struct list_head cb_promises; /* as yet unbroken promises from this server */ + spinlock_t cb_lock; /* access lock */ +}; + +extern int afs_server_lookup(afs_cell_t *cell, const struct in_addr *addr, afs_server_t **_server); + +#define afs_get_server(S) do { atomic_inc(&(S)->usage); } while(0) + +extern void afs_put_server(afs_server_t *server); +extern void afs_server_do_timeout(afs_server_t *server); + +extern int afs_server_find_by_peer(const struct rxrpc_peer *peer, afs_server_t **_server); + +extern int afs_server_get_vlconn(afs_server_t *server, struct rxrpc_connection **_conn); + +static inline afs_server_t *afs_server_get_from_peer(struct rxrpc_peer *peer) +{ + afs_server_t *server; + + spin_lock(&afs_server_peer_lock); + server = peer->user; + if (server) + afs_get_server(server); + spin_unlock(&afs_server_peer_lock); + + return server; +} + +/*****************************************************************************/ +/* + * AFS server callslot grant record + */ +struct afs_server_callslot +{ + struct list_head link; /* link in server's list */ + struct task_struct *task; /* process waiting to make call */ + struct rxrpc_connection *conn; /* connection to use (or NULL on error) */ + short nconn; /* connection slot number (-1 on error) */ + char ready; /* T when ready */ + int errno; /* error number if nconn==-1 */ +}; + +extern int afs_server_request_callslot(afs_server_t *server, + struct afs_server_callslot *callslot); + +extern void afs_server_release_callslot(afs_server_t *server, + struct afs_server_callslot *callslot); + +#endif /* _LINUX_AFS_SERVER_H */ diff -urNp linux-5240/fs/afs/super.c linux-5250/fs/afs/super.c --- linux-5240/fs/afs/super.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/super.c @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2002 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: David Howells + * David Woodhouse + * + */ + +#include +#include +#include +#include +#include +#include +#include "vnode.h" +#include "volume.h" +#include "cell.h" +#include "cmservice.h" +#include "fsclient.h" +#include "super.h" +#include "cache.h" +#include "internal.h" + +#define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */ + +static inline char *strdup(const char *s) +{ + char *ns = kmalloc(strlen(s)+1,GFP_KERNEL); + if (ns) + strcpy(ns,s); + return ns; +} + +static void afs_i_init_once(void *foo, kmem_cache_t *cachep, unsigned long flags); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static struct super_block *afs_get_sb(struct file_system_type *fs_type, + int flags, char *dev_name, void *data); +#else +static struct super_block *afs_read_super(struct super_block *sb, void *data, int); +static void afs_put_inode(struct inode *inode); +#endif + +static struct inode *afs_alloc_inode(struct super_block *sb); + +static void afs_put_super(struct super_block *sb); + +static void afs_destroy_inode(struct inode *inode); + +static struct file_system_type afs_fs_type = { + owner: THIS_MODULE, + name: "afs", +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + get_sb: afs_get_sb, + kill_sb: kill_anon_super, +#else + read_super: afs_read_super, +#endif +}; + +static struct super_operations afs_super_ops = { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + statfs: simple_statfs, + alloc_inode: afs_alloc_inode, + drop_inode: generic_delete_inode, + destroy_inode: afs_destroy_inode, +#else + put_inode: afs_put_inode, + read_inode2: afs_read_inode2, +#endif + clear_inode: afs_clear_inode, + put_super: afs_put_super, +}; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static kmem_cache_t *afs_inode_cachep; +#endif + +#if 0 +static const char *cachedev; +#endif +static afs_cache_t *afs_cache; + +/*****************************************************************************/ +/* + * initialise the filesystem + */ +int __init afs_fs_init(void) +{ + int ret; + + /* open the cache */ +#if 0 + ret = -EINVAL; + if (!cachedev) { + printk(KERN_NOTICE "kAFS: No cache device specified as module parm\n"); + printk(KERN_NOTICE "kAFS: Set with \"cachedev=\" on insmod's cmdline\n"); + return ret; + } + + ret = afs_cache_open(cachedev,&afs_cache); + if (ret<0) { + printk(KERN_NOTICE "kAFS: Failed to open cache device\n"); + return ret; + } +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + /* create ourselves an inode cache */ + ret = -ENOMEM; + afs_inode_cachep = kmem_cache_create("afs_inode_cache", + sizeof(afs_vnode_t), + 0, + SLAB_HWCACHE_ALIGN, + afs_i_init_once, + NULL); + if (!afs_inode_cachep) { + printk(KERN_NOTICE "kAFS: Failed to allocate inode cache\n"); +#if 0 + afs_put_cache(afs_cache); +#endif + return ret; + } +#endif + + /* now export our filesystem to lesser mortals */ + ret = register_filesystem(&afs_fs_type); + if (ret<0) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + kmem_cache_destroy(afs_inode_cachep); +#endif +#if 0 + afs_put_cache(afs_cache); +#endif + return ret; + } + + return 0; +} /* end afs_fs_init() */ + +/*****************************************************************************/ +/* + * clean up the filesystem + */ +void __exit afs_fs_exit(void) +{ + /* destroy our private inode cache */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + kmem_cache_destroy(afs_inode_cachep); +#endif + + unregister_filesystem(&afs_fs_type); + +#if 0 + if (afs_cache) + afs_put_cache(afs_cache); +#endif +} /* end afs_fs_exit() */ + +/*****************************************************************************/ +/* + * check that an argument has a value + */ +static int want_arg(char **_value, const char *option) +{ + if (!_value || !*_value || !**_value) { + printk(KERN_NOTICE "kAFS: %s: argument missing\n",option); + return 0; + } + return 1; +} /* end want_arg() */ + +/*****************************************************************************/ +/* + * check that there is a value + */ +#if 0 +static int want_value(char **_value, const char *option) +{ + if (!_value || !*_value || !**_value) { + printk(KERN_NOTICE "kAFS: %s: argument incomplete\n",option); + return 0; + } + return 1; +} /* end want_value() */ +#endif + +/*****************************************************************************/ +/* + * check that there's no subsequent value + */ +static int want_no_value(char *const *_value, const char *option) +{ + if (*_value && **_value) { + printk(KERN_NOTICE "kAFS: %s: Invalid argument: %s\n",option,*_value); + return 0; + } + return 1; +} /* end want_no_value() */ + +/*****************************************************************************/ +/* + * extract a number from an option string value + */ +#if 0 +static int want_number(char **_value, const char *option, unsigned long *number, + unsigned long limit) +{ + char *value = *_value; + + if (!want_value(_value,option)) + return 0; + + *number = simple_strtoul(value,_value,0); + + if (value==*_value) { + printk(KERN_NOTICE "kAFS: %s: Invalid number: %s\n",option,value); + return 0; + } + + if (*number>limit) { + printk(KERN_NOTICE "kAFS: %s: numeric value %lu > %lu\n",option,*number,limit); + return 0; + } + + return 1; +} /* end want_number() */ +#endif + +/*****************************************************************************/ +/* + * extract a separator from an option string value + */ +#if 0 +static int want_sep(char **_value, const char *option, char sep) +{ + if (!want_value(_value,option)) + return 0; + + if (*(*_value)++ != sep) { + printk(KERN_NOTICE "kAFS: %s: '%c' expected: %s\n",option,sep,*_value-1); + return 0; + } + + return 1; +} /* end want_number() */ +#endif + +/*****************************************************************************/ +/* + * extract an IP address from an option string value + */ +#if 0 +static int want_ipaddr(char **_value, const char *option, struct in_addr *addr) +{ + unsigned long number[4]; + + if (!want_value(_value,option)) + return 0; + + if (!want_number(_value,option,&number[0],255) || + !want_sep(_value,option,'.') || + !want_number(_value,option,&number[1],255) || + !want_sep(_value,option,'.') || + !want_number(_value,option,&number[2],255) || + !want_sep(_value,option,'.') || + !want_number(_value,option,&number[3],255)) + return 0; + + ((u8*)addr)[0] = number[0]; + ((u8*)addr)[1] = number[1]; + ((u8*)addr)[2] = number[2]; + ((u8*)addr)[3] = number[3]; + + return 1; +} /* end want_numeric() */ +#endif + +/*****************************************************************************/ +/* + * parse the mount options + * - this function has been shamelessly adapted from the ext3 fs which shamelessly adapted it from + * the msdos fs + */ +static int afs_super_parse_options(struct afs_super_info *as, char *options, char **devname) +{ + char *key, *value; + int ret; + + kenter("%s",options); + + ret = 0; + while ((key = strsep(&options,","))) + { + value = strchr(key,'='); + if (value) + *value++ = 0; + + printk("kAFS: KEY: %s, VAL:%s\n",key,value?:"-"); + + if (strcmp(key,"rwpath")==0) { + if (!want_no_value(&value,"rwpath")) return -EINVAL; + as->rwparent = 1; + continue; + } + else if (strcmp(key,"vol")==0) { + if (!want_arg(&value,"vol")) return -EINVAL; + *devname = value; + continue; + } + +#if 0 + if (strcmp(key,"servers")==0) { + if (!want_arg(&value,"servers")) return -EINVAL; + + _debug("servers=%s",value); + + for (;;) { + struct in_addr addr; + + if (!want_ipaddr(&value,"servers",&addr)) + return -EINVAL; + + ret = afs_create_server(as->cell,&addr,&as->server); + if (ret<0) { + printk("kAFS: unable to create server: %d\n",ret); + return ret; + } + + if (!*value) + break; + + if (as->server) { + printk(KERN_NOTICE + "kAFS: only one server can be specified\n"); + return -EINVAL; + } + + if (!want_sep(&value,"servers",':')) + return -EINVAL; + } + continue; + } +#endif + + printk("kAFS: Unknown mount option: '%s'\n",key); + ret = -EINVAL; + goto error; + } + + ret = 0; + + error: + kleave(" = %d",ret); + + return ret; +} /* end afs_super_parse_options() */ + +/*****************************************************************************/ +/* + * fill in the superblock + */ +static int afs_fill_super(struct super_block *sb, void *_data, int silent) +{ + struct afs_super_info *as = NULL; + struct dentry *root = NULL; + struct inode *inode = NULL; + afs_fid_t fid; + void **data = _data; + char *options, *devname; + int ret; + + kenter(""); + + if (!data) { + kleave(" = -EINVAL"); + return -EINVAL; + } + devname = data[0]; + options = data[1]; + if (options) + options[PAGE_SIZE-1] = 0; + + /* allocate a superblock info record */ + as = kmalloc(sizeof(struct afs_super_info),GFP_KERNEL); + if (!as) { + kleave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(as,0,sizeof(struct afs_super_info)); + + /* parse the options */ + if (options) { + ret = afs_super_parse_options(as,options,&devname); + if (ret<0) + goto error; + if (!devname) { + printk("kAFS: no volume name specified\n"); + ret = -EINVAL; + goto error; + } + } + + /* parse the device name */ + ret = afs_volume_lookup(afs_cache,devname,as->rwparent,&as->volume); + if (ret<0) + goto error; + + /* fill in the superblock */ + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = AFS_FS_MAGIC; + sb->s_op = &afs_super_ops; + sb->u.generic_sbp = as; + + /* allocate the root inode and dentry */ + fid.vid = as->volume->vid; + fid.vnode = 1; + fid.unique = 1; + ret = afs_iget(sb,&fid,&inode); + if (ret<0) + goto error; + + ret = -ENOMEM; + root = d_alloc_root(inode); + if (!root) + goto error; + + sb->s_root = root; + + kleave(" = 0"); + return 0; + + error: + if (root) dput(root); + if (inode) iput(inode); + if (as) { + if (as->volume) afs_put_volume(as->volume); + kfree(as); + } + sb->u.generic_sbp = NULL; + + kleave(" = %d",ret); + return ret; +} /* end afs_fill_super() */ + +/*****************************************************************************/ +/* + * get an AFS superblock + * - TODO: don't use get_sb_nodev(), but rather call sget() directly + */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static struct super_block *afs_get_sb(struct file_system_type *fs_type, + int flags, + char *dev_name, + void *options) +{ + struct super_block *sb; + void *data[2] = { dev_name, options }; + int ret; + + _enter(",,%s,%p",dev_name,options); + + /* start the cache manager */ + ret = afscm_start(); + if (ret<0) { + _leave(" = %d",ret); + return ERR_PTR(ret); + } + + /* allocate a deviceless superblock */ + sb = get_sb_nodev(fs_type,flags,data,afs_fill_super); + if (IS_ERR(sb)) { + afscm_stop(); + return sb; + } + + _leave(""); + return sb; +} /* end afs_get_sb() */ +#endif + +/*****************************************************************************/ +/* + * read an AFS superblock + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +static struct super_block *afs_read_super(struct super_block *sb, void *options, int silent) +{ + void *data[2] = { NULL, options }; + int ret; + + kenter(",,%s",(char*)options); + + /* start the cache manager */ + ret = afscm_start(); + if (ret<0) { + kleave(" = NULL (%d)",ret); + return NULL; + } + + /* allocate a deviceless superblock */ + ret = afs_fill_super(sb,data,silent); + if (ret<0) { + afscm_stop(); + kleave(" = NULL (%d)",ret); + return NULL; + } + + kleave(" = %p",sb); + return sb; +} /* end afs_read_super() */ +#endif + +/*****************************************************************************/ +/* + * finish the unmounting process on the superblock + */ +static void afs_put_super(struct super_block *sb) +{ + struct afs_super_info *as = sb->u.generic_sbp; + + _enter(""); + + if (as) { + if (as->volume) afs_put_volume(as->volume); + } + + /* stop the cache manager */ + afscm_stop(); + + _leave(""); +} /* end afs_put_super() */ + +/*****************************************************************************/ +/* + * initialise an inode cache slab element prior to any use + */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static void afs_i_init_once(void *_vnode, kmem_cache_t *cachep, unsigned long flags) +{ + afs_vnode_t *vnode = (afs_vnode_t *) _vnode; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { + memset(vnode,0,sizeof(*vnode)); + inode_init_once(&vnode->vfs_inode); + init_waitqueue_head(&vnode->update_waitq); + spin_lock_init(&vnode->lock); + INIT_LIST_HEAD(&vnode->cb_link); + INIT_LIST_HEAD(&vnode->cb_hash_link); + afs_timer_init(&vnode->cb_timeout,&afs_vnode_cb_timed_out_ops); + } + +} /* end afs_i_init_once() */ +#endif + +/*****************************************************************************/ +/* + * allocate an AFS inode struct from our slab cache + */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static struct inode *afs_alloc_inode(struct super_block *sb) +{ + afs_vnode_t *vnode; + + vnode = (afs_vnode_t *) kmem_cache_alloc(afs_inode_cachep,SLAB_KERNEL); + if (!vnode) + return NULL; + + memset(&vnode->fid,0,sizeof(vnode->fid)); + memset(&vnode->status,0,sizeof(vnode->status)); + + vnode->volume = NULL; + vnode->update_cnt = 0; + vnode->flags = 0; + + return &vnode->vfs_inode; +} /* end afs_alloc_inode() */ +#endif + +/*****************************************************************************/ +/* + * put an inode + */ +static void afs_put_inode(struct inode *inode) +{ + if (inode->u.generic_ip) kfree(inode->u.generic_ip); + +} /* end afs_put_inode() */ + +/*****************************************************************************/ +/* + * destroy an AFS inode struct + */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static void afs_destroy_inode(struct inode *inode) +{ + _enter("{%lu}",inode->i_ino); + kmem_cache_free(afs_inode_cachep, AFS_FS_I(inode)); +} /* end afs_destroy_inode() */ +#endif diff -urNp linux-5240/fs/afs/super.h linux-5250/fs/afs/super.h --- linux-5240/fs/afs/super.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/super.h @@ -0,0 +1,43 @@ +/* super.h: AFS filesystem internal private data + * + * Copyright (c) 2002 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: David Woodhouse + * David Howells + * + */ + +#ifndef _LINUX_AFS_SUPER_H +#define _LINUX_AFS_SUPER_H + +#include +#include "server.h" + +#ifdef __KERNEL__ + +/*****************************************************************************/ +/* + * AFS superblock private data + * - there's one superblock per volume + */ +struct afs_super_info +{ + afs_volume_t *volume; /* volume record */ + char rwparent; /* T if parent is R/W AFS volume */ +}; + +static inline struct afs_super_info *AFS_FS_S(struct super_block *sb) +{ + return sb->u.generic_sbp; +} + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_AFS_SUPER_H */ diff -urNp linux-5240/fs/afs/transport.h linux-5250/fs/afs/transport.h --- linux-5240/fs/afs/transport.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/transport.h @@ -0,0 +1,21 @@ +/* transport.h: AFS transport management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_TRANSPORT_H +#define _LINUX_AFS_TRANSPORT_H + +#include "types.h" +#include + +/* the cache manager transport endpoint */ +extern struct rxrpc_transport *afs_transport; + +#endif /* _LINUX_AFS_TRANSPORT_H */ diff -urNp linux-5240/fs/afs/types.h linux-5250/fs/afs/types.h --- linux-5240/fs/afs/types.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/types.h @@ -0,0 +1,141 @@ +/* types.h: AFS types + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_TYPES_H +#define _LINUX_AFS_TYPES_H + +#ifdef __KERNEL__ +#include +#endif /* __KERNEL__ */ + +typedef unsigned afs_volid_t; +typedef unsigned afs_vnodeid_t; +typedef unsigned long long afs_dataversion_t; + +typedef struct afs_async_op afs_async_op_t; +typedef struct afs_cache afs_cache_t; +typedef struct afs_cache_volindex afs_cache_volindex_t; +typedef struct afs_callback afs_callback_t; +typedef struct afs_cell afs_cell_t; +typedef struct afs_fid afs_fid_t; +typedef struct afs_file_status afs_file_status_t; +typedef struct afs_server afs_server_t; +typedef struct afs_timer afs_timer_t; +typedef struct afs_vlocation afs_vlocation_t; +typedef struct afs_vnode afs_vnode_t; +typedef struct afs_volsync afs_volsync_t; +typedef struct afs_volume afs_volume_t; +typedef struct afs_volume_info afs_volume_info_t; + +typedef struct afsvl_dbentry afsvl_dbentry_t; + +typedef enum { + AFSVL_RWVOL, /* read/write volume */ + AFSVL_ROVOL, /* read-only volume */ + AFSVL_BACKVOL, /* backup volume */ +} afs_voltype_t; + +extern const char *afs_voltypes[]; + +typedef enum { + AFS_FTYPE_INVALID = 0, + AFS_FTYPE_FILE = 1, + AFS_FTYPE_DIR = 2, + AFS_FTYPE_SYMLINK = 3, +} afs_file_type_t; + +#ifdef __KERNEL__ + +/*****************************************************************************/ +/* + * AFS file identifier + */ +struct afs_fid +{ + afs_volid_t vid; /* volume ID */ + afs_vnodeid_t vnode; /* file index within volume */ + unsigned unique; /* unique ID number (file index version) */ +}; + +/*****************************************************************************/ +/* + * AFS callback notification + */ +typedef enum { + AFSCM_CB_UNTYPED = 0, /* no type set on CB break */ + AFSCM_CB_EXCLUSIVE = 1, /* CB exclusive to CM [not implemented] */ + AFSCM_CB_SHARED = 2, /* CB shared by other CM's */ + AFSCM_CB_DROPPED = 3, /* CB promise cancelled by file server */ +} afs_callback_type_t; + +struct afs_callback +{ + afs_server_t *server; /* server that made the promise */ + afs_fid_t fid; /* file identifier */ + unsigned version; /* callback version */ + unsigned expiry; /* time at which expires */ + afs_callback_type_t type; /* type of callback */ +}; + +#define AFSCBMAX 50 + +/*****************************************************************************/ +/* + * AFS volume information + */ +struct afs_volume_info +{ + afs_volid_t vid; /* volume ID */ + afs_voltype_t type; /* type of this volume */ + afs_volid_t type_vids[5]; /* volume ID's for possible types for this vol */ + + /* list of fileservers serving this volume */ + size_t nservers; /* number of entries used in servers[] */ + struct { + struct in_addr addr; /* fileserver address */ + } servers[8]; +}; + +/*****************************************************************************/ +/* + * AFS file status information + */ +struct afs_file_status +{ + unsigned if_version; /* interface version */ +#define AFS_FSTATUS_VERSION 1 + + afs_file_type_t type; /* file type */ + unsigned nlink; /* link count */ + size_t size; /* file size */ + afs_dataversion_t version; /* current data version */ + unsigned author; /* author ID */ + unsigned owner; /* owner ID */ + unsigned caller_access; /* access rights for authenticated caller */ + unsigned anon_access; /* access rights for unauthenticated caller */ + umode_t mode; /* UNIX mode */ + afs_fid_t parent; /* parent file ID */ + time_t mtime_client; /* last time client changed data */ + time_t mtime_server; /* last time server changed data */ +}; + +/*****************************************************************************/ +/* + * AFS volume synchronisation information + */ +struct afs_volsync +{ + time_t creation; /* volume creation time */ +}; + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_AFS_TYPES_H */ diff -urNp linux-5240/fs/afs/vlclient.c linux-5250/fs/afs/vlclient.c --- linux-5240/fs/afs/vlclient.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/vlclient.c @@ -0,0 +1,661 @@ +/* vlclient.c: AFS Volume Location Service client + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "server.h" +#include "vlclient.h" +#include "kafsasyncd.h" +#include "kafstimod.h" +#include "errors.h" +#include "internal.h" + +#define VLGETENTRYBYID 503 /* AFS Get Cache Entry By ID operation ID */ +#define VLGETENTRYBYNAME 504 /* AFS Get Cache Entry By Name operation ID */ +#define VLPROBE 514 /* AFS Probe Volume Location Service operation ID */ + +static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call); +static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call); + +/*****************************************************************************/ +/* + * map afs VL abort codes to/from Linux error codes + * - called with call->lock held + */ +static void afs_rxvl_aemap(struct rxrpc_call *call) +{ + int err; + + _enter("{%u,%u,%d}",call->app_err_state,call->app_abort_code,call->app_errno); + + switch (call->app_err_state) { + case RXRPC_ESTATE_LOCAL_ABORT: + call->app_abort_code = -call->app_errno; + return; + + case RXRPC_ESTATE_PEER_ABORT: + switch (call->app_abort_code) { + case AFSVL_IDEXIST: err = -EEXIST; break; + case AFSVL_IO: err = -EREMOTEIO; break; + case AFSVL_NAMEEXIST: err = -EEXIST; break; + case AFSVL_CREATEFAIL: err = -EREMOTEIO; break; + case AFSVL_NOENT: err = -ENOMEDIUM; break; + case AFSVL_EMPTY: err = -ENOMEDIUM; break; + case AFSVL_ENTDELETED: err = -ENOMEDIUM; break; + case AFSVL_BADNAME: err = -EINVAL; break; + case AFSVL_BADINDEX: err = -EINVAL; break; + case AFSVL_BADVOLTYPE: err = -EINVAL; break; + case AFSVL_BADSERVER: err = -EINVAL; break; + case AFSVL_BADPARTITION: err = -EINVAL; break; + case AFSVL_REPSFULL: err = -EFBIG; break; + case AFSVL_NOREPSERVER: err = -ENOENT; break; + case AFSVL_DUPREPSERVER: err = -EEXIST; break; + case AFSVL_RWNOTFOUND: err = -ENOENT; break; + case AFSVL_BADREFCOUNT: err = -EINVAL; break; + case AFSVL_SIZEEXCEEDED: err = -EINVAL; break; + case AFSVL_BADENTRY: err = -EINVAL; break; + case AFSVL_BADVOLIDBUMP: err = -EINVAL; break; + case AFSVL_IDALREADYHASHED: err = -EINVAL; break; + case AFSVL_ENTRYLOCKED: err = -EBUSY; break; + case AFSVL_BADVOLOPER: err = -EBADRQC; break; + case AFSVL_BADRELLOCKTYPE: err = -EINVAL; break; + case AFSVL_RERELEASE: err = -EREMOTEIO; break; + case AFSVL_BADSERVERFLAG: err = -EINVAL; break; + case AFSVL_PERM: err = -EACCES; break; + case AFSVL_NOMEM: err = -EREMOTEIO; break; + default: + err = afs_abort_to_error(call->app_abort_code); + break; + } + call->app_errno = err; + return; + + default: + return; + } +} /* end afs_rxvl_aemap() */ + +/*****************************************************************************/ +/* + * probe a volume location server to see if it is still alive + */ +int afs_rxvl_probe(afs_server_t *server, int alloc_flags) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct iovec piov[1]; + size_t sent; + int ret; + u32 param[1]; + + /* get hold of the vlserver connection */ + ret = afs_server_get_vlconn(server,&conn); + if (ret<0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(conn,NULL,NULL,afs_rxvl_aemap,&call); + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + goto out_put_conn; + } + call->app_opcode = VLPROBE; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + param[0] = htonl(VLPROBE); + piov[0].iov_len = sizeof(param); + piov[0].iov_base = param; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,1,piov,RXRPC_LAST_PACKET,alloc_flags,0,&sent); + if (ret<0) + goto abort; + + /* wait for the reply to completely arrive */ + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (call->app_call_state!=RXRPC_CSTATE_CLNT_RCV_REPLY || + signal_pending(current)) + break; + schedule(); + } + set_current_state(TASK_RUNNING); + + ret = -EINTR; + if (signal_pending(current)) + goto abort; + + switch (call->app_call_state) { + case RXRPC_CSTATE_ERROR: + ret = call->app_errno; + goto out_unwait; + + case RXRPC_CSTATE_CLNT_GOT_REPLY: + ret = 0; + goto out_unwait; + + default: + BUG(); + } + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call,ret); + schedule(); + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + rxrpc_put_connection(conn); + out: + return ret; + +} /* end afs_rxvl_probe() */ + +/*****************************************************************************/ +/* + * look up a volume location database entry by name + */ +int afs_rxvl_get_entry_by_name(afs_server_t *server, const char *volname, + struct afs_cache_volume *entry) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct iovec piov[3]; + unsigned tmp; + size_t sent; + int ret, loop; + u32 *bp, param[2], zero; + + _enter(",%s,",volname); + + memset(entry,0,sizeof(*entry)); + + /* get hold of the vlserver connection */ + ret = afs_server_get_vlconn(server,&conn); + if (ret<0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(conn,NULL,NULL,afs_rxvl_aemap,&call); + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + goto out_put_conn; + } + call->app_opcode = VLGETENTRYBYNAME; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + piov[1].iov_len = strlen(volname); + piov[1].iov_base = (char*)volname; + + zero = 0; + piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3; + piov[2].iov_base = &zero; + + param[0] = htonl(VLGETENTRYBYNAME); + param[1] = htonl(piov[1].iov_len); + + piov[0].iov_len = sizeof(param); + piov[0].iov_base = param; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,3,piov,RXRPC_LAST_PACKET,GFP_NOFS,0,&sent); + if (ret<0) + goto abort; + + /* wait for the reply to completely arrive */ + bp = rxrpc_call_alloc_scratch(call,384); + + ret = rxrpc_call_read_data(call,bp,384,RXRPC_CALL_READ_BLOCK|RXRPC_CALL_READ_ALL); + if (ret<0) { + if (ret==-ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + goto abort; + } + + /* unmarshall the reply */ + for (loop=0; loop<64; loop++) + entry->name[loop] = ntohl(*bp++); + bp++; /* final NUL */ + + bp++; /* type */ + entry->nservers = ntohl(*bp++); + + for (loop=0; loop<8; loop++) + entry->servers[loop].s_addr = *bp++; + + bp += 8; /* partition IDs */ + + for (loop=0; loop<8; loop++) { + tmp = ntohl(*bp++); + if (tmp & AFS_VLSF_RWVOL ) entry->srvtmask[loop] |= AFS_CACHE_VOL_STM_RW; + if (tmp & AFS_VLSF_ROVOL ) entry->srvtmask[loop] |= AFS_CACHE_VOL_STM_RO; + if (tmp & AFS_VLSF_BACKVOL) entry->srvtmask[loop] |= AFS_CACHE_VOL_STM_BAK; + } + + entry->vid[0] = ntohl(*bp++); + entry->vid[1] = ntohl(*bp++); + entry->vid[2] = ntohl(*bp++); + + bp++; /* clone ID */ + + tmp = ntohl(*bp++); /* flags */ + if (tmp & AFS_VLF_RWEXISTS ) entry->vidmask |= AFS_CACHE_VOL_STM_RW; + if (tmp & AFS_VLF_ROEXISTS ) entry->vidmask |= AFS_CACHE_VOL_STM_RO; + if (tmp & AFS_VLF_BACKEXISTS) entry->vidmask |= AFS_CACHE_VOL_STM_BAK; + + ret = -ENOMEDIUM; + if (!entry->vidmask) + goto abort; + + /* success */ + entry->ctime = xtime.tv_sec; + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + rxrpc_put_connection(conn); + out: + _leave(" = %d",ret); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call,ret); + schedule(); + goto out_unwait; +} /* end afs_rxvl_get_entry_by_name() */ + +/*****************************************************************************/ +/* + * look up a volume location database entry by ID + */ +int afs_rxvl_get_entry_by_id(afs_server_t *server, + afs_volid_t volid, + afs_voltype_t voltype, + struct afs_cache_volume *entry) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct iovec piov[1]; + unsigned tmp; + size_t sent; + int ret, loop; + u32 *bp, param[3]; + + _enter(",%x,%d,",volid,voltype); + + memset(entry,0,sizeof(*entry)); + + /* get hold of the vlserver connection */ + ret = afs_server_get_vlconn(server,&conn); + if (ret<0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(conn,NULL,NULL,afs_rxvl_aemap,&call); + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + goto out_put_conn; + } + call->app_opcode = VLGETENTRYBYID; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + param[0] = htonl(VLGETENTRYBYID); + param[1] = htonl(volid); + param[2] = htonl(voltype); + + piov[0].iov_len = sizeof(param); + piov[0].iov_base = param; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,1,piov,RXRPC_LAST_PACKET,GFP_NOFS,0,&sent); + if (ret<0) + goto abort; + + /* wait for the reply to completely arrive */ + bp = rxrpc_call_alloc_scratch(call,384); + + ret = rxrpc_call_read_data(call,bp,384,RXRPC_CALL_READ_BLOCK|RXRPC_CALL_READ_ALL); + if (ret<0) { + if (ret==-ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + goto abort; + } + + /* unmarshall the reply */ + for (loop=0; loop<64; loop++) + entry->name[loop] = ntohl(*bp++); + bp++; /* final NUL */ + + bp++; /* type */ + entry->nservers = ntohl(*bp++); + + for (loop=0; loop<8; loop++) + entry->servers[loop].s_addr = *bp++; + + bp += 8; /* partition IDs */ + + for (loop=0; loop<8; loop++) { + tmp = ntohl(*bp++); + if (tmp & AFS_VLSF_RWVOL ) entry->srvtmask[loop] |= AFS_CACHE_VOL_STM_RW; + if (tmp & AFS_VLSF_ROVOL ) entry->srvtmask[loop] |= AFS_CACHE_VOL_STM_RO; + if (tmp & AFS_VLSF_BACKVOL) entry->srvtmask[loop] |= AFS_CACHE_VOL_STM_BAK; + } + + entry->vid[0] = ntohl(*bp++); + entry->vid[1] = ntohl(*bp++); + entry->vid[2] = ntohl(*bp++); + + bp++; /* clone ID */ + + tmp = ntohl(*bp++); /* flags */ + if (tmp & AFS_VLF_RWEXISTS ) entry->vidmask |= AFS_CACHE_VOL_STM_RW; + if (tmp & AFS_VLF_ROEXISTS ) entry->vidmask |= AFS_CACHE_VOL_STM_RO; + if (tmp & AFS_VLF_BACKEXISTS) entry->vidmask |= AFS_CACHE_VOL_STM_BAK; + + ret = -ENOMEDIUM; + if (!entry->vidmask) + goto abort; + +#if 0 /* TODO: remove */ + entry->nservers = 3; + entry->servers[0].s_addr = htonl(0xac101249); + entry->servers[1].s_addr = htonl(0xac101243); + entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/); + + entry->srvtmask[0] = AFS_CACHE_VOL_STM_RO; + entry->srvtmask[1] = AFS_CACHE_VOL_STM_RO; + entry->srvtmask[2] = AFS_CACHE_VOL_STM_RO | AFS_CACHE_VOL_STM_RW; +#endif + + /* success */ + entry->ctime = xtime.tv_sec; + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + rxrpc_put_connection(conn); + out: + _leave(" = %d",ret); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call,ret); + schedule(); + goto out_unwait; +} /* end afs_rxvl_get_entry_by_id() */ + +/*****************************************************************************/ +/* + * look up a volume location database entry by ID asynchronously + */ +int afs_rxvl_get_entry_by_id_async(afs_async_op_t *op, + afs_volid_t volid, + afs_voltype_t voltype) +{ + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct iovec piov[1]; + size_t sent; + int ret; + u32 param[3]; + + _enter(",%x,%d,",volid,voltype); + + /* get hold of the vlserver connection */ + ret = afs_server_get_vlconn(op->server,&conn); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + /* create a call through that connection */ + ret = rxrpc_create_call(conn, + afs_rxvl_get_entry_by_id_attn, + afs_rxvl_get_entry_by_id_error, + afs_rxvl_aemap, + &op->call); + rxrpc_put_connection(conn); + + if (ret<0) { + printk("kAFS: Unable to create call: %d\n",ret); + _leave(" = %d",ret); + return ret; + } + + op->call->app_opcode = VLGETENTRYBYID; + op->call->app_user = op; + + call = op->call; + rxrpc_get_call(call); + + /* send event notifications from the call to kafsasyncd */ + afs_kafsasyncd_begin_op(op); + + /* marshall the parameters */ + param[0] = htonl(VLGETENTRYBYID); + param[1] = htonl(volid); + param[2] = htonl(voltype); + + piov[0].iov_len = sizeof(param); + piov[0].iov_base = param; + + /* allocate result read buffer in scratch space */ + call->app_scr_ptr = rxrpc_call_alloc_scratch(op->call,384); + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call,1,piov,RXRPC_LAST_PACKET,GFP_NOFS,0,&sent); + if (ret<0) { + rxrpc_call_abort(call,ret); /* handle from kafsasyncd */ + ret = 0; + goto out; + } + + /* wait for the reply to completely arrive */ + ret = rxrpc_call_read_data(call,call->app_scr_ptr,384,0); + switch (ret) { + case 0: + case -EAGAIN: + case -ECONNABORTED: + ret = 0; + break; /* all handled by kafsasyncd */ + + default: + rxrpc_call_abort(call,ret); /* force kafsasyncd to handle it */ + ret = 0; + break; + } + + out: + rxrpc_put_call(call); + _leave(" = %d",ret); + return ret; + +} /* end afs_rxvl_get_entry_by_id_async() */ + +/*****************************************************************************/ +/* + * attend to the asynchronous get VLDB entry by ID + */ +int afs_rxvl_get_entry_by_id_async2(afs_async_op_t *op, + struct afs_cache_volume *entry) +{ + unsigned *bp, tmp; + int loop, ret; + + _enter("{op=%p cst=%u}",op,op->call->app_call_state); + + memset(entry,0,sizeof(*entry)); + + if (op->call->app_call_state==RXRPC_CSTATE_COMPLETE) { + /* operation finished */ + afs_kafsasyncd_terminate_op(op); + + bp = op->call->app_scr_ptr; + + /* unmarshall the reply */ + for (loop=0; loop<64; loop++) + entry->name[loop] = ntohl(*bp++); + bp++; /* final NUL */ + + bp++; /* type */ + entry->nservers = ntohl(*bp++); + + for (loop=0; loop<8; loop++) + entry->servers[loop].s_addr = *bp++; + + bp += 8; /* partition IDs */ + + for (loop=0; loop<8; loop++) { + tmp = ntohl(*bp++); + if (tmp & AFS_VLSF_RWVOL ) entry->srvtmask[loop] |= AFS_CACHE_VOL_STM_RW; + if (tmp & AFS_VLSF_ROVOL ) entry->srvtmask[loop] |= AFS_CACHE_VOL_STM_RO; + if (tmp & AFS_VLSF_BACKVOL) entry->srvtmask[loop] |= AFS_CACHE_VOL_STM_BAK; + } + + entry->vid[0] = ntohl(*bp++); + entry->vid[1] = ntohl(*bp++); + entry->vid[2] = ntohl(*bp++); + + bp++; /* clone ID */ + + tmp = ntohl(*bp++); /* flags */ + if (tmp & AFS_VLF_RWEXISTS ) entry->vidmask |= AFS_CACHE_VOL_STM_RW; + if (tmp & AFS_VLF_ROEXISTS ) entry->vidmask |= AFS_CACHE_VOL_STM_RO; + if (tmp & AFS_VLF_BACKEXISTS) entry->vidmask |= AFS_CACHE_VOL_STM_BAK; + + ret = -ENOMEDIUM; + if (!entry->vidmask) { + rxrpc_call_abort(op->call,ret); + goto done; + } + +#if 0 /* TODO: remove */ + entry->nservers = 3; + entry->servers[0].s_addr = htonl(0xac101249); + entry->servers[1].s_addr = htonl(0xac101243); + entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/); + + entry->srvtmask[0] = AFS_CACHE_VOL_STM_RO; + entry->srvtmask[1] = AFS_CACHE_VOL_STM_RO; + entry->srvtmask[2] = AFS_CACHE_VOL_STM_RO | AFS_CACHE_VOL_STM_RW; +#endif + + /* success */ + entry->ctime = xtime.tv_sec; + ret = 0; + goto done; + } + + if (op->call->app_call_state==RXRPC_CSTATE_ERROR) { + /* operation error */ + ret = op->call->app_errno; + goto done; + } + + _leave(" = -EAGAIN"); + return -EAGAIN; + + done: + rxrpc_put_call(op->call); + op->call = NULL; + _leave(" = %d",ret); + return ret; +} /* end afs_rxvl_get_entry_by_id_async2() */ + +/*****************************************************************************/ +/* + * handle attention events on an async get-entry-by-ID op + * - called from krxiod + */ +static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call) +{ + afs_async_op_t *op = call->app_user; + + _enter("{op=%p cst=%u}",op,call->app_call_state); + + switch (call->app_call_state) { + case RXRPC_CSTATE_COMPLETE: + afs_kafsasyncd_attend_op(op); + break; + case RXRPC_CSTATE_CLNT_RCV_REPLY: + if (call->app_async_read) + break; + case RXRPC_CSTATE_CLNT_GOT_REPLY: + if (call->app_read_count==0) + break; + printk("kAFS: Reply bigger than expected {cst=%u asyn=%d mark=%d rdy=%u pr=%u%s}", + call->app_call_state, + call->app_async_read, + call->app_mark, + call->app_ready_qty, + call->pkt_rcv_count, + call->app_last_rcv ? " last" : ""); + + rxrpc_call_abort(call,-EBADMSG); + break; + default: + BUG(); + } + + _leave(""); + +} /* end afs_rxvl_get_entry_by_id_attn() */ + +/*****************************************************************************/ +/* + * handle error events on an async get-entry-by-ID op + * - called from krxiod + */ +static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call) +{ + afs_async_op_t *op = call->app_user; + + _enter("{op=%p cst=%u}",op,call->app_call_state); + + afs_kafsasyncd_attend_op(op); + + _leave(""); + +} /* end afs_rxvl_get_entry_by_id_error() */ diff -urNp linux-5240/fs/afs/vlclient.h linux-5250/fs/afs/vlclient.h --- linux-5240/fs/afs/vlclient.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/vlclient.h @@ -0,0 +1,96 @@ +/* vlclient.h: Volume Location Service client interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_VLCLIENT_H +#define _LINUX_AFS_VLCLIENT_H + +#include "types.h" +#include "cache-layout.h" + +enum AFSVL_Errors { + AFSVL_IDEXIST = 363520, /* Volume Id entry exists in vl database */ + AFSVL_IO = 363521, /* I/O related error */ + AFSVL_NAMEEXIST = 363522, /* Volume name entry exists in vl database */ + AFSVL_CREATEFAIL = 363523, /* Internal creation failure */ + AFSVL_NOENT = 363524, /* No such entry */ + AFSVL_EMPTY = 363525, /* Vl database is empty */ + AFSVL_ENTDELETED = 363526, /* Entry is deleted (soft delete) */ + AFSVL_BADNAME = 363527, /* Volume name is illegal */ + AFSVL_BADINDEX = 363528, /* Index is out of range */ + AFSVL_BADVOLTYPE = 363529, /* Bad volume type */ + AFSVL_BADSERVER = 363530, /* Illegal server number (out of range) */ + AFSVL_BADPARTITION = 363531, /* Bad partition number */ + AFSVL_REPSFULL = 363532, /* Run out of space for Replication sites */ + AFSVL_NOREPSERVER = 363533, /* No such Replication server site exists */ + AFSVL_DUPREPSERVER = 363534, /* Replication site already exists */ + AFSVL_RWNOTFOUND = 363535, /* Parent R/W entry not found */ + AFSVL_BADREFCOUNT = 363536, /* Illegal Reference Count number */ + AFSVL_SIZEEXCEEDED = 363537, /* Vl size for attributes exceeded */ + AFSVL_BADENTRY = 363538, /* Bad incoming vl entry */ + AFSVL_BADVOLIDBUMP = 363539, /* Illegal max volid increment */ + AFSVL_IDALREADYHASHED = 363540, /* RO/BACK id already hashed */ + AFSVL_ENTRYLOCKED = 363541, /* Vl entry is already locked */ + AFSVL_BADVOLOPER = 363542, /* Bad volume operation code */ + AFSVL_BADRELLOCKTYPE = 363543, /* Bad release lock type */ + AFSVL_RERELEASE = 363544, /* Status report: last release was aborted */ + AFSVL_BADSERVERFLAG = 363545, /* Invalid replication site server °ag */ + AFSVL_PERM = 363546, /* No permission access */ + AFSVL_NOMEM = 363547, /* malloc/realloc failed to alloc enough memory */ +}; + +/* maps to "struct vldbentry" in vvl-spec.pdf */ +struct afsvl_dbentry { + char name[65]; /* name of volume (including NUL char) */ + afs_voltype_t type; /* volume type */ + unsigned num_servers; /* num servers that hold instances of this vol */ + unsigned clone_id; /* cloning ID */ + + unsigned flags; +#define AFS_VLF_RWEXISTS 0x1000 /* R/W volume exists */ +#define AFS_VLF_ROEXISTS 0x2000 /* R/O volume exists */ +#define AFS_VLF_BACKEXISTS 0x4000 /* backup volume exists */ + + afs_volid_t volume_ids[3]; /* volume IDs */ + + struct { + struct in_addr addr; /* server address */ + unsigned partition; /* partition ID on this server */ + unsigned flags; /* server specific flags */ +#define AFS_VLSF_NEWREPSITE 0x0001 /* unused */ +#define AFS_VLSF_ROVOL 0x0002 /* this server holds a R/O instance of the volume */ +#define AFS_VLSF_RWVOL 0x0004 /* this server holds a R/W instance of the volume */ +#define AFS_VLSF_BACKVOL 0x0008 /* this server holds a backup instance of the volume */ + } servers[8]; + +}; + +/* probe a volume location server to see if it is still alive */ +extern int afs_rxvl_probe(afs_server_t *server, int alloc_flags); + +/* look up a volume location database entry by name */ +extern int afs_rxvl_get_entry_by_name(afs_server_t *server, + const char *volname, + struct afs_cache_volume *entry); + +/* look up a volume location database entry by ID */ +extern int afs_rxvl_get_entry_by_id(afs_server_t *server, + afs_volid_t volid, + afs_voltype_t voltype, + struct afs_cache_volume *entry); + +extern int afs_rxvl_get_entry_by_id_async(afs_async_op_t *op, + afs_volid_t volid, + afs_voltype_t voltype); + +extern int afs_rxvl_get_entry_by_id_async2(afs_async_op_t *op, + struct afs_cache_volume *entry); + +#endif /* _LINUX_AFS_VLCLIENT_H */ diff -urNp linux-5240/fs/afs/vlocation.c linux-5250/fs/afs/vlocation.c --- linux-5240/fs/afs/vlocation.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/vlocation.c @@ -0,0 +1,833 @@ +/* vlocation.c: volume location management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "volume.h" +#include "cell.h" +#include "cmservice.h" +#include "fsclient.h" +#include "vlclient.h" +#include "cache.h" +#include "kafstimod.h" +#include +#include "internal.h" + +#define AFS_VLDB_TIMEOUT HZ*1000 + +static void afs_vlocation_update_timer(afs_timer_t *timer); +static void afs_vlocation_update_attend(afs_async_op_t *op); +static void afs_vlocation_update_discard(afs_async_op_t *op); + +static void __afs_vlocation_timeout(afs_timer_t *timer) +{ + afs_vlocation_t *vlocation = list_entry(timer,afs_vlocation_t,timeout); + + _debug("VL TIMEOUT [%s{u=%d}]",vlocation->vldb.name,atomic_read(&vlocation->usage)); + + afs_vlocation_do_timeout(vlocation); +} + +static const struct afs_timer_ops afs_vlocation_timer_ops = { + timed_out: __afs_vlocation_timeout, +}; + +static const struct afs_timer_ops afs_vlocation_update_timer_ops = { + timed_out: afs_vlocation_update_timer, +}; + +static const struct afs_async_op_ops afs_vlocation_update_op_ops = { + attend: afs_vlocation_update_attend, + discard: afs_vlocation_update_discard, +}; + +static LIST_HEAD(afs_vlocation_update_pendq); /* queue of VLs awaiting update */ +static afs_vlocation_t *afs_vlocation_update; /* VL currently being updated */ +static spinlock_t afs_vlocation_update_lock = SPIN_LOCK_UNLOCKED; /* lock guarding update queue */ + +/*****************************************************************************/ +/* + * iterate through the VL servers in a cell until one of them admits knowing about the volume in + * question + * - caller must have cell->vl_sem write-locked + */ +static int afs_vlocation_access_vl_by_name(afs_vlocation_t *vlocation, + const char *name, + struct afs_cache_volume *vldb) +{ + afs_server_t *server = NULL; + afs_cell_t *cell = vlocation->cell; + int count, ret; + + _enter("%s,%s,",cell->name,name); + + ret = -ENOMEDIUM; + for (count=cell->vl_naddrs; count>0; count--) { + _debug("CellServ[%hu]: %08x", + cell->vl_curr_svix,cell->vl_addrs[cell->vl_curr_svix].s_addr); + + /* try and create a server */ + ret = afs_server_lookup(cell,&cell->vl_addrs[cell->vl_curr_svix],&server); + switch (ret) { + case 0: + break; + case -ENOMEM: + case -ENONET: + goto out; + default: + goto rotate; + } + + /* attempt to access the VL server */ + ret = afs_rxvl_get_entry_by_name(server,name,vldb); + switch (ret) { + case 0: + afs_put_server(server); + vlocation->vldb.cell_ix = cell->cache_ix; + goto out; + case -ENOMEM: + case -ENONET: + case -ENETUNREACH: + case -EHOSTUNREACH: + case -ECONNREFUSED: + down_write(&server->sem); + if (server->vlserver) { + rxrpc_put_connection(server->vlserver); + server->vlserver = NULL; + } + up_write(&server->sem); + afs_put_server(server); + if (ret==-ENOMEM || ret==-ENONET) + goto out; + goto rotate; + case -ENOMEDIUM: + afs_put_server(server); + goto out; + default: + afs_put_server(server); + ret = -ENOMEDIUM; + goto rotate; + } + + /* rotate the server records upon lookup failure */ + rotate: + cell->vl_curr_svix++; + cell->vl_curr_svix %= cell->vl_naddrs; + } + + out: + _leave(" = %d",ret); + return ret; + +} /* end afs_vlocation_access_vl_by_name() */ + +/*****************************************************************************/ +/* + * iterate through the VL servers in a cell until one of them admits knowing about the volume in + * question + * - caller must have cell->vl_sem write-locked + */ +static int afs_vlocation_access_vl_by_id(afs_vlocation_t *vlocation, + afs_volid_t volid, + afs_voltype_t voltype, + struct afs_cache_volume *vldb) +{ + afs_server_t *server = NULL; + afs_cell_t *cell = vlocation->cell; + int count, ret; + + _enter("%s,%x,%d,",cell->name,volid,voltype); + + ret = -ENOMEDIUM; + for (count=cell->vl_naddrs; count>0; count--) { + _debug("CellServ[%hu]: %08x", + cell->vl_curr_svix,cell->vl_addrs[cell->vl_curr_svix].s_addr); + + /* try and create a server */ + ret = afs_server_lookup(cell,&cell->vl_addrs[cell->vl_curr_svix],&server); + switch (ret) { + case 0: + break; + case -ENOMEM: + case -ENONET: + goto out; + default: + goto rotate; + } + + /* attempt to access the VL server */ + ret = afs_rxvl_get_entry_by_id(server,volid,voltype,vldb); + switch (ret) { + case 0: + afs_put_server(server); + vlocation->vldb.cell_ix = cell->cache_ix; + goto out; + case -ENOMEM: + case -ENONET: + case -ENETUNREACH: + case -EHOSTUNREACH: + case -ECONNREFUSED: + down_write(&server->sem); + if (server->vlserver) { + rxrpc_put_connection(server->vlserver); + server->vlserver = NULL; + } + up_write(&server->sem); + afs_put_server(server); + if (ret==-ENOMEM || ret==-ENONET) + goto out; + goto rotate; + case -ENOMEDIUM: + afs_put_server(server); + goto out; + default: + afs_put_server(server); + ret = -ENOMEDIUM; + goto rotate; + } + + /* rotate the server records upon lookup failure */ + rotate: + cell->vl_curr_svix++; + cell->vl_curr_svix %= cell->vl_naddrs; + } + + out: + _leave(" = %d",ret); + return ret; + +} /* end afs_vlocation_access_vl_by_id() */ + +/*****************************************************************************/ +/* + * lookup volume location + * - caller must have cell->vol_sem write-locked + * - iterate through the VL servers in a cell until one of them admits knowing about the volume in + * question + * - lookup in the local cache if not able to find on the VL server + * - insert/update in the local cache if did get a VL response + */ +int afs_vlocation_lookup(afs_cache_t *cache, afs_cell_t *cell, const char *name, + afs_vlocation_t **_vlocation) +{ + struct afs_cache_volume vldb; + struct list_head *_p; + afs_vlocation_t *vlocation; + afs_voltype_t voltype; + afs_volid_t vid; + int active = 0, ret; + + _enter(",%s,%s,",cell->name,name); + + if (strlen(name)>sizeof(vlocation->vldb.name)) { + _leave(" = -ENAMETOOLONG"); + return -ENAMETOOLONG; + } + + /* search the cell's active list first */ + list_for_each(_p,&cell->vl_list) { + vlocation = list_entry(_p,afs_vlocation_t,link); + if (strncmp(vlocation->vldb.name,name,sizeof(vlocation->vldb.name))==0) + goto found_in_memory; + } + + /* search the cell's graveyard list second */ + spin_lock(&cell->vl_gylock); + list_for_each(_p,&cell->vl_graveyard) { + vlocation = list_entry(_p,afs_vlocation_t,link); + if (strncmp(vlocation->vldb.name,name,sizeof(vlocation->vldb.name))==0) + goto found_in_graveyard; + } + spin_unlock(&cell->vl_gylock); + + /* not in the cell's in-memory lists - create a new record */ + vlocation = kmalloc(sizeof(afs_vlocation_t),GFP_KERNEL); + if (!vlocation) + return -ENOMEM; + + memset(vlocation,0,sizeof(afs_vlocation_t)); + atomic_set(&vlocation->usage,1); + INIT_LIST_HEAD(&vlocation->link); + rwlock_init(&vlocation->lock); + strncpy(vlocation->vldb.name,name,sizeof(vlocation->vldb.name)); + + afs_timer_init(&vlocation->timeout,&afs_vlocation_timer_ops); + afs_timer_init(&vlocation->upd_timer,&afs_vlocation_update_timer_ops); + afs_async_op_init(&vlocation->upd_op,&afs_vlocation_update_op_ops); + +#if 0 + afs_get_cache(cache); + vlocation->cache = cache; +#endif + afs_get_cell(cell); + vlocation->cell = cell; + vlocation->vldb.cell_ix = cell->cache_ix; + + list_add_tail(&vlocation->link,&cell->vl_list); + +#if 0 + /* search local cache if wasn't in memory */ + ret = afs_cache_lookup_vlocation(vlocation); + switch (ret) { + default: goto error; /* disk error */ + case 0: goto found_in_cache; /* pulled from local cache into memory */ + case -ENOENT: break; /* not in local cache */ + } +#endif + + /* try to look up an unknown volume in the cell VL databases by name */ + ret = afs_vlocation_access_vl_by_name(vlocation,name,&vldb); + if (ret<0) { + printk("kAFS: failed to locate '%s' in cell '%s'\n",name,cell->name); + goto error; + } + + goto found_on_vlserver; + + found_in_graveyard: + /* found in the graveyard - resurrect */ + _debug("found in graveyard"); + atomic_inc(&vlocation->usage); + list_del(&vlocation->link); + list_add_tail(&vlocation->link,&cell->vl_list); + spin_unlock(&cell->vl_gylock); + + afs_kafstimod_del_timer(&vlocation->timeout); + goto active; + + found_in_memory: + /* found in memory - check to see if it's active */ + _debug("found in memory"); + atomic_inc(&vlocation->usage); + + active: + active = 1; + +/* found_in_cache: */ + /* try to look up a cached volume in the cell VL databases by ID */ + _debug("found in cache"); + + _debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", + vlocation->vldb.name, + vlocation->vldb.vidmask, + ntohl(vlocation->vldb.servers[0].s_addr),vlocation->vldb.srvtmask[0], + ntohl(vlocation->vldb.servers[1].s_addr),vlocation->vldb.srvtmask[1], + ntohl(vlocation->vldb.servers[2].s_addr),vlocation->vldb.srvtmask[2] + ); + + _debug("Vids: %08x %08x %08x", + vlocation->vldb.vid[0],vlocation->vldb.vid[1],vlocation->vldb.vid[2]); + + if (vlocation->vldb.vidmask & AFS_CACHE_VOL_STM_RW) { + vid = vlocation->vldb.vid[0]; + voltype = AFSVL_RWVOL; + } + else if (vlocation->vldb.vidmask & AFS_CACHE_VOL_STM_RO) { + vid = vlocation->vldb.vid[1]; + voltype = AFSVL_ROVOL; + } + else if (vlocation->vldb.vidmask & AFS_CACHE_VOL_STM_BAK) { + vid = vlocation->vldb.vid[2]; + voltype = AFSVL_BACKVOL; + } + else { + BUG(); + vid = 0; + voltype = 0; + } + + ret = afs_vlocation_access_vl_by_id(vlocation,vid,voltype,&vldb); + switch (ret) { + /* net error */ + default: + printk("kAFS: failed to volume '%s' (%x) up in '%s': %d\n", + name,vid,cell->name,ret); + goto error; + + /* pulled from local cache into memory */ + case 0: + goto found_on_vlserver; + + /* uh oh... looks like the volume got deleted */ + case -ENOMEDIUM: + printk("kAFS: volume '%s' (%x) does not exist '%s'\n",name,vid,cell->name); + + /* TODO: make existing record unavailable */ + goto error; + } + + found_on_vlserver: + _debug("Done VL Lookup: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", + name, + vldb.vidmask, + ntohl(vldb.servers[0].s_addr),vldb.srvtmask[0], + ntohl(vldb.servers[1].s_addr),vldb.srvtmask[1], + ntohl(vldb.servers[2].s_addr),vldb.srvtmask[2] + ); + + _debug("Vids: %08x %08x %08x",vldb.vid[0],vldb.vid[1],vldb.vid[2]); + + if (strncmp(vldb.name,name,sizeof(vlocation->vldb.name))!=0) + printk("kAFS: name of volume '%s' changed to '%s' on server\n",name,vldb.name); + + memcpy(&vlocation->vldb,&vldb,sizeof(vlocation->vldb)); + vlocation->vldb.cell_ix = cell->cache_ix; + +#if 0 + /* add volume entry to local cache */ + ret = afs_cache_update_vlocation(vlocation); + if (ret<0) + goto error; +#endif + + afs_kafstimod_add_timer(&vlocation->upd_timer,10*HZ); + + *_vlocation = vlocation; + _leave(" = 0 (%p)",vlocation); + return 0; + + error: + if (vlocation) { + if (active) { + __afs_put_vlocation(vlocation); + } + else { + list_del(&vlocation->link); + afs_put_cell(vlocation->cell); +#if 0 + afs_put_cache(vlocation->cache); +#endif + kfree(vlocation); + } + } + + _leave(" = %d",ret); + return ret; +} /* end afs_vlocation_lookup() */ + +/*****************************************************************************/ +/* + * finish using a volume location record + * - caller must have cell->vol_sem write-locked + */ +void __afs_put_vlocation(afs_vlocation_t *vlocation) +{ + afs_cell_t *cell = vlocation->cell; + + _enter("%s",vlocation->vldb.name); + + /* sanity check */ + if (atomic_read(&vlocation->usage)<=0) + BUG(); + + spin_lock(&cell->vl_gylock); + if (likely(!atomic_dec_and_test(&vlocation->usage))) { + spin_unlock(&cell->vl_gylock); + _leave(""); + return; + } + + /* move to graveyard queue */ + list_del(&vlocation->link); + list_add_tail(&vlocation->link,&cell->vl_graveyard); + + /* remove from pending timeout queue (refcounted if actually being updated) */ + list_del_init(&vlocation->upd_op.link); + + /* time out in 10 secs */ + afs_kafstimod_del_timer(&vlocation->upd_timer); + afs_kafstimod_add_timer(&vlocation->timeout,10*HZ); + + spin_unlock(&cell->vl_gylock); + + _leave(" [killed]"); +} /* end __afs_put_vlocation() */ + +/*****************************************************************************/ +/* + * finish using a volume location record + */ +void afs_put_vlocation(afs_vlocation_t *vlocation) +{ + afs_cell_t *cell = vlocation->cell; + + down_write(&cell->vl_sem); + __afs_put_vlocation(vlocation); + up_write(&cell->vl_sem); +} /* end afs_put_vlocation() */ + +/*****************************************************************************/ +/* + * timeout vlocation record + * - removes from the cell's graveyard if the usage count is zero + */ +void afs_vlocation_do_timeout(afs_vlocation_t *vlocation) +{ + afs_cell_t *cell; + + _enter("%s",vlocation->vldb.name); + + cell = vlocation->cell; + + if (atomic_read(&vlocation->usage)<0) BUG(); + + /* remove from graveyard if still dead */ + spin_lock(&cell->vl_gylock); + if (atomic_read(&vlocation->usage)==0) + list_del_init(&vlocation->link); + else + vlocation = NULL; + spin_unlock(&cell->vl_gylock); + + if (!vlocation) { + _leave(""); + return; /* resurrected */ + } + + /* we can now destroy it properly */ + afs_put_cell(cell); +#if 0 + afs_put_cache(vlocation->cache); +#endif + + kfree(vlocation); + + _leave(" [destroyed]"); +} /* end afs_vlocation_do_timeout() */ + +/*****************************************************************************/ +/* + * send an update operation to the currently selected server + */ +static int afs_vlocation_update_begin(afs_vlocation_t *vlocation) +{ + afs_voltype_t voltype; + afs_volid_t vid; + int ret; + + _enter("%s{ufs=%u ucs=%u}", + vlocation->vldb.name,vlocation->upd_first_svix,vlocation->upd_curr_svix); + + /* try to look up a cached volume in the cell VL databases by ID */ + if (vlocation->vldb.vidmask & AFS_CACHE_VOL_STM_RW) { + vid = vlocation->vldb.vid[0]; + voltype = AFSVL_RWVOL; + } + else if (vlocation->vldb.vidmask & AFS_CACHE_VOL_STM_RO) { + vid = vlocation->vldb.vid[1]; + voltype = AFSVL_ROVOL; + } + else if (vlocation->vldb.vidmask & AFS_CACHE_VOL_STM_BAK) { + vid = vlocation->vldb.vid[2]; + voltype = AFSVL_BACKVOL; + } + else { + BUG(); + vid = 0; + voltype = 0; + } + + /* contact the chosen server */ + ret = afs_server_lookup(vlocation->cell, + &vlocation->cell->vl_addrs[vlocation->upd_curr_svix], + &vlocation->upd_op.server); + switch (ret) { + case 0: + break; + case -ENOMEM: + case -ENONET: + default: + _leave(" = %d",ret); + return ret; + } + + /* initiate the update operation */ + ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op,vid,voltype); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + _leave(" = %d",ret); + return ret; +} /* end afs_vlocation_update_begin() */ + +/*****************************************************************************/ +/* + * abandon updating a VL record + * - does not restart the update timer + */ +static void afs_vlocation_update_abandon(afs_vlocation_t *vlocation, + afs_vlocation_upd_t state, + int ret) +{ + _enter("%s,%u",vlocation->vldb.name,state); + + if (ret<0) + printk("kAFS: Abandoning VL update '%s': %d\n",vlocation->vldb.name,ret); + + /* discard the server record */ + if (vlocation->upd_op.server) { + afs_put_server(vlocation->upd_op.server); + vlocation->upd_op.server = NULL; + } + + spin_lock(&afs_vlocation_update_lock); + afs_vlocation_update = NULL; + vlocation->upd_state = state; + + /* TODO: start updating next VL record on pending list */ + + spin_unlock(&afs_vlocation_update_lock); + + _leave(""); +} /* end afs_vlocation_update_abandon() */ + +/*****************************************************************************/ +/* + * handle periodic update timeouts and busy retry timeouts + * - called from kafstimod + */ +static void afs_vlocation_update_timer(afs_timer_t *timer) +{ + afs_vlocation_t *vlocation = list_entry(timer,afs_vlocation_t,upd_timer); + int ret; + + _enter("%s",vlocation->vldb.name); + + /* only update if not in the graveyard (defend against putting too) */ + spin_lock(&vlocation->cell->vl_gylock); + + if (!atomic_read(&vlocation->usage)) + goto out_unlock1; + + spin_lock(&afs_vlocation_update_lock); + + /* if we were woken up due to EBUSY sleep then restart immediately if possible or else jump + * to front of pending queue */ + if (vlocation->upd_state==AFS_VLUPD_BUSYSLEEP) { + if (afs_vlocation_update) { + list_add(&vlocation->upd_op.link,&afs_vlocation_update_pendq); + } + else { + afs_get_vlocation(vlocation); + afs_vlocation_update = vlocation; + vlocation->upd_state = AFS_VLUPD_INPROGRESS; + } + goto out_unlock2; + } + + /* put on pending queue if there's already another update in progress */ + if (afs_vlocation_update) { + vlocation->upd_state = AFS_VLUPD_PENDING; + list_add_tail(&vlocation->upd_op.link,&afs_vlocation_update_pendq); + goto out_unlock2; + } + + /* hold a ref on it while actually updating */ + afs_get_vlocation(vlocation); + afs_vlocation_update = vlocation; + vlocation->upd_state = AFS_VLUPD_INPROGRESS; + + spin_unlock(&afs_vlocation_update_lock); + spin_unlock(&vlocation->cell->vl_gylock); + + /* okay... we can start the update */ + _debug("BEGIN VL UPDATE [%s]",vlocation->vldb.name); + vlocation->upd_first_svix = vlocation->cell->vl_curr_svix; + vlocation->upd_curr_svix = vlocation->upd_first_svix; + vlocation->upd_rej_cnt = 0; + vlocation->upd_busy_cnt = 0; + + ret = afs_vlocation_update_begin(vlocation); + if (ret<0) { + afs_vlocation_update_abandon(vlocation,AFS_VLUPD_SLEEP,ret); + afs_kafstimod_add_timer(&vlocation->upd_timer,AFS_VLDB_TIMEOUT); + afs_put_vlocation(vlocation); + } + + _leave(""); + return; + + out_unlock2: + spin_unlock(&afs_vlocation_update_lock); + out_unlock1: + spin_unlock(&vlocation->cell->vl_gylock); + _leave(""); + return; + +} /* end afs_vlocation_update_timer() */ + +/*****************************************************************************/ +/* + * attend to an update operation upon which an event happened + * - called in kafsasyncd context + */ +static void afs_vlocation_update_attend(afs_async_op_t *op) +{ + struct afs_cache_volume vldb; + afs_vlocation_t *vlocation = list_entry(op,afs_vlocation_t,upd_op); + unsigned tmp; + int ret; + + _enter("%s",vlocation->vldb.name); + + ret = afs_rxvl_get_entry_by_id_async2(op,&vldb); + switch (ret) { + case -EAGAIN: + _leave(" [unfinished]"); + return; + + case 0: + _debug("END VL UPDATE: %d\n",ret); + vlocation->valid = 1; + + _debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }", + vldb.vidmask, + ntohl(vldb.servers[0].s_addr),vldb.srvtmask[0], + ntohl(vldb.servers[1].s_addr),vldb.srvtmask[1], + ntohl(vldb.servers[2].s_addr),vldb.srvtmask[2] + ); + + _debug("Vids: %08x %08x %08x",vldb.vid[0],vldb.vid[1],vldb.vid[2]); + + afs_vlocation_update_abandon(vlocation,AFS_VLUPD_SLEEP,0); + + down_write(&vlocation->cell->vl_sem); + + /* actually update the cache */ + if (strncmp(vldb.name,vlocation->vldb.name,sizeof(vlocation->vldb.name))!=0) + printk("kAFS: name of volume '%s' changed to '%s' on server\n", + vlocation->vldb.name,vldb.name); + + memcpy(&vlocation->vldb,&vldb,sizeof(vlocation->vldb)); + vlocation->vldb.cell_ix = vlocation->cell->cache_ix; + +#if 0 + /* add volume entry to local cache */ + ret = afs_cache_update_vlocation(vlocation); +#endif + + up_write(&vlocation->cell->vl_sem); + + if (ret<0) + printk("kAFS: failed to update local cache: %d\n",ret); + + afs_kafstimod_add_timer(&vlocation->upd_timer,AFS_VLDB_TIMEOUT); + afs_put_vlocation(vlocation); + _leave(" [found]"); + return; + + case -ENOMEDIUM: + vlocation->upd_rej_cnt++; + goto try_next; + + /* the server is locked - retry in a very short while */ + case -EBUSY: + vlocation->upd_busy_cnt++; + if (vlocation->upd_busy_cnt>3) + goto try_next; /* too many retries */ + + afs_vlocation_update_abandon(vlocation,AFS_VLUPD_BUSYSLEEP,0); + afs_kafstimod_add_timer(&vlocation->upd_timer,HZ/2); + afs_put_vlocation(vlocation); + _leave(" [busy]"); + return; + + case -ENETUNREACH: + case -EHOSTUNREACH: + case -ECONNREFUSED: + case -EREMOTEIO: + /* record bad vlserver info in the cell too + * - TODO: use down_write_trylock() if available + */ + if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix) + vlocation->cell->vl_curr_svix = + vlocation->cell->vl_curr_svix % vlocation->cell->vl_naddrs; + + case -EBADRQC: + case -EINVAL: + case -EACCES: + case -EBADMSG: + goto try_next; + + default: + goto abandon; + } + + /* try contacting the next server */ + try_next: + vlocation->upd_busy_cnt = 0; + + if (vlocation->upd_op.server) { + /* discard the server record */ + afs_put_server(vlocation->upd_op.server); + vlocation->upd_op.server = NULL; + } + + tmp = vlocation->cell->vl_naddrs; + if (tmp==0) + goto abandon; + + vlocation->upd_curr_svix++; + if (vlocation->upd_curr_svix >= tmp) vlocation->upd_curr_svix = 0; + if (vlocation->upd_first_svix >= tmp) vlocation->upd_first_svix = tmp - 1; + + /* move to the next server */ + if (vlocation->upd_curr_svix!=vlocation->upd_first_svix) { + afs_vlocation_update_begin(vlocation); + _leave(" [next]"); + return; + } + + /* run out of servers to try - was the volume rejected? */ + if (vlocation->upd_rej_cnt>0) { + printk("kAFS: Active volume no longer valid '%s'\n",vlocation->vldb.name); + vlocation->valid = 0; + afs_vlocation_update_abandon(vlocation,AFS_VLUPD_SLEEP,0); + afs_kafstimod_add_timer(&vlocation->upd_timer,AFS_VLDB_TIMEOUT); + afs_put_vlocation(vlocation); + _leave(" [invalidated]"); + return; + } + + /* abandon the update */ + abandon: + afs_vlocation_update_abandon(vlocation,AFS_VLUPD_SLEEP,ret); + afs_kafstimod_add_timer(&vlocation->upd_timer,HZ*10); + afs_put_vlocation(vlocation); + _leave(" [abandoned]"); + +} /* end afs_vlocation_update_attend() */ + +/*****************************************************************************/ +/* + * deal with an update operation being discarded + * - called in kafsasyncd context when it's dying due to rmmod + * - the call has already been aborted and put()'d + */ +static void afs_vlocation_update_discard(afs_async_op_t *op) +{ + afs_vlocation_t *vlocation = list_entry(op,afs_vlocation_t,upd_op); + + _enter("%s",vlocation->vldb.name); + + afs_put_server(op->server); + op->server = NULL; + + afs_put_vlocation(vlocation); + + _leave(""); +} /* end afs_vlocation_update_discard() */ diff -urNp linux-5240/fs/afs/vnode.c linux-5250/fs/afs/vnode.c --- linux-5240/fs/afs/vnode.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/vnode.c @@ -0,0 +1,317 @@ +/* vnode.c: AFS vnode management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "volume.h" +#include "cell.h" +#include "cmservice.h" +#include "fsclient.h" +#include "vlclient.h" +#include "cache.h" +#include "vnode.h" +#include "internal.h" + +static void afs_vnode_cb_timed_out(struct afs_timer *timer); + +struct afs_timer_ops afs_vnode_cb_timed_out_ops = { + timed_out: afs_vnode_cb_timed_out, +}; + +/*****************************************************************************/ +/* + * handle a callback timing out + * TODO: retain a ref to vnode struct for an outstanding callback timeout + */ +static void afs_vnode_cb_timed_out(struct afs_timer *timer) +{ + afs_server_t *oldserver; + afs_vnode_t *vnode; + + vnode = list_entry(timer,afs_vnode_t,cb_timeout); + + _enter("%p",vnode); + + /* set the changed flag in the vnode and release the server */ + spin_lock(&vnode->lock); + + oldserver = xchg(&vnode->cb_server,NULL); + if (oldserver) { + vnode->flags |= AFS_VNODE_CHANGED; + + spin_lock(&afs_cb_hash_lock); + list_del_init(&vnode->cb_hash_link); + spin_unlock(&afs_cb_hash_lock); + + spin_lock(&oldserver->cb_lock); + list_del_init(&vnode->cb_link); + spin_unlock(&oldserver->cb_lock); + } + + spin_unlock(&vnode->lock); + + if (oldserver) + afs_put_server(oldserver); + + _leave(""); +} /* end afs_vnode_cb_timed_out() */ + +/*****************************************************************************/ +/* + * finish off updating the recorded status of a file + * - starts callback expiry timer + * - adds to server's callback list + */ +void afs_vnode_finalise_status_update(afs_vnode_t *vnode, afs_server_t *server, int ret) +{ + afs_server_t *oldserver = NULL; + + _enter("%p,%p,%d",vnode,server,ret); + + spin_lock(&vnode->lock); + + vnode->flags &= ~AFS_VNODE_CHANGED; + + if (ret==0) { + /* adjust the callback timeout appropriately */ + afs_kafstimod_add_timer(&vnode->cb_timeout,vnode->cb_expiry*HZ); + + spin_lock(&afs_cb_hash_lock); + list_del(&vnode->cb_hash_link); + list_add_tail(&vnode->cb_hash_link,&afs_cb_hash(server,&vnode->fid)); + spin_unlock(&afs_cb_hash_lock); + + /* swap ref to old callback server with that for new callback server */ + oldserver = xchg(&vnode->cb_server,server); + if (oldserver!=server) { + if (oldserver) { + spin_lock(&oldserver->cb_lock); + list_del_init(&vnode->cb_link); + spin_unlock(&oldserver->cb_lock); + } + + afs_get_server(server); + spin_lock(&server->cb_lock); + list_add_tail(&vnode->cb_link,&server->cb_promises); + spin_unlock(&server->cb_lock); + } + else { + /* same server */ + oldserver = NULL; + } + } + else if (ret==-ENOENT) { + /* the file was deleted - clear the callback timeout */ + oldserver = xchg(&vnode->cb_server,NULL); + afs_kafstimod_del_timer(&vnode->cb_timeout); + + _debug("got NOENT from server - marking file deleted"); + vnode->flags |= AFS_VNODE_DELETED; + } + + vnode->update_cnt--; + + spin_unlock(&vnode->lock); + + wake_up_all(&vnode->update_waitq); + + if (oldserver) + afs_put_server(oldserver); + + _leave(""); + +} /* end afs_vnode_finalise_status_update() */ + +/*****************************************************************************/ +/* + * fetch file status from the volume + * - don't issue a fetch if: + * - the changed bit is not set and there's a valid callback + * - there are any outstanding ops that will fetch the status + * - TODO implement local caching + */ +int afs_vnode_fetch_status(afs_vnode_t *vnode) +{ + afs_server_t *server; + int ret; + + DECLARE_WAITQUEUE(myself,current); + + _enter("%s,{%u,%u,%u}",vnode->volume->vlocation->vldb.name, + vnode->fid.vid,vnode->fid.vnode,vnode->fid.unique); + + if (!(vnode->flags & AFS_VNODE_CHANGED) && vnode->cb_server) { + _leave(" [unchanged]"); + return 0; + } + + if (vnode->flags & AFS_VNODE_DELETED) { + _leave(" [deleted]"); + return -ENOENT; + } + + spin_lock(&vnode->lock); + + if (!(vnode->flags & AFS_VNODE_CHANGED)) { + spin_unlock(&vnode->lock); + _leave(" [unchanged]"); + return 0; + } + + if (vnode->update_cnt>0) { + /* someone else started a fetch */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&vnode->update_waitq,&myself); + + /* wait for the status to be updated */ + for (;;) { + if (!(vnode->flags & AFS_VNODE_CHANGED)) break; + if (vnode->flags & AFS_VNODE_DELETED) break; + + /* it got updated and invalidated all before we saw it */ + if (vnode->update_cnt==0) { + remove_wait_queue(&vnode->update_waitq,&myself); + set_current_state(TASK_RUNNING); + goto get_anyway; + } + + spin_unlock(&vnode->lock); + + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + + spin_lock(&vnode->lock); + } + + remove_wait_queue(&vnode->update_waitq,&myself); + spin_unlock(&vnode->lock); + set_current_state(TASK_RUNNING); + + return vnode->flags & AFS_VNODE_DELETED ? -ENOENT : 0; + } + + get_anyway: + /* okay... we're going to have to initiate the op */ + vnode->update_cnt++; + + spin_unlock(&vnode->lock); + + /* merge AFS status fetches and clear outstanding callback on this vnode */ + do { + /* pick a server to query */ + ret = afs_volume_pick_fileserver(vnode->volume,&server); + if (ret<0) + return ret; + + _debug("USING SERVER: %08x\n",ntohl(server->addr.s_addr)); + + ret = afs_rxfs_fetch_file_status(server,vnode,NULL); + + } while (!afs_volume_release_fileserver(vnode->volume,server,ret)); + + /* adjust the flags */ + afs_vnode_finalise_status_update(vnode,server,ret); + + _leave(" = %d",ret); + return ret; +} /* end afs_vnode_fetch_status() */ + +/*****************************************************************************/ +/* + * fetch file data from the volume + * - TODO implement caching and server failover + */ +int afs_vnode_fetch_data(afs_vnode_t *vnode, struct afs_rxfs_fetch_descriptor *desc) +{ + afs_server_t *server; + int ret; + + _enter("%s,{%u,%u,%u}", + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique); + + /* this op will fetch the status */ + spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); + + /* merge in AFS status fetches and clear outstanding callback on this vnode */ + do { + /* pick a server to query */ + ret = afs_volume_pick_fileserver(vnode->volume,&server); + if (ret<0) + return ret; + + _debug("USING SERVER: %08x\n",ntohl(server->addr.s_addr)); + + ret = afs_rxfs_fetch_file_data(server,vnode,desc,NULL); + + } while (!afs_volume_release_fileserver(vnode->volume,server,ret)); + + /* adjust the flags */ + afs_vnode_finalise_status_update(vnode,server,ret); + + _leave(" = %d",ret); + return ret; + +} /* end afs_vnode_fetch_data() */ + +/*****************************************************************************/ +/* + * break any outstanding callback on a vnode + * - only relevent to server that issued it + */ +int afs_vnode_give_up_callback(afs_vnode_t *vnode) +{ + afs_server_t *server; + int ret; + + _enter("%s,{%u,%u,%u}", + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique); + + spin_lock(&afs_cb_hash_lock); + list_del_init(&vnode->cb_hash_link); + spin_unlock(&afs_cb_hash_lock); + + /* set the changed flag in the vnode and release the server */ + spin_lock(&vnode->lock); + + afs_kafstimod_del_timer(&vnode->cb_timeout); + + server = xchg(&vnode->cb_server,NULL); + if (server) { + vnode->flags |= AFS_VNODE_CHANGED; + + spin_lock(&server->cb_lock); + list_del_init(&vnode->cb_link); + spin_unlock(&server->cb_lock); + } + + spin_unlock(&vnode->lock); + + ret = 0; + if (server) { + ret = afs_rxfs_give_up_callback(server,vnode); + afs_put_server(server); + } + + _leave(" = %d",ret); + return ret; +} /* end afs_vnode_give_up_callback() */ diff -urNp linux-5240/fs/afs/vnode.h linux-5250/fs/afs/vnode.h --- linux-5240/fs/afs/vnode.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/vnode.h @@ -0,0 +1,88 @@ +/* vnode.h: AFS vnode record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_VNODE_H +#define _LINUX_AFS_VNODE_H + +#include +#include +#include "server.h" +#include "kafstimod.h" + +#ifdef __KERNEL__ + +struct afs_rxfs_fetch_descriptor; + +/*****************************************************************************/ +/* + * AFS inode private data + */ +struct afs_vnode +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + struct inode vfs_inode; /* the VFS's inode record */ +#else + struct inode *inode; /* the VFS's inode */ +#endif + + afs_volume_t *volume; /* volume on which vnode resides */ + afs_fid_t fid; /* the file identifier for this inode */ + afs_file_status_t status; /* AFS status info for this file */ + unsigned nix; /* vnode index in cache */ + + wait_queue_head_t update_waitq; /* status fetch waitqueue */ + unsigned update_cnt; /* number of outstanding ops that will update the + * status */ + spinlock_t lock; /* waitqueue/flags lock */ + unsigned flags; +#define AFS_VNODE_CHANGED 0x00000001 /* set if vnode reported changed by callback */ +#define AFS_VNODE_DELETED 0x00000002 /* set if vnode deleted on server */ +#define AFS_VNODE_MOUNTPOINT 0x00000004 /* set if vnode is a mountpoint symlink */ + + /* outstanding callback notification on this file */ + afs_server_t *cb_server; /* server that made the current promise */ + struct list_head cb_link; /* link in server's promises list */ + struct list_head cb_hash_link; /* link in master callback hash */ + afs_timer_t cb_timeout; /* timeout on promise */ + unsigned cb_version; /* callback version */ + unsigned cb_expiry; /* callback expiry time */ + afs_callback_type_t cb_type; /* type of callback */ +}; + +static inline afs_vnode_t *AFS_FS_I(struct inode *inode) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + return list_entry(inode,afs_vnode_t,vfs_inode); +#else + return inode->u.generic_ip; +#endif +} + +static inline struct inode *AFS_VNODE_TO_I(afs_vnode_t *vnode) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + return &vnode->vfs_inode; +#else + return vnode->inode; +#endif +} + +extern int afs_vnode_fetch_status(afs_vnode_t *vnode); + +extern int afs_vnode_fetch_data(afs_vnode_t *vnode, struct afs_rxfs_fetch_descriptor *desc); + +extern int afs_vnode_give_up_callback(afs_vnode_t *vnode); + +extern struct afs_timer_ops afs_vnode_cb_timed_out_ops; + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_AFS_VNODE_H */ diff -urNp linux-5240/fs/afs/volume.c linux-5250/fs/afs/volume.c --- linux-5240/fs/afs/volume.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/volume.c @@ -0,0 +1,434 @@ +/* volume.c: AFS volume management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "volume.h" +#include "cell.h" +#include "cmservice.h" +#include "fsclient.h" +#include "vlclient.h" +#include "cache.h" +#include "internal.h" + +const char *afs_voltypes[] = { "R/W", "R/O", "BAK" }; + +/*****************************************************************************/ +/* + * lookup a volume by name + * - this can be one of the following: + * "%[cell:]volume[.]" R/W volume + * "#[cell:]volume[.]" R/O or R/W volume (rwparent=0), or R/W (rwparent=1) volume + * "%[cell:]volume.readonly" R/O volume + * "#[cell:]volume.readonly" R/O volume + * "%[cell:]volume.backup" Backup volume + * "#[cell:]volume.backup" Backup volume + * + * The cell name is optional, and defaults to the current cell. + * + * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin Guide + * - Rule 1: Explicit type suffix forces access of that type or nothing + * (no suffix, then use Rule 2 & 3) + * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W if not available + * - Rule 3: If parent volume is R/W, then only mount R/W volume unless explicitly told otherwise + */ +int afs_volume_lookup(afs_cache_t *cache, char *name, int rwparent, afs_volume_t **_volume) +{ + afs_vlocation_t *vlocation = NULL; + afs_voltype_t type; + afs_volume_t *volume = NULL; + afs_cell_t *cell = NULL; + char *cellname, *volname, *suffix; + char srvtmask; + int force, ret, loop; + + _enter(",%s,",name); + + if (!name || (name[0]!='%' && name[0]!='#') || !name[1]) { + printk("kAFS: unparsable volume name\n"); + return -EINVAL; + } + + /* determine the type of volume we're looking for */ + force = 0; + type = AFSVL_ROVOL; + + if (rwparent || name[0]=='%') { + type = AFSVL_RWVOL; + force = 1; + } + + suffix = strrchr(name,'.'); + if (suffix) { + if (strcmp(suffix,".readonly")==0) { + type = AFSVL_ROVOL; + force = 1; + } + else if (strcmp(suffix,".backup")==0) { + type = AFSVL_BACKVOL; + force = 1; + } + else if (suffix[1]==0) { + *suffix = 0; + suffix = NULL; + } + else { + suffix = NULL; + } + } + + /* split the cell and volume names */ + name++; + volname = strchr(name,':'); + if (volname) { + *volname++ = 0; + cellname = name; + } + else { + volname = name; + cellname = NULL; + } + + _debug("CELL:%s VOLUME:%s SUFFIX:%s TYPE:%d%s", + cellname,volname,suffix?:"-",type,force?" FORCE":""); + + /* lookup the cell record */ + ret = afs_cell_lookup(cache,cellname,&cell); + if (ret<0) + printk("kAFS: unable to lookup cell '%s'\n",cellname?:""); + + if (cellname) volname[-1] = ':'; + if (ret<0) + goto error; + + /* lookup the volume location record */ + if (suffix) *suffix = 0; + ret = afs_vlocation_lookup(cache,cell,volname,&vlocation); + if (suffix) *suffix = '.'; + if (ret<0) + goto error; + + /* make the final decision on the type we want */ + ret = -ENOMEDIUM; + if (force && !(vlocation->vldb.vidmask & (1<vldb.nservers; loop++) + srvtmask |= vlocation->vldb.srvtmask[loop]; + + if (force) { + if (!(srvtmask & (1 <vl_sem); + + /* is the volume already active? */ + if (vlocation->vols[type]) { + /* yes - re-use it */ + volume = vlocation->vols[type]; + afs_get_volume(volume); + goto success; + } + + /* create a new volume record */ + _debug("creating new volume record"); + + ret = -ENOMEM; + volume = kmalloc(sizeof(afs_volume_t),GFP_KERNEL); + if (!volume) + goto error_up; + + memset(volume,0,sizeof(afs_volume_t)); + atomic_set(&volume->usage,1); + volume->type = type; + volume->type_force = force; + volume->cell = cell; + volume->cix = cell->cache_ix; + volume->vid = vlocation->vldb.vid[type]; + + volume->vix.index = (vlocation->vix.index << 2) | type; + + init_rwsem(&volume->server_sem); + + /* look up all the applicable server records */ + for (loop=0; loop<8; loop++) { + if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) { + ret = afs_server_lookup(volume->cell, + &vlocation->vldb.servers[loop], + &volume->servers[volume->nservers]); + if (ret<0) + goto error_discard; + + volume->nservers++; + } + } + + /* attach the cache and volume location */ +#if 0 + afs_get_cache(cache); volume->cache = cache; +#endif + afs_get_vlocation(vlocation); volume->vlocation = vlocation; + + vlocation->vols[type] = volume; + + success: + _debug("kAFS selected %s volume %08x",afs_voltypes[volume->type],volume->vid); + *_volume = volume; + ret = 0; + + /* clean up */ + error_up: + up_write(&cell->vl_sem); + error: + if (vlocation) afs_put_vlocation(vlocation); + if (cell) afs_put_cell(cell); + + _leave(" = %d (%p)",ret,volume); + return ret; + + error_discard: + up_write(&cell->vl_sem); + + for (loop=volume->nservers-1; loop>=0; loop--) + if (volume->servers[loop]) + afs_put_server(volume->servers[loop]); + + kfree(volume); + goto error; +} /* end afs_volume_lookup() */ + +/*****************************************************************************/ +/* + * destroy a volume record + */ +void afs_put_volume(afs_volume_t *volume) +{ + afs_vlocation_t *vlocation; + int loop; + + _enter("%p",volume); + + vlocation = volume->vlocation; + + /* sanity check */ + if (atomic_read(&volume->usage)<=0) + BUG(); + + /* to prevent a race, the decrement and the dequeue must be effectively atomic */ + down_write(&vlocation->cell->vl_sem); + + if (likely(!atomic_dec_and_test(&volume->usage))) { + up_write(&vlocation->cell->vl_sem); + _leave(""); + return; + } + + vlocation->vols[volume->type] = NULL; + + up_write(&vlocation->cell->vl_sem); + + afs_put_vlocation(vlocation); + + /* finish cleaning up the volume */ +#if 0 + if (volume->cache) afs_put_cache(volume->cache); +#endif + + for (loop=volume->nservers-1; loop>=0; loop--) + if (volume->servers[loop]) + afs_put_server(volume->servers[loop]); + + kfree(volume); + + _leave(" [destroyed]"); +} /* end afs_put_volume() */ + +/*****************************************************************************/ +/* + * pick a server to use to try accessing this volume + * - returns with an elevated usage count on the server chosen + */ +int afs_volume_pick_fileserver(afs_volume_t *volume, afs_server_t **_server) +{ + afs_server_t *server; + int ret, state, loop; + + _enter("%s",volume->vlocation->vldb.name); + + down_read(&volume->server_sem); + + /* handle the no-server case */ + if (volume->nservers==0) { + ret = volume->rjservers ? -ENOMEDIUM : -ESTALE; + up_read(&volume->server_sem); + _leave(" = %d [no servers]",ret); + return ret; + } + + /* basically, just search the list for the first live server and use that */ + ret = 0; + for (loop=0; loopnservers; loop++) { + server = volume->servers[loop]; + state = server->fs_state; + + switch (state) { + /* found an apparently healthy server */ + case 0: + afs_get_server(server); + up_read(&volume->server_sem); + *_server = server; + _leave(" = 0 (picked %08x)",ntohl(server->addr.s_addr)); + return 0; + + case -ENETUNREACH: + if (ret==0) + ret = state; + break; + + case -EHOSTUNREACH: + if (ret==0 || ret==-ENETUNREACH) + ret = state; + break; + + case -ECONNREFUSED: + if (ret==0 || ret==-ENETUNREACH || ret==-EHOSTUNREACH) + ret = state; + break; + + default: + case -EREMOTEIO: + if (ret==0 || + ret==-ENETUNREACH || + ret==-EHOSTUNREACH || + ret==-ECONNREFUSED) + ret = state; + break; + } + } + + /* no available servers + * - TODO: handle the no active servers case better + */ + up_read(&volume->server_sem); + _leave(" = %d",ret); + return ret; +} /* end afs_volume_pick_fileserver() */ + +/*****************************************************************************/ +/* + * release a server after use + * - releases the ref on the server struct that was acquired by picking + * - records result of using a particular server to access a volume + * - return 0 to try again, 1 if okay or to issue error + */ +int afs_volume_release_fileserver(afs_volume_t *volume, afs_server_t *server, int result) +{ + unsigned loop; + + _enter("%s,%08x,%d",volume->vlocation->vldb.name,ntohl(server->addr.s_addr),result); + + switch (result) { + /* success */ + case 0: + server->fs_act_jif = jiffies; + break; + + /* the fileserver denied all knowledge of the volume */ + case -ENOMEDIUM: + server->fs_act_jif = jiffies; + down_write(&volume->server_sem); + + /* first, find where the server is in the active list (if it is) */ + for (loop=0; loopnservers; loop++) + if (volume->servers[loop]==server) + goto present; + + /* no longer there - may have been discarded by another op */ + goto try_next_server_upw; + + present: + volume->nservers--; + memmove(&volume->servers[loop], + &volume->servers[loop+1], + sizeof(volume->servers[loop]) * (volume->nservers - loop) + ); + volume->servers[volume->nservers] = NULL; + afs_put_server(server); + volume->rjservers++; + + if (volume->nservers>0) + /* another server might acknowledge its existence */ + goto try_next_server_upw; + + /* handle the case where all the fileservers have rejected the volume + * - TODO: try asking the fileservers for volume information + * - TODO: contact the VL server again to see if the volume is no longer registered + */ + up_write(&volume->server_sem); + afs_put_server(server); + _leave(" [completely rejected]"); + return 1; + + /* problem reaching the server */ + case -ENETUNREACH: + case -EHOSTUNREACH: + case -ECONNREFUSED: + case -ETIMEDOUT: + case -EREMOTEIO: + /* mark the server as dead + * TODO: vary dead timeout depending on error + */ + spin_lock(&server->fs_lock); + if (!server->fs_state) { + server->fs_dead_jif = jiffies + HZ * 10; + server->fs_state = result; + printk("kAFS: SERVER DEAD state=%d\n",result); + } + spin_unlock(&server->fs_lock); + goto try_next_server; + + /* miscellaneous error */ + default: + server->fs_act_jif = jiffies; + case -ENOMEM: + case -ENONET: + break; + } + + /* tell the caller to accept the result */ + afs_put_server(server); + _leave(""); + return 1; + + /* tell the caller to loop around and try the next server */ + try_next_server_upw: + up_write(&volume->server_sem); + try_next_server: + afs_put_server(server); + _leave(" [try next server]"); + return 0; + +} /* end afs_volume_release_fileserver() */ diff -urNp linux-5240/fs/afs/volume.h linux-5250/fs/afs/volume.h --- linux-5240/fs/afs/volume.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/fs/afs/volume.h @@ -0,0 +1,102 @@ +/* volume.h: AFS volume management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_VOLUME_H +#define _LINUX_AFS_VOLUME_H + +#include "types.h" +#include "cache-layout.h" +#include "fsclient.h" +#include "kafstimod.h" +#include "kafsasyncd.h" + +#define __packed __attribute__((packed)) + +typedef enum { + AFS_VLUPD_SLEEP, /* sleeping waiting for update timer to fire */ + AFS_VLUPD_PENDING, /* on pending queue */ + AFS_VLUPD_INPROGRESS, /* op in progress */ + AFS_VLUPD_BUSYSLEEP, /* sleeping because server returned EBUSY */ + +} __attribute__((packed)) afs_vlocation_upd_t; + +/*****************************************************************************/ +/* + * AFS volume location record + */ +struct afs_vlocation +{ + atomic_t usage; + struct list_head link; /* link in cell volume location list */ + afs_timer_t timeout; /* decaching timer */ + afs_cell_t *cell; /* cell to which volume belongs */ +#if 0 + afs_cache_t *cache; /* backing cache */ +#endif + afs_cache_volix_t vix; /* volume index in this cache */ + struct afs_cache_volume vldb; /* volume information DB record */ + struct afs_volume *vols[3]; /* volume access record pointer (index by type) */ + rwlock_t lock; /* access lock */ + unsigned long read_jif; /* time at which last read from vlserver */ + afs_timer_t upd_timer; /* update timer */ + afs_async_op_t upd_op; /* update operation */ + afs_vlocation_upd_t upd_state; /* update state */ + unsigned short upd_first_svix; /* first server index during update */ + unsigned short upd_curr_svix; /* current server index during update */ + unsigned short upd_rej_cnt; /* ENOMEDIUM count during update */ + unsigned short upd_busy_cnt; /* EBUSY count during update */ + unsigned short valid; /* T if valid */ +}; + +extern int afs_vlocation_lookup(afs_cache_t *cache, afs_cell_t *cell, const char *name, + afs_vlocation_t **_vlocation); + +#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0) + +extern void __afs_put_vlocation(afs_vlocation_t *vlocation); +extern void afs_put_vlocation(afs_vlocation_t *vlocation); +extern void afs_vlocation_do_timeout(afs_vlocation_t *vlocation); + +/*****************************************************************************/ +/* + * AFS volume access record + */ +struct afs_volume +{ + atomic_t usage; + afs_cell_t *cell; /* cell to which belongs (unrefd ptr) */ + afs_vlocation_t *vlocation; /* volume location */ + afs_volid_t vid; /* volume ID */ + afs_voltype_t __packed type; /* type of volume */ + char type_force; /* force volume type (suppress R/O -> R/W) */ +#if 0 + afs_cache_t *cache; /* backing cache */ +#endif + afs_cache_cellix_t cix; /* cell index in this cache */ + afs_cache_volix_t vix; /* volume index in this cache */ + + unsigned short nservers; /* number of server slots filled */ + unsigned short rjservers; /* number of servers discarded due to -ENOMEDIUM */ + afs_server_t *servers[8]; /* servers on which volume resides (ordered) */ + struct rw_semaphore server_sem; /* lock for accessing current server */ +}; + +extern int afs_volume_lookup(afs_cache_t *cache, char *name, int ro, afs_volume_t **_volume); + +#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0) + +extern void afs_put_volume(afs_volume_t *volume); + +extern int afs_volume_pick_fileserver(afs_volume_t *volume, afs_server_t **_server); + +extern int afs_volume_release_fileserver(afs_volume_t *volume, afs_server_t *server, int result); + +#endif /* _LINUX_AFS_VOLUME_H */ diff -urNp linux-5240/fs/Config.in linux-5250/fs/Config.in --- linux-5240/fs/Config.in +++ linux-5250/fs/Config.in @@ -23,6 +23,7 @@ dep_mbool ' Enable reiserfs debug mode' dep_mbool ' Stats in /proc/fs/reiserfs' CONFIG_REISERFS_PROC_INFO $CONFIG_REISERFS_FS dep_tristate 'ADFS file system support (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL +dep_tristate 'AFS distributed file system support' CONFIG_AFS_FS $CONFIG_EXPERIMENTAL dep_mbool ' ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW $CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL dep_tristate 'Amiga FFS file system support (EXPERIMENTAL)' CONFIG_AFFS_FS $CONFIG_EXPERIMENTAL diff -urNp linux-5240/fs/Makefile linux-5250/fs/Makefile --- linux-5240/fs/Makefile +++ linux-5250/fs/Makefile @@ -67,6 +67,7 @@ subdir-$(CONFIG_UDF_FS) += udf subdir-$(CONFIG_AUTOFS_FS) += autofs subdir-$(CONFIG_AUTOFS4_FS) += autofs4 subdir-$(CONFIG_ADFS_FS) += adfs +subdir-$(CONFIG_AFS_FS) += afs subdir-$(CONFIG_REISERFS_FS) += reiserfs subdir-$(CONFIG_DEVPTS_FS) += devpts subdir-$(CONFIG_SUN_OPENPROMFS) += openpromfs diff -urNp linux-5240/include/rxrpc/call.h linux-5250/include/rxrpc/call.h --- linux-5240/include/rxrpc/call.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/call.h @@ -0,0 +1,218 @@ +/* call.h: Rx call record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_CALL_H +#define _LINUX_RXRPC_CALL_H + +#include +#include +#include +#include + +#define RXRPC_CALL_ACK_WINDOW_SIZE 16 + +extern unsigned rxrpc_call_rcv_timeout; /* receive activity timeout (secs) */ +extern unsigned rxrpc_call_acks_timeout; /* pending ACK (retransmit) timeout (secs) */ +extern unsigned rxrpc_call_dfr_ack_timeout; /* deferred ACK timeout (secs) */ +extern unsigned short rxrpc_call_max_resend; /* maximum consecutive resend count */ + +/* application call state + * - only state 0 and ffff are reserved, the state is set to 1 after an opid is received + */ +enum rxrpc_app_cstate { + RXRPC_CSTATE_COMPLETE = 0, /* operation complete */ + RXRPC_CSTATE_ERROR, /* operation ICMP error or aborted */ + RXRPC_CSTATE_SRVR_RCV_OPID, /* [SERVER] receiving operation ID */ + RXRPC_CSTATE_SRVR_RCV_ARGS, /* [SERVER] receiving operation data */ + RXRPC_CSTATE_SRVR_GOT_ARGS, /* [SERVER] completely received operation data */ + RXRPC_CSTATE_SRVR_SND_REPLY, /* [SERVER] sending operation reply */ + RXRPC_CSTATE_SRVR_RCV_FINAL_ACK, /* [SERVER] receiving final ACK */ + RXRPC_CSTATE_CLNT_SND_ARGS, /* [CLIENT] sending operation args */ + RXRPC_CSTATE_CLNT_RCV_REPLY, /* [CLIENT] receiving operation reply */ + RXRPC_CSTATE_CLNT_GOT_REPLY, /* [CLIENT] completely received operation reply */ +} __attribute__((packed)); + +extern const char *rxrpc_call_states[]; + +enum rxrpc_app_estate { + RXRPC_ESTATE_NO_ERROR = 0, /* no error */ + RXRPC_ESTATE_LOCAL_ABORT, /* aborted locally by application layer */ + RXRPC_ESTATE_PEER_ABORT, /* aborted remotely by peer */ + RXRPC_ESTATE_LOCAL_ERROR, /* local ICMP network error */ + RXRPC_ESTATE_REMOTE_ERROR, /* remote ICMP network error */ +} __attribute__((packed)); + +extern const char *rxrpc_call_error_states[]; + +/*****************************************************************************/ +/* + * Rx call record and application scratch buffer + * - the call record occupies the bottom of a complete page + * - the application scratch buffer occupies the rest + */ +struct rxrpc_call +{ + atomic_t usage; + struct rxrpc_connection *conn; /* connection upon which active */ + spinlock_t lock; /* access lock */ + struct module *owner; /* owner module */ + wait_queue_head_t waitq; /* wait queue for events to happen */ + struct list_head link; /* general internal list link */ + struct list_head call_link; /* master call list link */ + u32 chan_ix; /* connection channel index (net order) */ + u32 call_id; /* call ID on connection (net order) */ + unsigned long cjif; /* jiffies at call creation */ + unsigned long flags; /* control flags */ +#define RXRPC_CALL_ACKS_TIMO 0x00000001 /* ACKS timeout reached */ +#define RXRPC_CALL_ACKR_TIMO 0x00000002 /* ACKR timeout reached */ +#define RXRPC_CALL_RCV_TIMO 0x00000004 /* RCV timeout reached */ +#define RXRPC_CALL_RCV_PKT 0x00000008 /* received packet */ + + /* transmission */ + rxrpc_seq_t snd_seq_count; /* outgoing packet sequence number counter */ + struct rxrpc_message *snd_nextmsg; /* next message being constructed for sending */ + struct rxrpc_message *snd_ping; /* last ping message sent */ + unsigned short snd_resend_cnt; /* count of resends since last ACK */ + + /* transmission ACK tracking */ + struct list_head acks_pendq; /* messages pending ACK (ordered by seq) */ + unsigned acks_pend_cnt; /* number of un-ACK'd packets */ + rxrpc_seq_t acks_dftv_seq; /* highest definitively ACK'd msg seq */ + struct timer_list acks_timeout; /* timeout on expected ACK */ + + /* reception */ + struct list_head rcv_receiveq; /* messages pending reception (ordered by seq) */ + struct list_head rcv_krxiodq_lk; /* krxiod queue for new inbound packets */ + struct timer_list rcv_timeout; /* call receive activity timeout */ + + /* reception ACK'ing */ + rxrpc_seq_t ackr_win_bot; /* bottom of ACK window */ + rxrpc_seq_t ackr_win_top; /* top of ACK window */ + rxrpc_seq_t ackr_high_seq; /* highest seqno yet received */ + rxrpc_seq_t ackr_prev_seq; /* previous seqno received */ + unsigned ackr_pend_cnt; /* number of pending ACKs */ + struct timer_list ackr_dfr_timo; /* timeout on deferred ACK */ + char ackr_dfr_perm; /* request for deferred ACKs permitted */ + rxrpc_seq_t ackr_dfr_seq; /* seqno for deferred ACK */ + struct rxrpc_ackpacket ackr; /* pending normal ACK packet */ + u8 ackr_array[RXRPC_CALL_ACK_WINDOW_SIZE]; /* ACK records */ + + /* presentation layer */ + char app_last_rcv; /* T if received last packet from remote end */ + enum rxrpc_app_cstate app_call_state; /* call state */ + enum rxrpc_app_estate app_err_state; /* abort/error state */ + struct list_head app_readyq; /* ordered ready received packet queue */ + struct list_head app_unreadyq; /* ordered post-hole recv'd packet queue */ + rxrpc_seq_t app_ready_seq; /* last seq number dropped into readyq */ + size_t app_ready_qty; /* amount of data ready in readyq */ + unsigned app_opcode; /* operation ID */ + unsigned app_abort_code; /* abort code (when aborted) */ + int app_errno; /* error number (when ICMP error received) */ + + /* statisics */ + unsigned pkt_rcv_count; /* count of received packets on this call */ + unsigned pkt_snd_count; /* count of sent packets on this call */ + unsigned app_read_count; /* number of reads issued */ + + /* bits for the application to use */ + rxrpc_call_attn_func_t app_attn_func; /* callback when attention required */ + rxrpc_call_error_func_t app_error_func; /* callback when abort sent (cleanup and put) */ + rxrpc_call_aemap_func_t app_aemap_func; /* callback to map abort code to/from errno */ + void *app_user; /* application data */ + struct list_head app_link; /* application list linkage */ + struct list_head app_attn_link; /* application attention list linkage */ + size_t app_mark; /* trigger callback when app_ready_qty>=app_mark */ + char app_async_read; /* T if in async-read mode */ + u8 *app_read_buf; /* application async read buffer (app_mark size) */ + u8 *app_scr_alloc; /* application scratch allocation pointer */ + void *app_scr_ptr; /* application pointer into scratch buffer */ + +#define RXRPC_APP_MARK_EOF 0xFFFFFFFFU /* mark at end of input */ + + /* application scratch buffer */ + u8 app_scratch[0] __attribute__((aligned(sizeof(long)))); +}; + +#define RXRPC_CALL_SCRATCH_SIZE (PAGE_SIZE - sizeof(struct rxrpc_call)) + +#define rxrpc_call_reset_scratch(CALL) \ +do { (CALL)->app_scr_alloc = (CALL)->app_scratch; } while(0) + +#define rxrpc_call_alloc_scratch(CALL,SIZE) \ +({ \ + void *ptr; \ + ptr = (CALL)->app_scr_alloc; \ + (CALL)->app_scr_alloc += (SIZE); \ + if ((SIZE)>RXRPC_CALL_SCRATCH_SIZE || \ + (size_t)((CALL)->app_scr_alloc - (u8*)(CALL)) > RXRPC_CALL_SCRATCH_SIZE) { \ + printk("rxrpc_call_alloc_scratch(%p,%u)\n",(CALL),(SIZE)); \ + BUG(); \ + } \ + ptr; \ +}) + +#define rxrpc_call_alloc_scratch_s(CALL,TYPE) \ +({ \ + size_t size = sizeof(TYPE); \ + TYPE *ptr; \ + ptr = (TYPE*)(CALL)->app_scr_alloc; \ + (CALL)->app_scr_alloc += size; \ + if (size>RXRPC_CALL_SCRATCH_SIZE || \ + (size_t)((CALL)->app_scr_alloc - (u8*)(CALL)) > RXRPC_CALL_SCRATCH_SIZE) { \ + printk("rxrpc_call_alloc_scratch(%p,%u)\n",(CALL),size); \ + BUG(); \ + } \ + ptr; \ +}) + +#define rxrpc_call_is_ack_pending(CALL) ((CALL)->ackr.reason != 0) + +extern int rxrpc_create_call(struct rxrpc_connection *conn, + rxrpc_call_attn_func_t attn, + rxrpc_call_error_func_t error, + rxrpc_call_aemap_func_t aemap, + struct rxrpc_call **_call); + +extern int rxrpc_incoming_call(struct rxrpc_connection *conn, + struct rxrpc_message *msg, + struct rxrpc_call **_call); + +static inline void rxrpc_get_call(struct rxrpc_call *call) +{ + if (atomic_read(&call->usage)<=0) + BUG(); + atomic_inc(&call->usage); + /*printk("rxrpc_get_call(%p{u=%d})\n",(C),atomic_read(&(C)->usage));*/ +} + +extern void rxrpc_put_call(struct rxrpc_call *call); + +extern void rxrpc_call_do_stuff(struct rxrpc_call *call); + +extern int rxrpc_call_abort(struct rxrpc_call *call, int error); + +#define RXRPC_CALL_READ_BLOCK 0x0001 /* block if not enough data and not yet EOF */ +#define RXRPC_CALL_READ_ALL 0x0002 /* error if insufficient data received */ +extern int rxrpc_call_read_data(struct rxrpc_call *call, void *buffer, size_t size, int flags); + +extern int rxrpc_call_write_data(struct rxrpc_call *call, + size_t sioc, + struct iovec siov[], + u8 rxhdr_flags, + int alloc_flags, + int dup_data, + size_t *size_sent); + +extern int rxrpc_call_flush(struct rxrpc_call *call); + +extern void rxrpc_call_handle_error(struct rxrpc_call *conn, int local, int errno); + +#endif /* _LINUX_RXRPC_CALL_H */ diff -urNp linux-5240/include/rxrpc/connection.h linux-5250/include/rxrpc/connection.h --- linux-5240/include/rxrpc/connection.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/connection.h @@ -0,0 +1,83 @@ +/* connection.h: Rx connection record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_CONNECTION_H +#define _LINUX_RXRPC_CONNECTION_H + +#include +#include + +struct sk_buff; + +/*****************************************************************************/ +/* + * Rx connection + * - connections are matched by (rmt_port,rmt_addr,service_id,conn_id,clientflag) + * - connections only retain a refcount on the peer when they are active + * - connections with refcount==0 are inactive and reside in the peer's graveyard + */ +struct rxrpc_connection +{ + atomic_t usage; + struct rxrpc_transport *trans; /* transport endpoint */ + struct rxrpc_peer *peer; /* peer from/to which connected */ + struct rxrpc_service *service; /* responsible service (inbound conns) */ + struct rxrpc_timer timeout; /* decaching timer */ + struct list_head link; /* link in peer's list */ + struct list_head proc_link; /* link in proc list */ + struct list_head err_link; /* link in ICMP error processing list */ + struct sockaddr_in addr; /* remote address */ + struct rxrpc_call *channels[4]; /* channels (active calls) */ + wait_queue_head_t chanwait; /* wait for channel to become available */ + spinlock_t lock; /* access lock */ + struct timeval atime; /* last access time */ + size_t mtu_size; /* MTU size for outbound messages */ + unsigned call_counter; /* call ID counter */ + rxrpc_serial_t serial_counter; /* packet serial number counter */ + + /* the following should all be in net order */ + u32 in_epoch; /* peer's epoch */ + u32 out_epoch; /* my epoch */ + u32 conn_id; /* connection ID, appropriately shifted */ + u16 service_id; /* service ID */ + u8 security_ix; /* security ID */ + u8 in_clientflag; /* RXRPC_CLIENT_INITIATED if we are server */ + u8 out_clientflag; /* RXRPC_CLIENT_INITIATED if we are client */ +}; + +extern int rxrpc_create_connection(struct rxrpc_transport *trans, + u16 port, + u32 addr, + unsigned short service_id, + void *security, + struct rxrpc_connection **_conn); + +extern int rxrpc_connection_lookup(struct rxrpc_peer *peer, + struct rxrpc_message *msg, + struct rxrpc_connection **_conn); + +static inline void rxrpc_get_connection(struct rxrpc_connection *conn) +{ + if (atomic_read(&conn->usage)<0) + BUG(); + atomic_inc(&conn->usage); + //printk("rxrpc_get_conn(%p{u=%d})\n",conn,atomic_read(&conn->usage)); +} + +extern void rxrpc_put_connection(struct rxrpc_connection *conn); + +extern int rxrpc_conn_receive_call_packet(struct rxrpc_connection *conn, + struct rxrpc_call *call, + struct rxrpc_message *msg); + +extern void rxrpc_conn_handle_error(struct rxrpc_connection *conn, int local, int errno); + +#endif /* _LINUX_RXRPC_CONNECTION_H */ diff -urNp linux-5240/include/rxrpc/krxiod.h linux-5250/include/rxrpc/krxiod.h --- linux-5240/include/rxrpc/krxiod.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/krxiod.h @@ -0,0 +1,27 @@ +/* krxiod.h: Rx RPC I/O kernel thread interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_KRXIOD_H +#define _LINUX_RXRPC_KRXIOD_H + +#include + +extern int rxrpc_krxiod_init(void); +extern void rxrpc_krxiod_kill(void); +extern void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans); +extern void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans); +extern void rxrpc_krxiod_queue_peer(struct rxrpc_peer *peer); +extern void rxrpc_krxiod_dequeue_peer(struct rxrpc_peer *peer); +extern void rxrpc_krxiod_clear_peers(struct rxrpc_transport *trans); +extern void rxrpc_krxiod_queue_call(struct rxrpc_call *call); +extern void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call); + +#endif /* _LINUX_RXRPC_KRXIOD_H */ diff -urNp linux-5240/include/rxrpc/krxsecd.h linux-5250/include/rxrpc/krxsecd.h --- linux-5240/include/rxrpc/krxsecd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/krxsecd.h @@ -0,0 +1,22 @@ +/* krxsecd.h: Rx RPC security kernel thread interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_KRXSECD_H +#define _LINUX_RXRPC_KRXSECD_H + +#include + +extern int rxrpc_krxsecd_init(void); +extern void rxrpc_krxsecd_kill(void); +extern void rxrpc_krxsecd_clear_transport(struct rxrpc_transport *trans); +extern void rxrpc_krxsecd_queue_incoming_call(struct rxrpc_message *msg); + +#endif /* _LINUX_RXRPC_KRXSECD_H */ diff -urNp linux-5240/include/rxrpc/krxtimod.h linux-5250/include/rxrpc/krxtimod.h --- linux-5240/include/rxrpc/krxtimod.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/krxtimod.h @@ -0,0 +1,45 @@ +/* krxtimod.h: RxRPC timeout daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_KRXTIMOD_H +#define _LINUX_RXRPC_KRXTIMOD_H + +#include + +struct rxrpc_timer_ops { + /* called when the front of the timer queue has timed out */ + void (*timed_out)(struct rxrpc_timer *timer); +}; + +/*****************************************************************************/ +/* + * RXRPC timer/timeout record + */ +struct rxrpc_timer +{ + struct list_head link; /* link in timer queue */ + unsigned long timo_jif; /* timeout time */ + const struct rxrpc_timer_ops *ops; /* timeout expiry function */ +}; + +static inline void rxrpc_timer_init(rxrpc_timer_t *timer, const struct rxrpc_timer_ops *ops) +{ + INIT_LIST_HEAD(&timer->link); + timer->ops = ops; +} + +extern int rxrpc_krxtimod_start(void); +extern void rxrpc_krxtimod_kill(void); + +extern void rxrpc_krxtimod_add_timer(rxrpc_timer_t *timer, unsigned long timeout); +extern int rxrpc_krxtimod_del_timer(rxrpc_timer_t *timer); + +#endif /* _LINUX_RXRPC_KRXTIMOD_H */ diff -urNp linux-5240/include/rxrpc/message.h linux-5250/include/rxrpc/message.h --- linux-5240/include/rxrpc/message.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/message.h @@ -0,0 +1,72 @@ +/* message.h: Rx message caching + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _H_3AD3363A_3A9C_11D6_83D8_0002B3163499 +#define _H_3AD3363A_3A9C_11D6_83D8_0002B3163499 + +#include + +/*****************************************************************************/ +/* + * Rx message record + */ +struct rxrpc_message +{ + atomic_t usage; + struct list_head link; /* list link */ + struct timeval stamp; /* time received or last sent */ + rxrpc_seq_t seq; /* message sequence number */ + + int state; /* the state the message is currently in */ +#define RXRPC_MSG_PREPARED 0 +#define RXRPC_MSG_SENT 1 +#define RXRPC_MSG_ACKED 2 /* provisionally ACK'd */ +#define RXRPC_MSG_DONE 3 /* definitively ACK'd (msg->sequsage); } while(0) + +extern void __rxrpc_put_message(struct rxrpc_message *msg); +static inline void rxrpc_put_message(struct rxrpc_message *msg) +{ + if (atomic_read(&msg->usage)<=0) + BUG(); + if (atomic_dec_and_test(&msg->usage)) + __rxrpc_put_message(msg); +} + +extern int rxrpc_conn_newmsg(struct rxrpc_connection *conn, + struct rxrpc_call *call, + u8 type, + int count, + struct iovec diov[], + int alloc_flags, + struct rxrpc_message **_msg); + +extern int rxrpc_conn_sendmsg(struct rxrpc_connection *conn, struct rxrpc_message *msg); + +#endif /* _H_3AD3363A_3A9C_11D6_83D8_0002B3163499 */ diff -urNp linux-5240/include/rxrpc/packet.h linux-5250/include/rxrpc/packet.h --- linux-5240/include/rxrpc/packet.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/packet.h @@ -0,0 +1,128 @@ +/* packet.h: Rx packet layout and definitions + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_PACKET_H +#define _LINUX_RXRPC_PACKET_H + +#include + +#define RXRPC_IPUDP_SIZE 28 +extern size_t RXRPC_MAX_PACKET_SIZE; +#define RXRPC_MAX_PACKET_DATA_SIZE (RXRPC_MAX_PACKET_SIZE - sizeof(struct rxrpc_header)) +#define RXRPC_LOCAL_PACKET_SIZE RXRPC_MAX_PACKET_SIZE +#define RXRPC_REMOTE_PACKET_SIZE (576 - RXRPC_IPUDP_SIZE) + +/*****************************************************************************/ +/* + * on-the-wire Rx packet header + * - all multibyte fields should be in network byte order + */ +struct rxrpc_header +{ + u32 epoch; /* client boot timestamp */ + + u32 cid; /* connection and channel ID */ +#define RXRPC_MAXCALLS 4 /* max active calls per conn */ +#define RXRPC_CHANNELMASK (RXRPC_MAXCALLS-1) /* mask for channel ID */ +#define RXRPC_CIDMASK (~RXRPC_CHANNELMASK) /* mask for connection ID */ +#define RXRPC_CIDSHIFT 2 /* shift for connection ID */ + + u32 callNumber; /* call ID (0 for connection-level packets) */ +#define RXRPC_PROCESS_MAXCALLS (1<<2) /* maximum number of active calls per conn (power of 2) */ + + u32 seq; /* sequence number of pkt in call stream */ + u32 serial; /* serial number of pkt sent to network */ + + u8 type; /* packet type */ +#define RXRPC_PACKET_TYPE_DATA 1 /* data */ +#define RXRPC_PACKET_TYPE_ACK 2 /* ACK */ +#define RXRPC_PACKET_TYPE_BUSY 3 /* call reject */ +#define RXRPC_PACKET_TYPE_ABORT 4 /* call/connection abort */ +#define RXRPC_PACKET_TYPE_ACKALL 5 /* ACK all outstanding packets on call */ +#define RXRPC_PACKET_TYPE_CHALLENGE 6 /* connection security challenge (SRVR->CLNT) */ +#define RXRPC_PACKET_TYPE_RESPONSE 7 /* connection secutity response (CLNT->SRVR) */ +#define RXRPC_PACKET_TYPE_DEBUG 8 /* debug info request */ +#define RXRPC_N_PACKET_TYPES 9 /* number of packet types (incl type 0) */ + + u8 flags; /* packet flags */ +#define RXRPC_CLIENT_INITIATED 0x01 /* signifies a packet generated by a client */ +#define RXRPC_REQUEST_ACK 0x02 /* request an unconditional ACK of this packet */ +#define RXRPC_LAST_PACKET 0x04 /* the last packet from this side for this call */ +#define RXRPC_MORE_PACKETS 0x08 /* more packets to come */ +#define RXRPC_JUMBO_PACKET 0x20 /* [DATA] this is a jumbo packet */ +#define RXRPC_SLOW_START_OK 0x20 /* [ACK] slow start supported */ + + u8 userStatus; /* app-layer defined status */ + u8 securityIndex; /* security protocol ID */ + u16 _rsvd; /* reserved (used by kerberos security as cksum) */ + u16 serviceId; /* service ID */ + +} __attribute__((packed)); + +#define __rxrpc_header_off(X) offsetof(struct rxrpc_header,X) + +extern const char *rxrpc_pkts[]; + +/*****************************************************************************/ +/* + * jumbo packet secondary header + * - can be mapped to read header by: + * - new_serial = serial + 1 + * - new_seq = seq + 1 + * - new_flags = j_flags + * - new__rsvd = j__rsvd + * - duplicating all other fields + */ +struct rxrpc_jumbo_header +{ + u8 flags; /* packet flags (as per rxrpc_header) */ + u8 pad; + u16 _rsvd; /* reserved (used by kerberos security as cksum) */ +}; + +#define RXRPC_JUMBO_DATALEN 1412 /* non-terminal jumbo packet data length */ + +/*****************************************************************************/ +/* + * on-the-wire Rx ACK packet data payload + * - all multibyte fields should be in network byte order + */ +struct rxrpc_ackpacket +{ + u16 bufferSpace; /* number of packet buffers available */ + u16 maxSkew; /* diff between serno being ACK'd and highest serial no received */ + u32 firstPacket; /* sequence no of first ACK'd packet in attached list */ + u32 previousPacket; /* sequence no of previous packet received */ + u32 serial; /* serial no of packet that prompted this ACK */ + + u8 reason; /* reason for ACK */ +#define RXRPC_ACK_REQUESTED 1 /* ACK was requested on packet */ +#define RXRPC_ACK_DUPLICATE 2 /* duplicate packet received */ +#define RXRPC_ACK_OUT_OF_SEQUENCE 3 /* out of sequence packet received */ +#define RXRPC_ACK_EXCEEDS_WINDOW 4 /* packet received beyond end of ACK window */ +#define RXRPC_ACK_NOSPACE 5 /* packet discarded due to lack of buffer space */ +#define RXRPC_ACK_PING 6 /* keep alive ACK */ +#define RXRPC_ACK_PING_RESPONSE 7 /* response to RXRPC_ACK_PING */ +#define RXRPC_ACK_DELAY 8 /* nothing happened since received packet */ +#define RXRPC_ACK_IDLE 9 /* ACK due to fully received ACK window */ + + u8 nAcks; /* number of ACKs */ +#define RXRPC_MAXACKS 255 + + u8 acks[0]; /* list of ACK/NAKs */ +#define RXRPC_ACK_TYPE_NACK 0 +#define RXRPC_ACK_TYPE_ACK 1 + +} __attribute__((packed)); + +extern const char *rxrpc_acks[]; + +#endif /* _LINUX_RXRPC_PACKET_H */ diff -urNp linux-5240/include/rxrpc/peer.h linux-5250/include/rxrpc/peer.h --- linux-5240/include/rxrpc/peer.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/peer.h @@ -0,0 +1,80 @@ +/* peer.h: Rx RPC per-transport peer record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_PEER_H +#define _LINUX_RXRPC_PEER_H + +#include +#include +#include + +struct rxrpc_peer_ops +{ + /* peer record being added */ + int (*adding)(struct rxrpc_peer *peer); + + /* peer record being discarded from graveyard */ + void (*discarding)(struct rxrpc_peer *peer); + + /* change of epoch detected on connection */ + void (*change_of_epoch)(struct rxrpc_connection *conn); +}; + +/*****************************************************************************/ +/* + * Rx RPC per-transport peer record + * - peers only retain a refcount on the transport when they are active + * - peers with refcount==0 are inactive and reside in the transport's graveyard + */ +struct rxrpc_peer +{ + atomic_t usage; + struct rxrpc_peer_ops *ops; /* operations on this peer */ + struct rxrpc_transport *trans; /* owner transport */ + struct rxrpc_timer timeout; /* timeout for grave destruction */ + struct list_head link; /* link in transport's peer list */ + struct list_head proc_link; /* link in /proc list */ + rwlock_t conn_lock; /* lock for connections */ + struct list_head conn_active; /* active connections to/from this peer */ + struct list_head conn_graveyard; /* graveyard for inactive connections */ + spinlock_t conn_gylock; /* lock for conn_graveyard */ + wait_queue_head_t conn_gy_waitq; /* wait queue hit when graveyard is empty */ + atomic_t conn_count; /* number of attached connections */ + struct in_addr addr; /* remote address */ + size_t if_mtu; /* interface MTU for this peer */ + spinlock_t lock; /* access lock */ + + void *user; /* application layer data */ + + /* calculated RTT cache */ +#define RXRPC_RTT_CACHE_SIZE 32 + suseconds_t rtt; /* current RTT estimate (in uS) */ + unsigned short rtt_point; /* next entry at which to insert */ + unsigned short rtt_usage; /* amount of cache actually used */ + suseconds_t rtt_cache[RXRPC_RTT_CACHE_SIZE]; /* calculated RTT cache */ +}; + + +extern int rxrpc_peer_lookup(struct rxrpc_transport *trans, + u32 addr, + struct rxrpc_peer **_peer); + +static inline void rxrpc_get_peer(struct rxrpc_peer *peer) +{ + if (atomic_read(&peer->usage)<0) + BUG(); + atomic_inc(&peer->usage); + //printk("rxrpc_get_peer(%p{u=%d})\n",peer,atomic_read(&peer->usage)); +} + +extern void rxrpc_put_peer(struct rxrpc_peer *peer); + +#endif /* _LINUX_RXRPC_PEER_H */ diff -urNp linux-5240/include/rxrpc/rxrpc.h linux-5250/include/rxrpc/rxrpc.h --- linux-5240/include/rxrpc/rxrpc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/rxrpc.h @@ -0,0 +1,29 @@ +/* rx.h: Rx RPC interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_RXRPC_H +#define _LINUX_RXRPC_RXRPC_H + +#ifdef __KERNEL__ + +extern u32 rxrpc_epoch; + +extern int rxrpc_ktrace; +extern int rxrpc_kdebug; +extern int rxrpc_kproto; +extern int rxrpc_knet; + +extern int rxrpc_sysctl_init(void); +extern void rxrpc_sysctl_cleanup(void); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_RXRPC_RXRPC_H */ diff -urNp linux-5240/include/rxrpc/transport.h linux-5250/include/rxrpc/transport.h --- linux-5240/include/rxrpc/transport.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/transport.h @@ -0,0 +1,115 @@ +/* transport.h: Rx transport management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_TRANSPORT_H +#define _LINUX_RXRPC_TRANSPORT_H + +#include +#include +#include +#include +#include + +typedef int (*rxrpc_newcall_fnx_t)(struct rxrpc_call *call); + +extern wait_queue_head_t rxrpc_krxiod_wq; + +/*****************************************************************************/ +/* + * Rx operation specification + * - tables of these must be sorted by op ID so that they can be binary-chop searched + */ +struct rxrpc_operation +{ + unsigned id; /* operation ID */ + size_t asize; /* minimum size of argument block */ + const char *name; /* name of operation */ + void *user; /* initial user data */ +}; + +/*****************************************************************************/ +/* + * Rx transport service record + */ +struct rxrpc_service +{ + struct list_head link; /* link in services list on transport */ + struct module *owner; /* owner module */ + rxrpc_newcall_fnx_t new_call; /* new call handler function */ + const char *name; /* name of service */ + unsigned short service_id; /* Rx service ID */ + rxrpc_call_attn_func_t attn_func; /* call requires attention callback */ + rxrpc_call_error_func_t error_func; /* call error callback */ + rxrpc_call_aemap_func_t aemap_func; /* abort -> errno mapping callback */ + + const struct rxrpc_operation *ops_begin; /* beginning of operations table */ + const struct rxrpc_operation *ops_end; /* end of operations table */ +}; + +/*****************************************************************************/ +/* + * Rx transport endpoint record + */ +struct rxrpc_transport +{ + atomic_t usage; + struct socket *socket; /* my UDP socket */ + struct list_head services; /* services listening on this socket */ + struct list_head link; /* link in transport list */ + struct list_head proc_link; /* link in transport proc list */ + struct list_head krxiodq_link; /* krxiod attention queue link */ + spinlock_t lock; /* access lock */ + struct list_head peer_active; /* active peers connected to over this socket */ + struct list_head peer_graveyard; /* inactive peer list */ + spinlock_t peer_gylock; /* peer graveyard lock */ + wait_queue_head_t peer_gy_waitq; /* wait queue hit when peer graveyard is empty */ + rwlock_t peer_lock; /* peer list access lock */ + atomic_t peer_count; /* number of peers */ + struct rxrpc_peer_ops *peer_ops; /* default peer operations */ + unsigned short port; /* port upon which listening */ + volatile char error_rcvd; /* T if received ICMP error outstanding */ +}; + +extern struct list_head rxrpc_transports; + +extern int rxrpc_create_transport(unsigned short port, + struct rxrpc_transport **_trans); + +static inline void rxrpc_get_transport(struct rxrpc_transport *trans) +{ + if (atomic_read(&trans->usage)<=0) + BUG(); + atomic_inc(&trans->usage); + //printk("rxrpc_get_transport(%p{u=%d})\n",trans,atomic_read(&trans->usage)); +} + +extern void rxrpc_put_transport(struct rxrpc_transport *trans); + +extern int rxrpc_add_service(struct rxrpc_transport *trans, + struct rxrpc_service *srv); + +extern void rxrpc_del_service(struct rxrpc_transport *trans, + struct rxrpc_service *srv); + +#if 0 +extern int rxrpc_trans_add_connection(struct rxrpc_transport *trans, + struct rxrpc_connection *conn); +#endif + +extern void rxrpc_trans_receive_packet(struct rxrpc_transport *trans); + +extern int rxrpc_trans_immediate_abort(struct rxrpc_transport *trans, + struct rxrpc_message *msg, + int error); + +extern void rxrpc_clear_transport(struct rxrpc_transport *trans); + +#endif /* _LINUX_RXRPC_TRANSPORT_H */ diff -urNp linux-5240/include/rxrpc/types.h linux-5250/include/rxrpc/types.h --- linux-5240/include/rxrpc/types.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/include/rxrpc/types.h @@ -0,0 +1,39 @@ +/* types.h: Rx types + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_TYPES_H +#define _LINUX_RXRPC_TYPES_H + +#include +#include +#include +#include +#include +#include + +typedef unsigned rxrpc_seq_t; /* Rx message sequence number */ +typedef unsigned rxrpc_serial_t; /* Rx message serial number */ + +struct rxrpc_call; +struct rxrpc_connection; +struct rxrpc_header; +struct rxrpc_message; +struct rxrpc_operation; +struct rxrpc_peer; +struct rxrpc_service; +typedef struct rxrpc_timer rxrpc_timer_t; +struct rxrpc_transport; + +typedef void (*rxrpc_call_attn_func_t)(struct rxrpc_call *call); +typedef void (*rxrpc_call_error_func_t)(struct rxrpc_call *call); +typedef void (*rxrpc_call_aemap_func_t)(struct rxrpc_call *call); + +#endif /* _LINUX_RXRPC_TYPES_H */ diff -urNp linux-5240/net/Makefile linux-5250/net/Makefile --- linux-5240/net/Makefile +++ linux-5250/net/Makefile @@ -7,7 +7,7 @@ O_TARGET := network.o mod-subdirs := ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \ - bluetooth atm netlink sched core + bluetooth atm netlink sched core rxrpc export-objs := netsyms.o subdir-y := core ethernet @@ -46,6 +46,7 @@ subdir-$(CONFIG_ATM) += atm subdir-$(CONFIG_DECNET) += decnet subdir-$(CONFIG_ECONET) += econet subdir-$(CONFIG_VLAN_8021Q) += 8021q +subdir-$(CONFIG_AFS_FS) += rxrpc obj-y := socket.o $(join $(subdir-y), $(patsubst %,/%.o,$(notdir $(subdir-y)))) diff -urNp linux-5240/net/rxrpc/call.c linux-5250/net/rxrpc/call.c --- linux-5240/net/rxrpc/call.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/call.c @@ -0,0 +1,2122 @@ +/* call.c: Rx call routines + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +__RXACCT_DECL(atomic_t rxrpc_call_count); +__RXACCT_DECL(atomic_t rxrpc_message_count); + +LIST_HEAD(rxrpc_calls); +DECLARE_RWSEM(rxrpc_calls_sem); + +unsigned rxrpc_call_rcv_timeout = 30; +unsigned rxrpc_call_acks_timeout = 30; +unsigned rxrpc_call_dfr_ack_timeout = 5; +unsigned short rxrpc_call_max_resend = 10; + +const char *rxrpc_call_states[] = { + "COMPLETE", + "ERROR", + "SRVR_RCV_OPID", + "SRVR_RCV_ARGS", + "SRVR_GOT_ARGS", + "SRVR_SND_REPLY", + "SRVR_RCV_FINAL_ACK", + "CLNT_SND_ARGS", + "CLNT_RCV_REPLY", + "CLNT_GOT_REPLY" +}; + +const char *rxrpc_call_error_states[] = { + "NO_ERROR", + "LOCAL_ABORT", + "PEER_ABORT", + "LOCAL_ERROR", + "REMOTE_ERROR" +}; + +const char *rxrpc_pkts[] = { + "?00", "data", "ack", "busy", "abort", "ackall", "chall", "resp", "debug", + "?09", "?10", "?11", "?12", "?13", "?14", "?15" +}; + +const char *rxrpc_acks[] = { + "---", "REQ", "DUP", "SEQ", "WIN", "MEM", "PNG", "PNR", "DLY", "IDL", "-?-" +}; + +static const char _acktype[] = "NA-"; + +static void rxrpc_call_receive_packet(struct rxrpc_call *call); +static void rxrpc_call_receive_data_packet(struct rxrpc_call *call, struct rxrpc_message *msg); +static void rxrpc_call_receive_ack_packet(struct rxrpc_call *call, struct rxrpc_message *msg); +static void rxrpc_call_definitively_ACK(struct rxrpc_call *call, rxrpc_seq_t higest); +static void rxrpc_call_resend(struct rxrpc_call *call, rxrpc_seq_t highest); +static int __rxrpc_call_read_data(struct rxrpc_call *call); + +static int rxrpc_call_record_ACK(struct rxrpc_call *call, + struct rxrpc_message *msg, + rxrpc_seq_t seq, + size_t count); +#define _state(call) \ + _debug("[[[ state %s ]]]",rxrpc_call_states[call->app_call_state]); + +static void rxrpc_call_default_attn_func(struct rxrpc_call *call) +{ + wake_up(&call->waitq); +} + +static void rxrpc_call_default_error_func(struct rxrpc_call *call) +{ + wake_up(&call->waitq); +} + +static void rxrpc_call_default_aemap_func(struct rxrpc_call *call) +{ + switch (call->app_err_state) { + case RXRPC_ESTATE_LOCAL_ABORT: + call->app_abort_code = -call->app_errno; + case RXRPC_ESTATE_PEER_ABORT: + call->app_errno = -ECONNABORTED; + default: + break; + } +} + +static void __rxrpc_call_acks_timeout(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _debug("ACKS TIMEOUT %05lu",jiffies - call->cjif); + + call->flags |= RXRPC_CALL_ACKS_TIMO; + rxrpc_krxiod_queue_call(call); +} + +static void __rxrpc_call_rcv_timeout(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _debug("RCV TIMEOUT %05lu",jiffies - call->cjif); + + call->flags |= RXRPC_CALL_RCV_TIMO; + rxrpc_krxiod_queue_call(call); +} + +static void __rxrpc_call_ackr_timeout(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _debug("ACKR TIMEOUT %05lu",jiffies - call->cjif); + + call->flags |= RXRPC_CALL_ACKR_TIMO; + rxrpc_krxiod_queue_call(call); +} + +/*****************************************************************************/ +/* + * create a new call record + */ +static inline int __rxrpc_create_call(struct rxrpc_connection *conn, + struct rxrpc_call **_call) +{ + struct rxrpc_call *call; + + _enter("%p",conn); + + /* allocate and initialise a call record */ + call = (struct rxrpc_call *) get_zeroed_page(GFP_KERNEL); + if (!call) { + _leave(" ENOMEM"); + return -ENOMEM; + } + + atomic_set(&call->usage,1); + + init_waitqueue_head(&call->waitq); + spin_lock_init(&call->lock); + INIT_LIST_HEAD(&call->link); + INIT_LIST_HEAD(&call->acks_pendq); + INIT_LIST_HEAD(&call->rcv_receiveq); + INIT_LIST_HEAD(&call->rcv_krxiodq_lk); + INIT_LIST_HEAD(&call->app_readyq); + INIT_LIST_HEAD(&call->app_unreadyq); + INIT_LIST_HEAD(&call->app_link); + INIT_LIST_HEAD(&call->app_attn_link); + + init_timer(&call->acks_timeout); + call->acks_timeout.data = (unsigned long) call; + call->acks_timeout.function = __rxrpc_call_acks_timeout; + + init_timer(&call->rcv_timeout); + call->rcv_timeout.data = (unsigned long) call; + call->rcv_timeout.function = __rxrpc_call_rcv_timeout; + + init_timer(&call->ackr_dfr_timo); + call->ackr_dfr_timo.data = (unsigned long) call; + call->ackr_dfr_timo.function = __rxrpc_call_ackr_timeout; + + call->conn = conn; + call->ackr_win_bot = 1; + call->ackr_win_top = call->ackr_win_bot + RXRPC_CALL_ACK_WINDOW_SIZE - 1; + call->ackr_prev_seq = 0; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_attn_func = rxrpc_call_default_attn_func; + call->app_error_func = rxrpc_call_default_error_func; + call->app_aemap_func = rxrpc_call_default_aemap_func; + call->app_scr_alloc = call->app_scratch; + + call->cjif = jiffies; + + _leave(" = 0 (%p)",call); + + *_call = call; + + return 0; +} /* end __rxrpc_create_call() */ + +/*****************************************************************************/ +/* + * create a new call record for outgoing calls + */ +int rxrpc_create_call(struct rxrpc_connection *conn, + rxrpc_call_attn_func_t attn, + rxrpc_call_error_func_t error, + rxrpc_call_aemap_func_t aemap, + struct rxrpc_call **_call) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_call *call; + int ret, cix, loop; + + _enter("%p",conn); + + /* allocate and initialise a call record */ + ret = __rxrpc_create_call(conn,&call); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + call->app_call_state = RXRPC_CSTATE_CLNT_SND_ARGS; + if (attn) call->app_attn_func = attn; + if (error) call->app_error_func = error; + if (aemap) call->app_aemap_func = aemap; + + _state(call); + + spin_lock(&conn->lock); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&conn->chanwait,&myself); + + try_again: + /* try to find an unused channel */ + for (cix=0; cix<4; cix++) + if (!conn->channels[cix]) + goto obtained_chan; + + /* no free channels - wait for one to become available */ + ret = -EINTR; + if (signal_pending(current)) + goto error_unwait; + + spin_unlock(&conn->lock); + + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock(&conn->lock); + goto try_again; + + /* got a channel - now attach to the connection */ + obtained_chan: + remove_wait_queue(&conn->chanwait,&myself); + set_current_state(TASK_RUNNING); + + /* concoct a unique call number */ + next_callid: + call->call_id = htonl(++conn->call_counter); + for (loop=0; loop<4; loop++) + if (conn->channels[loop] && conn->channels[loop]->call_id==call->call_id) + goto next_callid; + + rxrpc_get_connection(conn); + conn->channels[cix] = call; /* assign _after_ done callid check loop */ + conn->atime = xtime; + call->chan_ix = htonl(cix); + + spin_unlock(&conn->lock); + + down_write(&rxrpc_calls_sem); + list_add_tail(&call->call_link,&rxrpc_calls); + up_write(&rxrpc_calls_sem); + + __RXACCT(atomic_inc(&rxrpc_call_count)); + *_call = call; + + _leave(" = 0 (call=%p cix=%u)",call,cix); + return 0; + + error_unwait: + remove_wait_queue(&conn->chanwait,&myself); + set_current_state(TASK_RUNNING); + spin_unlock(&conn->lock); + + free_page((unsigned long)call); + _leave(" = %d",ret); + return ret; + +} /* end rxrpc_create_call() */ + +/*****************************************************************************/ +/* + * create a new call record for incoming calls + */ +int rxrpc_incoming_call(struct rxrpc_connection *conn, + struct rxrpc_message *msg, + struct rxrpc_call **_call) +{ + struct rxrpc_call *call; + unsigned cix; + int ret; + + cix = ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK; + + _enter("%p,%u,%u",conn,ntohl(msg->hdr.callNumber),cix); + + /* allocate and initialise a call record */ + ret = __rxrpc_create_call(conn,&call); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + call->pkt_rcv_count = 1; + call->app_call_state = RXRPC_CSTATE_SRVR_RCV_OPID; + call->app_mark = sizeof(u32); + + _state(call); + + /* attach to the connection */ + ret = -EBUSY; + call->chan_ix = htonl(cix); + call->call_id = msg->hdr.callNumber; + + spin_lock(&conn->lock); + + if (!conn->channels[cix]) { + conn->channels[cix] = call; + rxrpc_get_connection(conn); + ret = 0; + } + + spin_unlock(&conn->lock); + + if (ret<0) free_page((unsigned long)call); + + _leave(" = %p",call); + + if (ret==0) { + down_write(&rxrpc_calls_sem); + list_add_tail(&call->call_link,&rxrpc_calls); + up_write(&rxrpc_calls_sem); + __RXACCT(atomic_inc(&rxrpc_call_count)); + *_call = call; + } + + return ret; +} /* end rxrpc_incoming_call() */ + +/*****************************************************************************/ +/* + * free a call record + */ +void rxrpc_put_call(struct rxrpc_call *call) +{ + struct rxrpc_connection *conn = call->conn; + struct rxrpc_message *msg; + + _enter("%p{u=%d}",call,atomic_read(&call->usage)); + + /* sanity check */ + if (atomic_read(&call->usage)<=0) + BUG(); + + /* to prevent a race, the decrement and the de-list must be effectively atomic */ + spin_lock(&conn->lock); + if (likely(!atomic_dec_and_test(&call->usage))) { + spin_unlock(&conn->lock); + _leave(""); + return; + } + + conn->channels[ntohl(call->chan_ix)] = NULL; + + spin_unlock(&conn->lock); + + wake_up(&conn->chanwait); + + rxrpc_put_connection(conn); + + /* clear the timers and dequeue from krxiod */ + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + + rxrpc_krxiod_dequeue_call(call); + + /* clean up the contents of the struct */ + if (call->snd_nextmsg) + rxrpc_put_message(call->snd_nextmsg); + + if (call->snd_ping) + rxrpc_put_message(call->snd_ping); + + while (!list_empty(&call->acks_pendq)) { + msg = list_entry(call->acks_pendq.next,struct rxrpc_message,link); + list_del(&msg->link); + rxrpc_put_message(msg); + } + + while (!list_empty(&call->rcv_receiveq)) { + msg = list_entry(call->rcv_receiveq.next,struct rxrpc_message,link); + list_del(&msg->link); + rxrpc_put_message(msg); + } + + while (!list_empty(&call->app_readyq)) { + msg = list_entry(call->app_readyq.next,struct rxrpc_message,link); + list_del(&msg->link); + rxrpc_put_message(msg); + } + + while (!list_empty(&call->app_unreadyq)) { + msg = list_entry(call->app_unreadyq.next,struct rxrpc_message,link); + list_del(&msg->link); + rxrpc_put_message(msg); + } + + if (call->owner) __MOD_DEC_USE_COUNT(call->owner); + + down_write(&rxrpc_calls_sem); + list_del(&call->call_link); + up_write(&rxrpc_calls_sem); + + __RXACCT(atomic_dec(&rxrpc_call_count)); + free_page((unsigned long)call); + + _leave(" [destroyed]"); +} /* end rxrpc_put_call() */ + +/*****************************************************************************/ +/* + * actually generate a normal ACK + */ +static inline int __rxrpc_call_gen_normal_ACK(struct rxrpc_call *call, rxrpc_seq_t seq) +{ + struct rxrpc_message *msg; + struct iovec diov[3]; + unsigned aux[4]; + int delta, ret; + + /* ACKs default to DELAY */ + if (!call->ackr.reason) + call->ackr.reason = RXRPC_ACK_DELAY; + + _proto("Rx %05lu Sending ACK { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", + jiffies - call->cjif, + ntohs(call->ackr.maxSkew), + ntohl(call->ackr.firstPacket), + ntohl(call->ackr.previousPacket), + ntohl(call->ackr.serial), + rxrpc_acks[call->ackr.reason], + call->ackr.nAcks); + + aux[0] = htonl(call->conn->peer->if_mtu); /* interface MTU */ + aux[1] = htonl(1444); /* max MTU */ + aux[2] = htonl(16); /* rwind */ + aux[3] = htonl(4); /* max packets */ + + diov[0].iov_len = sizeof(struct rxrpc_ackpacket); + diov[0].iov_base = &call->ackr; + diov[1].iov_len = (call->ackr_pend_cnt+3); + diov[1].iov_base = call->ackr_array; + diov[2].iov_len = sizeof(aux); + diov[2].iov_base = &aux; + + /* build and send the message */ + ret = rxrpc_conn_newmsg(call->conn,call,RXRPC_PACKET_TYPE_ACK,3,diov,GFP_KERNEL,&msg); + if (ret<0) + goto out; + + msg->seq = seq; + msg->hdr.seq = htonl(seq); + msg->hdr.flags |= RXRPC_SLOW_START_OK; + + ret = rxrpc_conn_sendmsg(call->conn,msg); + rxrpc_put_message(msg); + if (ret<0) + goto out; + call->pkt_snd_count++; + + /* count how many actual ACKs there were at the front */ + for (delta=0; deltaackr_pend_cnt; delta++) + if (call->ackr_array[delta]!=RXRPC_ACK_TYPE_ACK) + break; + + call->ackr_pend_cnt -= delta; /* all ACK'd to this point */ + + /* crank the ACK window around */ + if (delta==0) { + /* un-ACK'd window */ + } + else if (delta < RXRPC_CALL_ACK_WINDOW_SIZE) { + /* partially ACK'd window + * - shuffle down to avoid losing out-of-sequence packets + */ + call->ackr_win_bot += delta; + call->ackr_win_top += delta; + + memmove(&call->ackr_array[0], + &call->ackr_array[delta], + call->ackr_pend_cnt); + + memset(&call->ackr_array[call->ackr_pend_cnt], + RXRPC_ACK_TYPE_NACK, + sizeof(call->ackr_array) - call->ackr_pend_cnt); + } + else { + /* fully ACK'd window + * - just clear the whole thing + */ + memset(&call->ackr_array,RXRPC_ACK_TYPE_NACK,sizeof(call->ackr_array)); + } + + /* clear this ACK */ + memset(&call->ackr,0,sizeof(call->ackr)); + + out: + if (!call->app_call_state) printk("___ STATE 0 ___\n"); + return ret; +} /* end __rxrpc_call_gen_normal_ACK() */ + +/*****************************************************************************/ +/* + * note the reception of a packet in the call's ACK records and generate an appropriate ACK packet + * if necessary + * - returns 0 if packet should be processed, 1 if packet should be ignored and -ve on an error + */ +static int rxrpc_call_generate_ACK(struct rxrpc_call *call, + struct rxrpc_header *hdr, + struct rxrpc_ackpacket *ack) +{ + struct rxrpc_message *msg; + rxrpc_seq_t seq; + unsigned offset; + int ret = 0, err; + u8 special_ACK, do_ACK, force; + + _enter("%p,%p { seq=%d tp=%d fl=%02x }",call,hdr,ntohl(hdr->seq),hdr->type,hdr->flags); + + seq = ntohl(hdr->seq); + offset = seq - call->ackr_win_bot; + do_ACK = RXRPC_ACK_DELAY; + special_ACK = 0; + force = (seq==1); + + if (call->ackr_high_seq < seq) + call->ackr_high_seq = seq; + + /* deal with generation of obvious special ACKs first */ + if (ack && ack->reason==RXRPC_ACK_PING) { + special_ACK = RXRPC_ACK_PING_RESPONSE; + ret = 1; + goto gen_ACK; + } + + if (seq < call->ackr_win_bot) { + special_ACK = RXRPC_ACK_DUPLICATE; + ret = 1; + goto gen_ACK; + } + + if (seq >= call->ackr_win_top) { + special_ACK = RXRPC_ACK_EXCEEDS_WINDOW; + ret = 1; + goto gen_ACK; + } + + if (call->ackr_array[offset] != RXRPC_ACK_TYPE_NACK) { + special_ACK = RXRPC_ACK_DUPLICATE; + ret = 1; + goto gen_ACK; + } + + /* okay... it's a normal data packet inside the ACK window */ + call->ackr_array[offset] = RXRPC_ACK_TYPE_ACK; + + if (offsetackr_pend_cnt) { + } + else if (offset>call->ackr_pend_cnt) { + do_ACK = RXRPC_ACK_OUT_OF_SEQUENCE; + call->ackr_pend_cnt = offset; + goto gen_ACK; + } + + if (hdr->flags & RXRPC_REQUEST_ACK) { + do_ACK = RXRPC_ACK_REQUESTED; + } + + /* generate an ACK on the final packet of a reply just received */ + if (hdr->flags & RXRPC_LAST_PACKET) { + if (call->conn->out_clientflag) + force = 1; + } + else if (!(hdr->flags & RXRPC_MORE_PACKETS)) { + do_ACK = RXRPC_ACK_REQUESTED; + } + + /* re-ACK packets previously received out-of-order */ + for (offset++; offsetackr_array[offset]!=RXRPC_ACK_TYPE_ACK) + break; + + call->ackr_pend_cnt = offset; + + /* generate an ACK if we fill up the window */ + if (call->ackr_pend_cnt >= RXRPC_CALL_ACK_WINDOW_SIZE) + force = 1; + + gen_ACK: + _debug("%05lu ACKs pend=%u norm=%s special=%s%s", + jiffies - call->cjif, + call->ackr_pend_cnt,rxrpc_acks[do_ACK],rxrpc_acks[special_ACK], + force ? " immediate" : + do_ACK==RXRPC_ACK_REQUESTED ? " merge-req" : + hdr->flags & RXRPC_LAST_PACKET ? " finalise" : + " defer" + ); + + /* send any pending normal ACKs if need be */ + if (call->ackr_pend_cnt>0) { + /* fill out the appropriate form */ + call->ackr.bufferSpace = htons(RXRPC_CALL_ACK_WINDOW_SIZE); + call->ackr.maxSkew = htons(min(call->ackr_high_seq - seq,65535U)); + call->ackr.firstPacket = htonl(call->ackr_win_bot); + call->ackr.previousPacket = call->ackr_prev_seq; + call->ackr.serial = hdr->serial; + call->ackr.nAcks = call->ackr_pend_cnt; + + if (do_ACK==RXRPC_ACK_REQUESTED) + call->ackr.reason = do_ACK; + + /* generate the ACK immediately if necessary */ + if (special_ACK || force) { + err = __rxrpc_call_gen_normal_ACK(call,do_ACK==RXRPC_ACK_DELAY ? 0 : seq); + if (err<0) { + ret = err; + goto out; + } + } + } + + if (call->ackr.reason==RXRPC_ACK_REQUESTED) + call->ackr_dfr_seq = seq; + + /* start the ACK timer if not running if there are any pending deferred ACKs */ + if (call->ackr_pend_cnt>0 && + call->ackr.reason!=RXRPC_ACK_REQUESTED && + !timer_pending(&call->ackr_dfr_timo) + ) { + unsigned long timo; + + timo = rxrpc_call_dfr_ack_timeout + jiffies; + + _debug("START ACKR TIMER for cj=%lu",timo-call->cjif); + + spin_lock(&call->lock); + mod_timer(&call->ackr_dfr_timo,timo); + spin_unlock(&call->lock); + } + else if ((call->ackr_pend_cnt==0 || call->ackr.reason==RXRPC_ACK_REQUESTED) && + timer_pending(&call->ackr_dfr_timo) + ) { + /* stop timer if no pending ACKs */ + _debug("CLEAR ACKR TIMER"); + del_timer_sync(&call->ackr_dfr_timo); + } + + /* send a special ACK if one is required */ + if (special_ACK) { + struct rxrpc_ackpacket ack; + struct iovec diov[2]; + u8 acks[1] = { RXRPC_ACK_TYPE_ACK }; + + /* fill out the appropriate form */ + ack.bufferSpace = htons(RXRPC_CALL_ACK_WINDOW_SIZE); + ack.maxSkew = htons(min(call->ackr_high_seq - seq,65535U)); + ack.firstPacket = htonl(call->ackr_win_bot); + ack.previousPacket = call->ackr_prev_seq; + ack.serial = hdr->serial; + ack.reason = special_ACK; + ack.nAcks = 0; + //ack.nAcks = special_ACK==RXRPC_ACK_OUT_OF_SEQUENCE ? 0 : hdr->seq ? 1 : 0; + + _proto("Rx Sending s-ACK { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", + ntohs(ack.maxSkew),ntohl(ack.firstPacket),ntohl(ack.previousPacket), + ntohl(ack.serial),rxrpc_acks[ack.reason],ack.nAcks); + + diov[0].iov_len = sizeof(struct rxrpc_ackpacket); + diov[0].iov_base = &ack; + diov[1].iov_len = sizeof(acks); + diov[1].iov_base = acks; + + /* build and send the message */ + err = rxrpc_conn_newmsg(call->conn,call,RXRPC_PACKET_TYPE_ACK, + hdr->seq ? 2 : 1,diov, + GFP_KERNEL, + &msg); + if (err<0) { + ret = err; + goto out; + } + + msg->seq = seq; + msg->hdr.seq = htonl(seq); + msg->hdr.flags |= RXRPC_SLOW_START_OK; + + err = rxrpc_conn_sendmsg(call->conn,msg); + rxrpc_put_message(msg); + if (err<0) { + ret = err; + goto out; + } + call->pkt_snd_count++; + } + + out: + if (hdr->seq) + call->ackr_prev_seq = hdr->seq; + + _leave(" = %d",ret); + return ret; +} /* end rxrpc_call_generate_ACK() */ + +/*****************************************************************************/ +/* + * handle work to be done on a call + * - includes packet reception and timeout processing + */ +void rxrpc_call_do_stuff(struct rxrpc_call *call) +{ + _enter("%p{flags=%lx}",call,call->flags); + + /* handle packet reception */ + if (call->flags & RXRPC_CALL_RCV_PKT) { + _debug("- receive packet"); + call->flags &= ~RXRPC_CALL_RCV_PKT; + rxrpc_call_receive_packet(call); + } + + /* handle overdue ACKs */ + if (call->flags & RXRPC_CALL_ACKS_TIMO) { + _debug("- overdue ACK timeout"); + call->flags &= ~RXRPC_CALL_ACKS_TIMO; + rxrpc_call_resend(call,call->snd_seq_count); + } + + /* handle lack of reception */ + if (call->flags & RXRPC_CALL_RCV_TIMO) { + _debug("- reception timeout"); + call->flags &= ~RXRPC_CALL_RCV_TIMO; + rxrpc_call_abort(call,-EIO); + } + + /* handle deferred ACKs */ + if (call->flags & RXRPC_CALL_ACKR_TIMO || + (call->ackr.nAcks>0 && call->ackr.reason==RXRPC_ACK_REQUESTED) + ) { + _debug("- deferred ACK timeout: cj=%05lu r=%s n=%u", + jiffies - call->cjif, + rxrpc_acks[call->ackr.reason], + call->ackr.nAcks); + + call->flags &= ~RXRPC_CALL_ACKR_TIMO; + + if (call->ackr.nAcks>0 && call->app_call_state!=RXRPC_CSTATE_ERROR) { + /* generate ACK */ + __rxrpc_call_gen_normal_ACK(call,call->ackr_dfr_seq); + call->ackr_dfr_seq = 0; + } + } + + _leave(""); + +} /* end rxrpc_call_do_timeout() */ + +/*****************************************************************************/ +/* + * send an abort message at call or connection level + * - must be called with call->lock held + * - the supplied error code is sent as the packet data + */ +static int __rxrpc_call_abort(struct rxrpc_call *call, int errno) +{ + struct rxrpc_connection *conn = call->conn; + struct rxrpc_message *msg; + struct iovec diov[1]; + int ret; + u32 _error; + + _enter("%p{%08x},%p{%d},%d",conn,ntohl(conn->conn_id),call,ntohl(call->call_id),errno); + + /* if this call is already aborted, then just wake up any waiters */ + if (call->app_call_state==RXRPC_CSTATE_ERROR) { + spin_unlock(&call->lock); + call->app_error_func(call); + _leave(" = 0"); + return 0; + } + + rxrpc_get_call(call); + + /* change the state _with_ the lock still held */ + call->app_call_state = RXRPC_CSTATE_ERROR; + call->app_err_state = RXRPC_ESTATE_LOCAL_ABORT; + call->app_errno = errno; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + call->app_async_read = 0; + + _state(call); + + /* ask the app to translate the error code */ + call->app_aemap_func(call); + + spin_unlock(&call->lock); + + /* flush any outstanding ACKs */ + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + + if (rxrpc_call_is_ack_pending(call)) + __rxrpc_call_gen_normal_ACK(call,0); + + /* send the abort packet only if we actually traded some other packets */ + ret = 0; + if (call->pkt_snd_count || call->pkt_rcv_count) { + /* actually send the abort */ + _proto("Rx Sending Call ABORT { data=%d }",call->app_abort_code); + + _error = htonl(call->app_abort_code); + + diov[0].iov_len = sizeof(_error); + diov[0].iov_base = &_error; + + ret = rxrpc_conn_newmsg(conn,call,RXRPC_PACKET_TYPE_ABORT,1,diov,GFP_KERNEL,&msg); + if (ret==0) { + ret = rxrpc_conn_sendmsg(conn,msg); + rxrpc_put_message(msg); + } + } + + /* tell the app layer to let go */ + call->app_error_func(call); + + rxrpc_put_call(call); + + _leave(" = %d",ret); + + return ret; +} /* end __rxrpc_call_abort() */ + +/*****************************************************************************/ +/* + * send an abort message at call or connection level + * - the supplied error code is sent as the packet data + */ +int rxrpc_call_abort(struct rxrpc_call *call, int error) +{ + spin_lock(&call->lock); + + return __rxrpc_call_abort(call,error); + +} /* end rxrpc_call_abort() */ + +/*****************************************************************************/ +/* + * process packets waiting for this call + */ +static void rxrpc_call_receive_packet(struct rxrpc_call *call) +{ + struct rxrpc_message *msg; + struct list_head *_p; + u32 data32; + + _enter("%p",call); + + rxrpc_get_call(call); /* must not go away too soon if aborted by app-layer */ + + while (!list_empty(&call->rcv_receiveq)) { + /* try to get next packet */ + _p = NULL; + spin_lock(&call->lock); + if (!list_empty(&call->rcv_receiveq)) { + _p = call->rcv_receiveq.next; + list_del_init(_p); + } + spin_unlock(&call->lock); + + if (!_p) break; + + msg = list_entry(_p,struct rxrpc_message,link); + + _proto("Rx %05lu Received %s packet (%%%u,#%u,%c%c%c%c%c)", + jiffies - call->cjif, + rxrpc_pkts[msg->hdr.type], + ntohl(msg->hdr.serial), + msg->seq, + msg->hdr.flags & RXRPC_JUMBO_PACKET ? 'j' : '-', + msg->hdr.flags & RXRPC_MORE_PACKETS ? 'm' : '-', + msg->hdr.flags & RXRPC_LAST_PACKET ? 'l' : '-', + msg->hdr.flags & RXRPC_REQUEST_ACK ? 'r' : '-', + msg->hdr.flags & RXRPC_CLIENT_INITIATED ? 'C' : 'S' + ); + + switch (msg->hdr.type) { + /* deal with data packets */ + case RXRPC_PACKET_TYPE_DATA: + /* ACK the packet if necessary */ + switch (rxrpc_call_generate_ACK(call,&msg->hdr,NULL)) { + case 0: /* useful packet */ + rxrpc_call_receive_data_packet(call,msg); + break; + case 1: /* duplicate or out-of-window packet */ + break; + default: + rxrpc_put_message(msg); + goto out; + } + break; + + /* deal with ACK packets */ + case RXRPC_PACKET_TYPE_ACK: + rxrpc_call_receive_ack_packet(call,msg); + break; + + /* deal with abort packets */ + case RXRPC_PACKET_TYPE_ABORT: + data32 = 0; + if (skb_copy_bits(msg->pkt,msg->offset,&data32,sizeof(data32))<0) { + printk("Rx Received short ABORT packet\n"); + } + else { + data32 = ntohl(data32); + } + + _proto("Rx Received Call ABORT { data=%d }",data32); + + spin_lock(&call->lock); + call->app_call_state = RXRPC_CSTATE_ERROR; + call->app_err_state = RXRPC_ESTATE_PEER_ABORT; + call->app_abort_code = data32; + call->app_errno = -ECONNABORTED; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + call->app_async_read = 0; + + /* ask the app to translate the error code */ + call->app_aemap_func(call); + _state(call); + spin_unlock(&call->lock); + call->app_error_func(call); + break; + + default: + /* deal with other packet types */ + _proto("Rx Unsupported packet type %u (#%u)",msg->hdr.type,msg->seq); + break; + } + + rxrpc_put_message(msg); + } + + out: + rxrpc_put_call(call); + _leave(""); +} /* end rxrpc_call_receive_packet() */ + +/*****************************************************************************/ +/* + * process next data packet + * - as the next data packet arrives: + * - it is queued on app_readyq _if_ it is the next one expected (app_ready_seq+1) + * - it is queued on app_unreadyq _if_ it is not the next one expected + * - if a packet placed on app_readyq completely fills a hole leading up to the first packet + * on app_unreadyq, then packets now in sequence are tranferred to app_readyq + * - the application layer can only see packets on app_readyq (app_ready_qty bytes) + * - the application layer is prodded every time a new packet arrives + */ +static void rxrpc_call_receive_data_packet(struct rxrpc_call *call, struct rxrpc_message *msg) +{ + const struct rxrpc_operation *optbl, *op; + struct rxrpc_message *pmsg; + struct list_head *_p; + int ret, lo, hi, rmtimo; + u32 opid; + + _enter("%p{%u},%p{%u}",call,ntohl(call->call_id),msg,msg->seq); + + rxrpc_get_message(msg); + + /* add to the unready queue if we'd have to create a hole in the ready queue otherwise */ + if (msg->seq != call->app_ready_seq+1) { + _debug("Call add packet %d to unreadyq",msg->seq); + + /* insert in seq order */ + list_for_each(_p,&call->app_unreadyq) { + pmsg = list_entry(_p,struct rxrpc_message,link); + if (pmsg->seq>msg->seq) + break; + } + + list_add_tail(&msg->link,_p); + + _leave(" [unreadyq]"); + return; + } + + /* next in sequence - simply append into the call's ready queue */ + _debug("Call add packet %d to readyq (+%d => %d bytes)", + msg->seq,msg->dsize,call->app_ready_qty); + + spin_lock(&call->lock); + call->app_ready_seq = msg->seq; + call->app_ready_qty += msg->dsize; + list_add_tail(&msg->link,&call->app_readyq); + + /* move unready packets to the readyq if we got rid of a hole */ + while (!list_empty(&call->app_unreadyq)) { + pmsg = list_entry(call->app_unreadyq.next,struct rxrpc_message,link); + + if (pmsg->seq != call->app_ready_seq+1) + break; + + /* next in sequence - just move list-to-list */ + _debug("Call transfer packet %d to readyq (+%d => %d bytes)", + pmsg->seq,pmsg->dsize,call->app_ready_qty); + + call->app_ready_seq = pmsg->seq; + call->app_ready_qty += pmsg->dsize; + list_del_init(&pmsg->link); + list_add_tail(&pmsg->link,&call->app_readyq); + } + + /* see if we've got the last packet yet */ + if (!list_empty(&call->app_readyq)) { + pmsg = list_entry(call->app_readyq.prev,struct rxrpc_message,link); + if (pmsg->hdr.flags & RXRPC_LAST_PACKET) { + call->app_last_rcv = 1; + _debug("Last packet on readyq"); + } + } + + switch (call->app_call_state) { + /* do nothing if call already aborted */ + case RXRPC_CSTATE_ERROR: + spin_unlock(&call->lock); + _leave(" [error]"); + return; + + /* extract the operation ID from an incoming call if that's not yet been done */ + case RXRPC_CSTATE_SRVR_RCV_OPID: + spin_unlock(&call->lock); + + /* handle as yet insufficient data for the operation ID */ + if (call->app_ready_qty<4) { + if (call->app_last_rcv) + rxrpc_call_abort(call,-EINVAL); /* trouble - last packet seen */ + + _leave(""); + return; + } + + /* pull the operation ID out of the buffer */ + ret = rxrpc_call_read_data(call,&opid,sizeof(opid),0); + if (ret<0) { + printk("Unexpected error from read-data: %d\n",ret); + if (call->app_call_state!=RXRPC_CSTATE_ERROR) + rxrpc_call_abort(call,ret); + _leave(""); + return; + } + call->app_opcode = ntohl(opid); + + /* locate the operation in the available ops table */ + optbl = call->conn->service->ops_begin; + lo = 0; + hi = call->conn->service->ops_end - optbl; + + while (loapp_opcode==op->id) + goto found_op; + if (call->app_opcode>op->id) + lo = mid+1; + else + hi = mid; + } + + /* search failed */ + kproto("Rx Client requested operation %d from %s service", + call->app_opcode,call->conn->service->name); + rxrpc_call_abort(call,-EINVAL); + _leave(" [inval]"); + return; + + found_op: + _proto("Rx Client requested operation %s from %s service", + op->name,call->conn->service->name); + + /* we're now waiting for the argument block (unless the call was aborted) */ + spin_lock(&call->lock); + if (call->app_call_state==RXRPC_CSTATE_SRVR_RCV_OPID || + call->app_call_state==RXRPC_CSTATE_SRVR_SND_REPLY) { + if (!call->app_last_rcv) + call->app_call_state = RXRPC_CSTATE_SRVR_RCV_ARGS; + else if (call->app_ready_qty>0) + call->app_call_state = RXRPC_CSTATE_SRVR_GOT_ARGS; + else + call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY; + call->app_mark = op->asize; + call->app_user = op->user; + } + spin_unlock(&call->lock); + + _state(call); + break; + + case RXRPC_CSTATE_SRVR_RCV_ARGS: + /* change state if just received last packet of arg block */ + if (call->app_last_rcv) + call->app_call_state = RXRPC_CSTATE_SRVR_GOT_ARGS; + spin_unlock(&call->lock); + + _state(call); + break; + + case RXRPC_CSTATE_CLNT_RCV_REPLY: + /* change state if just received last packet of reply block */ + rmtimo = 0; + if (call->app_last_rcv) { + call->app_call_state = RXRPC_CSTATE_CLNT_GOT_REPLY; + rmtimo = 1; + } + spin_unlock(&call->lock); + + if (rmtimo) { + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + } + + _state(call); + break; + + default: + /* deal with data reception in an unexpected state */ + printk("Unexpected state [[[ %u ]]]\n",call->app_call_state); + __rxrpc_call_abort(call,-EBADMSG); + _leave(""); + return; + } + + if (call->app_call_state==RXRPC_CSTATE_CLNT_RCV_REPLY && call->app_last_rcv) + BUG(); + + /* otherwise just invoke the data function whenever we can satisfy its desire for more + * data + */ + _proto("Rx Received Op Data: st=%u qty=%u mk=%u%s", + call->app_call_state,call->app_ready_qty,call->app_mark, + call->app_last_rcv ? " last-rcvd" : ""); + + spin_lock(&call->lock); + + ret = __rxrpc_call_read_data(call); + switch (ret) { + case 0: + spin_unlock(&call->lock); + call->app_attn_func(call); + break; + case -EAGAIN: + spin_unlock(&call->lock); + break; + case -ECONNABORTED: + spin_unlock(&call->lock); + break; + default: + __rxrpc_call_abort(call,ret); + break; + } + + _state(call); + + _leave(""); + +} /* end rxrpc_call_receive_data_packet() */ + +/*****************************************************************************/ +/* + * received an ACK packet + */ +static void rxrpc_call_receive_ack_packet(struct rxrpc_call *call, struct rxrpc_message *msg) +{ + struct rxrpc_ackpacket ack; + rxrpc_serial_t serial; + rxrpc_seq_t seq; + int ret; + + _enter("%p{%u},%p{%u}",call,ntohl(call->call_id),msg,msg->seq); + + /* extract the basic ACK record */ + if (skb_copy_bits(msg->pkt,msg->offset,&ack,sizeof(ack))<0) { + printk("Rx Received short ACK packet\n"); + return; + } + msg->offset += sizeof(ack); + + serial = ack.serial; + seq = ntohl(ack.firstPacket); + + _proto("Rx Received ACK %%%d { b=%hu m=%hu f=%u p=%u s=%u r=%s n=%u }", + ntohl(msg->hdr.serial), + ntohs(ack.bufferSpace), + ntohs(ack.maxSkew), + seq, + ntohl(ack.previousPacket), + ntohl(serial), + rxrpc_acks[ack.reason], + call->ackr.nAcks + ); + + /* check the other side isn't ACK'ing a sequence number I haven't sent yet */ + if (ack.nAcks>0 && (seq > call->snd_seq_count || seq+ack.nAcks-1 > call->snd_seq_count)) { + printk("Received ACK (#%u-#%u) for unsent packet\n",seq,seq+ack.nAcks-1); + rxrpc_call_abort(call,-EINVAL); + _leave(""); + return; + } + + /* deal with RTT calculation */ + if (serial) { + struct rxrpc_message *rttmsg; + + /* find the prompting packet */ + spin_lock(&call->lock); + if (call->snd_ping && call->snd_ping->hdr.serial==serial) { + /* it was a ping packet */ + rttmsg = call->snd_ping; + call->snd_ping = NULL; + spin_unlock(&call->lock); + + if (rttmsg) { + rttmsg->rttdone = 1; + rxrpc_peer_calculate_rtt(call->conn->peer,rttmsg,msg); + rxrpc_put_message(rttmsg); + } + } + else { + struct list_head *_p; + + /* it ought to be a data packet - look in the pending ACK list */ + list_for_each(_p,&call->acks_pendq) { + rttmsg = list_entry(_p,struct rxrpc_message,link); + if (rttmsg->hdr.serial==serial) { + if (rttmsg->rttdone) + break; /* never do RTT twice without resending */ + + rttmsg->rttdone = 1; + rxrpc_peer_calculate_rtt(call->conn->peer,rttmsg,msg); + break; + } + } + spin_unlock(&call->lock); + } + } + + switch (ack.reason) { + /* deal with negative/positive acknowledgement of data packets */ + case RXRPC_ACK_REQUESTED: + case RXRPC_ACK_DELAY: + case RXRPC_ACK_IDLE: + rxrpc_call_definitively_ACK(call,seq-1); + + case RXRPC_ACK_DUPLICATE: + case RXRPC_ACK_OUT_OF_SEQUENCE: + case RXRPC_ACK_EXCEEDS_WINDOW: + call->snd_resend_cnt = 0; + ret = rxrpc_call_record_ACK(call,msg,seq,ack.nAcks); + if (ret<0) + rxrpc_call_abort(call,ret); + break; + + /* respond to ping packets immediately */ + case RXRPC_ACK_PING: + rxrpc_call_generate_ACK(call,&msg->hdr,&ack); + break; + + /* only record RTT on ping response packets */ + case RXRPC_ACK_PING_RESPONSE: + if (call->snd_ping) { + struct rxrpc_message *rttmsg; + + /* only do RTT stuff if the response matches the retained ping */ + rttmsg = NULL; + spin_lock(&call->lock); + if (call->snd_ping && call->snd_ping->hdr.serial==ack.serial) { + rttmsg = call->snd_ping; + call->snd_ping = NULL; + } + spin_unlock(&call->lock); + + if (rttmsg) { + rttmsg->rttdone = 1; + rxrpc_peer_calculate_rtt(call->conn->peer,rttmsg,msg); + rxrpc_put_message(rttmsg); + } + } + break; + + default: + printk("Unsupported ACK reason %u\n",ack.reason); + break; + } + + _leave(""); +} /* end rxrpc_call_receive_ack_packet() */ + +/*****************************************************************************/ +/* + * record definitive ACKs for all messages up to and including the one with the 'highest' seq + */ +static void rxrpc_call_definitively_ACK(struct rxrpc_call *call, rxrpc_seq_t highest) +{ + struct rxrpc_message *msg; + int now_complete; + + _enter("%p{ads=%u},%u",call,call->acks_dftv_seq,highest); + + while (call->acks_dftv_seqacks_dftv_seq++; + + _proto("Definitive ACK on packet #%u",call->acks_dftv_seq); + + /* discard those at front of queue until message with highest ACK is found */ + spin_lock(&call->lock); + msg = NULL; + if (!list_empty(&call->acks_pendq)) { + msg = list_entry(call->acks_pendq.next,struct rxrpc_message,link); + list_del_init(&msg->link); /* dequeue */ + if (msg->state==RXRPC_MSG_SENT) + call->acks_pend_cnt--; + } + spin_unlock(&call->lock); + + /* insanity check */ + if (!msg) + panic("%s(): acks_pendq unexpectedly empty\n",__FUNCTION__); + + if (msg->seq!=call->acks_dftv_seq) + panic("%s(): Packet #%u expected at front of acks_pendq (#%u found)\n", + __FUNCTION__,call->acks_dftv_seq,msg->seq); + + /* discard the message */ + msg->state = RXRPC_MSG_DONE; + rxrpc_put_message(msg); + } + + /* if all sent packets are definitively ACK'd then prod any sleepers just in case */ + now_complete = 0; + spin_lock(&call->lock); + if (call->acks_dftv_seq==call->snd_seq_count) { + if (call->app_call_state!=RXRPC_CSTATE_COMPLETE) { + call->app_call_state = RXRPC_CSTATE_COMPLETE; + _state(call); + now_complete = 1; + } + } + spin_unlock(&call->lock); + + if (now_complete) { + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + call->app_attn_func(call); + } + + _leave(""); +} /* end rxrpc_call_definitively_ACK() */ + +/*****************************************************************************/ +/* + * record the specified amount of ACKs/NAKs + */ +static int rxrpc_call_record_ACK(struct rxrpc_call *call, + struct rxrpc_message *msg, + rxrpc_seq_t seq, + size_t count) +{ + struct rxrpc_message *dmsg; + struct list_head *_p; + rxrpc_seq_t highest; + unsigned ix; + size_t chunk; + char resend, now_complete; + u8 acks[16]; + + _enter("%p{apc=%u ads=%u},%p,%u,%u", + call,call->acks_pend_cnt,call->acks_dftv_seq,msg,seq,count); + + /* handle re-ACK'ing of definitively ACK'd packets (may be out-of-order ACKs) */ + if (seq<=call->acks_dftv_seq) { + unsigned delta = call->acks_dftv_seq - seq; + + if (count<=delta) { + _leave(" = 0 [all definitively ACK'd]"); + return 0; + } + + seq += delta; + count -= delta; + msg->offset += delta; + } + + highest = seq + count - 1; + resend = 0; + while (count>0) { + /* extract up to 16 ACK slots at a time */ + chunk = min(count,sizeof(acks)); + count -= chunk; + + memset(acks,2,sizeof(acks)); + + if (skb_copy_bits(msg->pkt,msg->offset,&acks,chunk)<0) { + printk("Rx Received short ACK packet\n"); + _leave(" = -EINVAL"); + return -EINVAL; + } + msg->offset += chunk; + + /* check that the ACK set is valid */ + for (ix=0; ixacks_pend_cnt + ); + + /* mark the packets in the ACK queue as being provisionally ACK'd */ + ix = 0; + spin_lock(&call->lock); + + /* find the first packet ACK'd/NAK'd here */ + list_for_each(_p,&call->acks_pendq) { + dmsg = list_entry(_p,struct rxrpc_message,link); + if (dmsg->seq==seq) + goto found_first; + _debug("- %u: skipping #%u",ix,dmsg->seq); + } + goto bad_queue; + + found_first: + do { + _debug("- %u: processing #%u (%c) apc=%u", + ix,dmsg->seq,_acktype[acks[ix]],call->acks_pend_cnt); + + if (acks[ix]==RXRPC_ACK_TYPE_ACK) { + if (dmsg->state==RXRPC_MSG_SENT) call->acks_pend_cnt--; + dmsg->state = RXRPC_MSG_ACKED; + } + else { + if (dmsg->state==RXRPC_MSG_ACKED) call->acks_pend_cnt++; + dmsg->state = RXRPC_MSG_SENT; + } + ix++; + seq++; + + _p = dmsg->link.next; + dmsg = list_entry(_p,struct rxrpc_message,link); + } while(ixacks_pendq && dmsg->seq==seq); + + if (ixlock); + } + + if (resend) + rxrpc_call_resend(call,highest); + + /* if all packets are provisionally ACK'd, then wake up anyone who's waiting for that */ + now_complete = 0; + spin_lock(&call->lock); + if (call->acks_pend_cnt==0) { + if (call->app_call_state==RXRPC_CSTATE_SRVR_RCV_FINAL_ACK) { + call->app_call_state = RXRPC_CSTATE_COMPLETE; + _state(call); + } + now_complete = 1; + } + spin_unlock(&call->lock); + + if (now_complete) { + _debug("- wake up waiters"); + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + call->app_attn_func(call); + } + + _leave(" = 0 (apc=%u)",call->acks_pend_cnt); + return 0; + + bad_queue: + panic("%s(): acks_pendq in bad state (packet #%u absent)\n",__FUNCTION__,seq); + +} /* end rxrpc_call_record_ACK() */ + +/*****************************************************************************/ +/* + * transfer data from the ready packet queue to the asynchronous read buffer + * - since this func is the only one going to look at packets queued on app_readyq, we don't need + * a lock to modify or access them, only to modify the queue pointers + * - called with call->lock held + * - the buffer must be in kernel space + * - returns: + * 0 if buffer filled + * -EAGAIN if buffer not filled and more data to come + * -EBADMSG if last packet received and insufficient data left + * -ECONNABORTED if the call has in an error state + */ +static int __rxrpc_call_read_data(struct rxrpc_call *call) +{ + struct rxrpc_message *msg; + size_t qty; + int ret; + + _enter("%p{as=%d buf=%p qty=%u/%u}", + call,call->app_async_read,call->app_read_buf,call->app_ready_qty,call->app_mark); + + /* check the state */ + switch (call->app_call_state) { + case RXRPC_CSTATE_SRVR_RCV_ARGS: + case RXRPC_CSTATE_CLNT_RCV_REPLY: + if (call->app_last_rcv) { + printk("%s(%p,%p,%d): Inconsistent call state (%s, last pkt)", + __FUNCTION__,call,call->app_read_buf,call->app_mark, + rxrpc_call_states[call->app_call_state]); + BUG(); + } + break; + + case RXRPC_CSTATE_SRVR_RCV_OPID: + case RXRPC_CSTATE_SRVR_GOT_ARGS: + case RXRPC_CSTATE_CLNT_GOT_REPLY: + break; + + case RXRPC_CSTATE_SRVR_SND_REPLY: + if (!call->app_last_rcv) { + printk("%s(%p,%p,%d): Inconsistent call state (%s, not last pkt)", + __FUNCTION__,call,call->app_read_buf,call->app_mark, + rxrpc_call_states[call->app_call_state]); + BUG(); + } + _debug("Trying to read data from call in SND_REPLY state"); + break; + + case RXRPC_CSTATE_ERROR: + _leave(" = -ECONNABORTED"); + return -ECONNABORTED; + + default: + printk("reading in unexpected state [[[ %u ]]]\n",call->app_call_state); + BUG(); + } + + /* handle the case of not having an async buffer */ + if (!call->app_async_read) { + if (call->app_mark==RXRPC_APP_MARK_EOF) { + ret = call->app_last_rcv ? 0 : -EAGAIN; + } + else { + if (call->app_mark >= call->app_ready_qty) { + call->app_mark = RXRPC_APP_MARK_EOF; + ret = 0; + } + else { + ret = call->app_last_rcv ? -EBADMSG : -EAGAIN; + } + } + + _leave(" = %d [no buf]",ret); + return 0; + } + + while (!list_empty(&call->app_readyq) && call->app_mark>0) { + msg = list_entry(call->app_readyq.next,struct rxrpc_message,link); + + /* drag as much data as we need out of this packet */ + qty = min(call->app_mark,msg->dsize); + + _debug("reading %u from skb=%p off=%lu",qty,msg->pkt,msg->offset); + + if (call->app_read_buf) + if (skb_copy_bits(msg->pkt,msg->offset,call->app_read_buf,qty)<0) + panic("%s: Failed to copy data from packet: (%p,%p,%d)", + __FUNCTION__,call,call->app_read_buf,qty); + + /* if that packet is now empty, discard it */ + call->app_ready_qty -= qty; + msg->dsize -= qty; + + if (msg->dsize==0) { + list_del_init(&msg->link); + rxrpc_put_message(msg); + } + else { + msg->offset += qty; + } + + call->app_mark -= qty; + if (call->app_read_buf) call->app_read_buf += qty; + } + + if (call->app_mark==0) { + call->app_async_read = 0; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + + /* adjust the state if used up all packets */ + if (list_empty(&call->app_readyq) && call->app_last_rcv) { + switch (call->app_call_state) { + case RXRPC_CSTATE_SRVR_RCV_OPID: + call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY; + call->app_mark = RXRPC_APP_MARK_EOF; + _state(call); + del_timer_sync(&call->rcv_timeout); + break; + case RXRPC_CSTATE_SRVR_GOT_ARGS: + call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY; + _state(call); + del_timer_sync(&call->rcv_timeout); + break; + default: + call->app_call_state = RXRPC_CSTATE_COMPLETE; + _state(call); + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->ackr_dfr_timo); + del_timer_sync(&call->rcv_timeout); + break; + } + } + + _leave(" = 0"); + return 0; + } + + if (call->app_last_rcv) { + _debug("Insufficient data (%u/%u)",call->app_ready_qty,call->app_mark); + call->app_async_read = 0; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + + _leave(" = -EBADMSG"); + return -EBADMSG; + } + + _leave(" = -EAGAIN"); + return -EAGAIN; +} /* end __rxrpc_call_read_data() */ + +/*****************************************************************************/ +/* + * attempt to read the specified amount of data from the call's ready queue into the buffer + * provided + * - since this func is the only one going to look at packets queued on app_readyq, we don't need + * a lock to modify or access them, only to modify the queue pointers + * - if the buffer pointer is NULL, then data is merely drained, not copied + * - if flags&RXRPC_CALL_READ_BLOCK, then the function will wait until there is enough data or an + * error will be generated + * - note that the caller must have added the calling task to the call's wait queue beforehand + * - if flags&RXRPC_CALL_READ_ALL, then an error will be generated if this function doesn't read + * all available data + */ +int rxrpc_call_read_data(struct rxrpc_call *call, void *buffer, size_t size, int flags) +{ + int ret; + + _enter("%p{arq=%u},%p,%d,%x",call,call->app_ready_qty,buffer,size,flags); + + spin_lock(&call->lock); + + if (unlikely(!!call->app_read_buf)) { + spin_unlock(&call->lock); + _leave(" = -EBUSY"); + return -EBUSY; + } + + call->app_mark = size; + call->app_read_buf = buffer; + call->app_async_read = 1; + call->app_read_count++; + + /* read as much data as possible */ + ret = __rxrpc_call_read_data(call); + switch (ret) { + case 0: + if (flags&RXRPC_CALL_READ_ALL && (!call->app_last_rcv || call->app_ready_qty>0)) { + _leave(" = -EBADMSG"); + __rxrpc_call_abort(call,-EBADMSG); + return -EBADMSG; + } + + spin_unlock(&call->lock); + call->app_attn_func(call); + _leave(" = 0"); + return ret; + + case -ECONNABORTED: + spin_unlock(&call->lock); + _leave(" = %d [aborted]",ret); + return ret; + + default: + __rxrpc_call_abort(call,ret); + _leave(" = %d",ret); + return ret; + + case -EAGAIN: + spin_unlock(&call->lock); + + if (!(flags&RXRPC_CALL_READ_BLOCK)) { + _leave(" = -EAGAIN"); + return -EAGAIN; + } + + /* wait for the data to arrive */ + _debug("blocking for data arrival"); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (!call->app_async_read || signal_pending(current)) + break; + schedule(); + } + set_current_state(TASK_RUNNING); + + if (signal_pending(current)) { + _leave(" = -EINTR"); + return -EINTR; + } + + if (call->app_call_state==RXRPC_CSTATE_ERROR) { + _leave(" = -ECONNABORTED"); + return -ECONNABORTED; + } + + _leave(" = 0"); + return 0; + } + +} /* end rxrpc_call_read_data() */ + +/*****************************************************************************/ +/* + * write data to a call + * - the data may not be sent immediately if it doesn't fill a buffer + * - if we can't queue all the data for buffering now, siov[] will have been adjusted to take + * account of what has been sent + */ +int rxrpc_call_write_data(struct rxrpc_call *call, + size_t sioc, + struct iovec siov[], + u8 rxhdr_flags, + int alloc_flags, + int dup_data, + size_t *size_sent) +{ + struct rxrpc_message *msg; + struct iovec *sptr; + size_t space, size, chunk, tmp; + char *buf; + int ret; + + _enter("%p,%u,%p,%02x,%x,%d,%p",call,sioc,siov,rxhdr_flags,alloc_flags,dup_data,size_sent); + + *size_sent = 0; + size = 0; + ret = -EINVAL; + + /* can't send more if we've sent last packet from this end */ + switch (call->app_call_state) { + case RXRPC_CSTATE_SRVR_SND_REPLY: + case RXRPC_CSTATE_CLNT_SND_ARGS: + break; + case RXRPC_CSTATE_ERROR: + ret = call->app_errno; + default: + goto out; + } + + /* calculate how much data we've been given */ + sptr = siov; + for (; sioc>0; sptr++, sioc--) { + if (!sptr->iov_len) continue; + + if (!sptr->iov_base) + goto out; + + size += sptr->iov_len; + } + + _debug("- size=%u mtu=%u",size,call->conn->mtu_size); + + do { + /* make sure there's a message under construction */ + if (!call->snd_nextmsg) { + /* no - allocate a message with no data yet attached */ + ret = rxrpc_conn_newmsg(call->conn,call,RXRPC_PACKET_TYPE_DATA, + 0,NULL,alloc_flags,&call->snd_nextmsg); + if (ret<0) + goto out; + _debug("- allocated new message [ds=%u]",call->snd_nextmsg->dsize); + } + + msg = call->snd_nextmsg; + msg->hdr.flags |= rxhdr_flags; + + /* deal with zero-length terminal packet */ + if (size==0) { + if (rxhdr_flags & RXRPC_LAST_PACKET) { + ret = rxrpc_call_flush(call); + if (ret<0) + goto out; + } + break; + } + + /* work out how much space current packet has available */ + space = call->conn->mtu_size - msg->dsize; + chunk = min(space,size); + + _debug("- [before] space=%u chunk=%u",space,chunk); + + while (!siov->iov_len) + siov++; + + /* if we are going to have to duplicate the data then coalesce it too */ + if (dup_data) { + /* don't allocate more that 1 page at a time */ + if (chunk>PAGE_SIZE) + chunk = PAGE_SIZE; + + /* allocate a data buffer and attach to the message */ + buf = kmalloc(chunk,alloc_flags); + if (unlikely(!buf)) { + if (msg->dsize==sizeof(struct rxrpc_header)) { + /* discard an empty msg and wind back the seq counter */ + rxrpc_put_message(msg); + call->snd_nextmsg = NULL; + call->snd_seq_count--; + } + + ret = -ENOMEM; + goto out; + } + + tmp = msg->dcount++; + set_bit(tmp,&msg->dfree); + msg->data[tmp].iov_base = buf; + msg->data[tmp].iov_len = chunk; + msg->dsize += chunk; + *size_sent += chunk; + size -= chunk; + + /* load the buffer with data */ + while (chunk>0) { + tmp = min(chunk,siov->iov_len); + memcpy(buf,siov->iov_base,tmp); + buf += tmp; + siov->iov_base += tmp; + siov->iov_len -= tmp; + if (!siov->iov_len) + siov++; + chunk -= tmp; + } + } + else { + /* we want to attach the supplied buffers directly */ + while (chunk>0 && msg->dcountdcount++; + msg->data[tmp].iov_base = siov->iov_base; + msg->data[tmp].iov_len = siov->iov_len; + msg->dsize += siov->iov_len; + *size_sent += siov->iov_len; + size -= siov->iov_len; + chunk -= siov->iov_len; + siov++; + } + } + + _debug("- [loaded] chunk=%u size=%u",chunk,size); + + /* dispatch the message when full, final or requesting ACK */ + if (msg->dsize>=call->conn->mtu_size || rxhdr_flags) { + ret = rxrpc_call_flush(call); + if (ret<0) + goto out; + } + + } while(size>0); + + ret = 0; + out: + _leave(" = %d (%d queued, %d rem)",ret,*size_sent,size); + return ret; + +} /* end rxrpc_call_write_data() */ + +/*****************************************************************************/ +/* + * flush outstanding packets to the network + */ +int rxrpc_call_flush(struct rxrpc_call *call) +{ + struct rxrpc_message *msg; + int ret = 0; + + _enter("%p",call); + + rxrpc_get_call(call); + + /* if there's a packet under construction, then dispatch it now */ + if (call->snd_nextmsg) { + msg = call->snd_nextmsg; + call->snd_nextmsg = NULL; + + if (msg->hdr.flags & RXRPC_LAST_PACKET) { + msg->hdr.flags &= ~RXRPC_MORE_PACKETS; + msg->hdr.flags |= RXRPC_REQUEST_ACK; + } + else { + msg->hdr.flags |= RXRPC_MORE_PACKETS; + } + + _proto("Sending DATA message { ds=%u dc=%u df=%02lu }", + msg->dsize,msg->dcount,msg->dfree); + + /* queue and adjust call state */ + spin_lock(&call->lock); + list_add_tail(&msg->link,&call->acks_pendq); + + /* decide what to do depending on current state and if this is the last packet */ + ret = -EINVAL; + switch (call->app_call_state) { + case RXRPC_CSTATE_SRVR_SND_REPLY: + if (msg->hdr.flags & RXRPC_LAST_PACKET) { + call->app_call_state = RXRPC_CSTATE_SRVR_RCV_FINAL_ACK; + _state(call); + } + break; + + case RXRPC_CSTATE_CLNT_SND_ARGS: + if (msg->hdr.flags & RXRPC_LAST_PACKET) { + call->app_call_state = RXRPC_CSTATE_CLNT_RCV_REPLY; + _state(call); + } + break; + + case RXRPC_CSTATE_ERROR: + ret = call->app_errno; + default: + spin_unlock(&call->lock); + goto out; + } + + call->acks_pend_cnt++; + + mod_timer(&call->acks_timeout,jiffies + rxrpc_call_acks_timeout); + + spin_unlock(&call->lock); + + ret = rxrpc_conn_sendmsg(call->conn,msg); + if (ret==0) + call->pkt_snd_count++; + } + + out: + rxrpc_put_call(call); + + _leave(" = %d",ret); + return ret; + +} /* end rxrpc_call_flush() */ + +/*****************************************************************************/ +/* + * resend NAK'd or unacknowledged packets up to the highest one specified + */ +static void rxrpc_call_resend(struct rxrpc_call *call, rxrpc_seq_t highest) +{ + struct rxrpc_message *msg; + struct list_head *_p; + rxrpc_seq_t seq = 0; + + _enter("%p,%u",call,highest); + + _proto("Rx Resend required"); + + /* handle too many resends */ + if (call->snd_resend_cnt>=rxrpc_call_max_resend) { + _debug("Aborting due to too many resends (rcv=%d)",call->pkt_rcv_count); + rxrpc_call_abort(call,call->pkt_rcv_count>0?-EIO:-ETIMEDOUT); + _leave(""); + return; + } + + spin_lock(&call->lock); + call->snd_resend_cnt++; + for (;;) { + /* determine which the next packet we might need to ACK is */ + if (seq<=call->acks_dftv_seq) + seq = call->acks_dftv_seq; + seq++; + + if (seq>highest) + break; + + /* look for the packet in the pending-ACK queue */ + list_for_each(_p,&call->acks_pendq) { + msg = list_entry(_p,struct rxrpc_message,link); + if (msg->seq==seq) + goto found_msg; + } + + panic("%s(%p,%d): Inconsistent pending-ACK queue (ds=%u sc=%u sq=%u)\n", + __FUNCTION__,call,highest,call->acks_dftv_seq,call->snd_seq_count,seq); + + found_msg: + if (msg->state!=RXRPC_MSG_SENT) + continue; /* only un-ACK'd packets */ + + rxrpc_get_message(msg); + spin_unlock(&call->lock); + + /* send each message again (and ignore any errors we might incur) */ + _proto("Resending DATA message { ds=%u dc=%u df=%02lu }", + msg->dsize,msg->dcount,msg->dfree); + + if (rxrpc_conn_sendmsg(call->conn,msg)==0) + call->pkt_snd_count++; + + rxrpc_put_message(msg); + + spin_lock(&call->lock); + } + + /* reset the timeout */ + mod_timer(&call->acks_timeout,jiffies + rxrpc_call_acks_timeout); + + spin_unlock(&call->lock); + + _leave(""); +} /* end rxrpc_call_resend() */ + +/*****************************************************************************/ +/* + * handle an ICMP error being applied to a call + */ +void rxrpc_call_handle_error(struct rxrpc_call *call, int local, int errno) +{ + _enter("%p{%u},%d",call,ntohl(call->call_id),errno); + + /* if this call is already aborted, then just wake up any waiters */ + if (call->app_call_state==RXRPC_CSTATE_ERROR) { + call->app_error_func(call); + } + else { + /* tell the app layer what happened */ + spin_lock(&call->lock); + call->app_call_state = RXRPC_CSTATE_ERROR; + _state(call); + if (local) + call->app_err_state = RXRPC_ESTATE_LOCAL_ERROR; + else + call->app_err_state = RXRPC_ESTATE_REMOTE_ERROR; + call->app_errno = errno; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + call->app_async_read = 0; + + /* map the error */ + call->app_aemap_func(call); + + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + + spin_unlock(&call->lock); + + call->app_error_func(call); + } + + _leave(""); +} /* end rxrpc_call_handle_error() */ diff -urNp linux-5240/net/rxrpc/connection.c linux-5250/net/rxrpc/connection.c --- linux-5240/net/rxrpc/connection.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/connection.c @@ -0,0 +1,686 @@ +/* connection.c: Rx connection routines + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +__RXACCT_DECL(atomic_t rxrpc_connection_count); + +LIST_HEAD(rxrpc_conns); +DECLARE_RWSEM(rxrpc_conns_sem); + +static void __rxrpc_conn_timeout(rxrpc_timer_t *timer) +{ + struct rxrpc_connection *conn = list_entry(timer,struct rxrpc_connection,timeout); + + _debug("Rx CONN TIMEOUT [%p{u=%d}]",conn,atomic_read(&conn->usage)); + + rxrpc_conn_do_timeout(conn); +} + +static const struct rxrpc_timer_ops rxrpc_conn_timer_ops = { + timed_out: __rxrpc_conn_timeout, +}; + +/*****************************************************************************/ +/* + * create a new connection record + */ +static inline int __rxrpc_create_connection(struct rxrpc_peer *peer, + struct rxrpc_connection **_conn) +{ + struct rxrpc_connection *conn; + + _enter("%p",peer); + + /* allocate and initialise a connection record */ + conn = kmalloc(sizeof(struct rxrpc_connection),GFP_KERNEL); + if (!conn) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(conn,0,sizeof(struct rxrpc_connection)); + atomic_set(&conn->usage,1); + + INIT_LIST_HEAD(&conn->link); + init_waitqueue_head(&conn->chanwait); + spin_lock_init(&conn->lock); + rxrpc_timer_init(&conn->timeout,&rxrpc_conn_timer_ops); + + conn->atime = xtime; + conn->mtu_size = 1024; + conn->peer = peer; + conn->trans = peer->trans; + + __RXACCT(atomic_inc(&rxrpc_connection_count)); + *_conn = conn; + _leave(" = 0 (%p)",conn); + + return 0; +} /* end __rxrpc_create_connection() */ + +/*****************************************************************************/ +/* + * create a new connection record for outgoing connections + */ +int rxrpc_create_connection(struct rxrpc_transport *trans, + u16 port, + u32 addr, + unsigned short service_id, + void *security, + struct rxrpc_connection **_conn) +{ + struct rxrpc_connection *conn; + struct rxrpc_peer *peer; + int ret; + + _enter("%p{%hu},%u,%hu",trans,trans->port,ntohs(port),service_id); + + /* get a peer record */ + ret = rxrpc_peer_lookup(trans,addr,&peer); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + /* allocate and initialise a connection record */ + ret = __rxrpc_create_connection(peer,&conn); + if (ret<0) { + rxrpc_put_peer(peer); + _leave(" = %d",ret); + return ret; + } + + /* fill in the specific bits */ + conn->addr.sin_family = AF_INET; + conn->addr.sin_port = port; + conn->addr.sin_addr.s_addr = addr; + + conn->in_epoch = rxrpc_epoch; + conn->out_epoch = rxrpc_epoch; + conn->in_clientflag = 0; + conn->out_clientflag = RXRPC_CLIENT_INITIATED; + conn->conn_id = htonl((unsigned) conn & RXRPC_CIDMASK); + conn->service_id = htons(service_id); + + /* attach to peer */ + conn->peer = peer; + + write_lock(&peer->conn_lock); + list_add_tail(&conn->link,&peer->conn_active); + atomic_inc(&peer->conn_count); + write_unlock(&peer->conn_lock); + + down_write(&rxrpc_conns_sem); + list_add_tail(&conn->proc_link,&rxrpc_conns); + up_write(&rxrpc_conns_sem); + + *_conn = conn; + _leave(" = 0 (%p)",conn); + + return 0; +} /* end rxrpc_create_connection() */ + +/*****************************************************************************/ +/* + * lookup the connection for an incoming packet + * - create a new connection record for unrecorded incoming connections + */ +int rxrpc_connection_lookup(struct rxrpc_peer *peer, + struct rxrpc_message *msg, + struct rxrpc_connection **_conn) +{ + struct rxrpc_connection *conn, *candidate = NULL; + struct list_head *_p; + int ret, fresh = 0; + u32 x_epoch, x_connid; + u16 x_port, x_secix, x_servid; + u8 x_clflag; + + _enter("%p{{%hu}},%u,%hu", + peer,peer->trans->port,ntohs(msg->pkt->h.uh->source),ntohs(msg->hdr.serviceId)); + + x_port = msg->pkt->h.uh->source; + x_epoch = msg->hdr.epoch; + x_clflag = msg->hdr.flags & RXRPC_CLIENT_INITIATED; + x_connid = htonl(ntohl(msg->hdr.cid) & RXRPC_CIDMASK); + x_servid = msg->hdr.serviceId; + x_secix = msg->hdr.securityIndex; + + /* [common case] search the transport's active list first */ + read_lock(&peer->conn_lock); + list_for_each(_p,&peer->conn_active) { + conn = list_entry(_p,struct rxrpc_connection,link); + if (conn->addr.sin_port == x_port && + conn->in_epoch == x_epoch && + conn->conn_id == x_connid && + conn->security_ix == x_secix && + conn->service_id == x_servid && + conn->in_clientflag == x_clflag) + goto found_active; + } + read_unlock(&peer->conn_lock); + + /* [uncommon case] not active + * - create a candidate for a new record if an inbound connection + * - only examine the graveyard for an outbound connection + */ + if (x_clflag) { + ret = __rxrpc_create_connection(peer,&candidate); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + /* fill in the specifics */ + candidate->addr.sin_family = AF_INET; + candidate->addr.sin_port = x_port; + candidate->addr.sin_addr.s_addr = msg->pkt->nh.iph->saddr; + candidate->in_epoch = x_epoch; + candidate->out_epoch = x_epoch; + candidate->in_clientflag = RXRPC_CLIENT_INITIATED; + candidate->out_clientflag = 0; + candidate->conn_id = x_connid; + candidate->service_id = x_servid; + candidate->security_ix = x_secix; + } + + /* search the active list again, just in case it appeared whilst we were busy */ + write_lock(&peer->conn_lock); + list_for_each(_p,&peer->conn_active) { + conn = list_entry(_p,struct rxrpc_connection,link); + if (conn->addr.sin_port == x_port && + conn->in_epoch == x_epoch && + conn->conn_id == x_connid && + conn->security_ix == x_secix && + conn->service_id == x_servid && + conn->in_clientflag == x_clflag) + goto found_active_second_chance; + } + + /* search the transport's graveyard list */ + spin_lock(&peer->conn_gylock); + list_for_each(_p,&peer->conn_graveyard) { + conn = list_entry(_p,struct rxrpc_connection,link); + if (conn->addr.sin_port == x_port && + conn->in_epoch == x_epoch && + conn->conn_id == x_connid && + conn->security_ix == x_secix && + conn->service_id == x_servid && + conn->in_clientflag == x_clflag) + goto found_in_graveyard; + } + spin_unlock(&peer->conn_gylock); + + /* outbound connections aren't created here */ + if (!x_clflag) { + write_unlock(&peer->conn_lock); + _leave(" = -ENOENT"); + return -ENOENT; + } + + /* we can now add the new candidate to the list */ + rxrpc_get_peer(peer); + conn = candidate; + candidate = NULL; + atomic_inc(&peer->conn_count); + fresh = 1; + + make_active: + list_add_tail(&conn->link,&peer->conn_active); + + success_uwfree: + write_unlock(&peer->conn_lock); + + if (candidate) { + __RXACCT(atomic_dec(&rxrpc_connection_count)); + kfree(candidate); + } + + if (fresh) { + down_write(&rxrpc_conns_sem); + list_add_tail(&conn->proc_link,&rxrpc_conns); + up_write(&rxrpc_conns_sem); + } + + success: + *_conn = conn; + _leave(" = 0 (%p)",conn); + return 0; + + /* handle the connection being found in the active list straight off */ + found_active: + rxrpc_get_connection(conn); + read_unlock(&peer->conn_lock); + goto success; + + /* handle resurrecting a connection from the graveyard */ + found_in_graveyard: + rxrpc_get_peer(peer); + rxrpc_get_connection(conn); + rxrpc_krxtimod_del_timer(&conn->timeout); + list_del_init(&conn->link); + spin_unlock(&peer->conn_gylock); + goto make_active; + + /* handle finding the connection on the second time through the active list */ + found_active_second_chance: + rxrpc_get_connection(conn); + goto success_uwfree; + +} /* end rxrpc_connection_lookup() */ + +/*****************************************************************************/ +/* + * finish using a connection record + * - it will be transferred to the peer's connection graveyard when refcount reaches 0 + */ +void rxrpc_put_connection(struct rxrpc_connection *conn) +{ + struct rxrpc_peer *peer = conn->peer; + + _enter("%p{u=%d p=%hu}",conn,atomic_read(&conn->usage),ntohs(conn->addr.sin_port)); + + /* sanity check */ + if (atomic_read(&conn->usage)<=0) + BUG(); + + spin_lock(&peer->conn_gylock); + if (likely(!atomic_dec_and_test(&conn->usage))) { + spin_unlock(&peer->conn_gylock); + _leave(""); + return; + } + + /* move to graveyard queue */ + list_del(&conn->link); + list_add_tail(&conn->link,&peer->conn_graveyard); + + /* discard in 100 secs */ + rxrpc_krxtimod_add_timer(&conn->timeout,20*HZ); + + spin_unlock(&peer->conn_gylock); + + rxrpc_put_peer(conn->peer); + + _leave(" [killed]"); +} /* end rxrpc_put_connection() */ + +/*****************************************************************************/ +/* + * free a connection record + */ +void rxrpc_conn_do_timeout(struct rxrpc_connection *conn) +{ + struct rxrpc_peer *peer; + + _enter("%p{u=%d p=%hu}",conn,atomic_read(&conn->usage),ntohs(conn->addr.sin_port)); + + peer = conn->peer; + + if (atomic_read(&conn->usage)<0) + BUG(); + + /* remove from graveyard if still dead */ + spin_lock(&peer->conn_gylock); + if (atomic_read(&conn->usage)==0) { + list_del_init(&conn->link); + } + else { + conn = NULL; + } + spin_unlock(&peer->conn_gylock); + + if (!conn) { + _leave(""); + return; /* resurrected */ + } + + _debug("--- Destroying Connection %p ---",conn); + + down_write(&rxrpc_conns_sem); + list_del(&conn->proc_link); + up_write(&rxrpc_conns_sem); + + __RXACCT(atomic_dec(&rxrpc_connection_count)); + kfree(conn); + + /* if the graveyard is now empty, wake up anyone waiting for that */ + if (atomic_dec_and_test(&peer->conn_count)) + wake_up(&peer->conn_gy_waitq); + + _leave(" [destroyed]"); +} /* end rxrpc_conn_do_timeout() */ + +/*****************************************************************************/ +/* + * clear all connection records from a peer endpoint + */ +void rxrpc_conn_clearall(struct rxrpc_peer *peer) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_connection *conn; + int err; + + _enter("%p",peer); + + /* there shouldn't be any active conns remaining */ + if (!list_empty(&peer->conn_active)) + BUG(); + + /* manually timeout all conns in the graveyard */ + spin_lock(&peer->conn_gylock); + while (!list_empty(&peer->conn_graveyard)) { + conn = list_entry(peer->conn_graveyard.next,struct rxrpc_connection,link); + err = rxrpc_krxtimod_del_timer(&conn->timeout); + spin_unlock(&peer->conn_gylock); + + if (err==0) + rxrpc_conn_do_timeout(conn); + + spin_lock(&peer->conn_gylock); + } + spin_unlock(&peer->conn_gylock); + + /* wait for the the conn graveyard to be completely cleared */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&peer->conn_gy_waitq,&myself); + + while (atomic_read(&peer->conn_count)!=0) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } + + remove_wait_queue(&peer->conn_gy_waitq,&myself); + set_current_state(TASK_RUNNING); + + _leave(""); + +} /* end rxrpc_conn_clearall() */ + +/*****************************************************************************/ +/* + * allocate and prepare a message for sending out through the transport endpoint + */ +int rxrpc_conn_newmsg(struct rxrpc_connection *conn, + struct rxrpc_call *call, + u8 type, + int dcount, + struct iovec diov[], + int alloc_flags, + struct rxrpc_message **_msg) +{ + struct rxrpc_message *msg; + int loop; + + _enter("%p{%d},%p,%u",conn,ntohs(conn->addr.sin_port),call,type); + + if (dcount>3) { + _leave(" = -EINVAL"); + return -EINVAL; + } + + msg = kmalloc(sizeof(struct rxrpc_message),alloc_flags); + if (!msg) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(msg,0,sizeof(*msg)); + atomic_set(&msg->usage,1); + + INIT_LIST_HEAD(&msg->link); + + msg->state = RXRPC_MSG_PREPARED; + + msg->hdr.epoch = conn->out_epoch; + msg->hdr.cid = conn->conn_id | (call ? call->chan_ix : 0); + msg->hdr.callNumber = call ? call->call_id : 0; + msg->hdr.type = type; + msg->hdr.flags = conn->out_clientflag; + msg->hdr.securityIndex = conn->security_ix; + msg->hdr.serviceId = conn->service_id; + + /* generate sequence numbers for data packets */ + if (call) { + switch (type) { + case RXRPC_PACKET_TYPE_DATA: + msg->seq = ++call->snd_seq_count; + msg->hdr.seq = htonl(msg->seq); + break; + case RXRPC_PACKET_TYPE_ACK: + /* ACK sequence numbers are complicated. The following may be wrong: + * - jumbo packet ACKs should have a seq number + * - normal ACKs should not + */ + default: + break; + } + } + + msg->dcount = dcount + 1; + msg->dsize = sizeof(msg->hdr); + msg->data[0].iov_len = sizeof(msg->hdr); + msg->data[0].iov_base = &msg->hdr; + + for (loop=0; loopdsize += diov[loop].iov_len; + msg->data[loop+1].iov_len = diov[loop].iov_len; + msg->data[loop+1].iov_base = diov[loop].iov_base; + } + + __RXACCT(atomic_inc(&rxrpc_message_count)); + *_msg = msg; + _leave(" = 0 (%p) #%d",msg,atomic_read(&rxrpc_message_count)); + return 0; +} /* end rxrpc_conn_newmsg() */ + +/*****************************************************************************/ +/* + * free a message + */ +void __rxrpc_put_message(struct rxrpc_message *msg) +{ + int loop; + + _enter("%p #%d",msg,atomic_read(&rxrpc_message_count)); + + if (msg->pkt) kfree_skb(msg->pkt); + if (msg->conn) rxrpc_put_connection(msg->conn); + + for (loop=0; loop<8; loop++) + if (test_bit(loop,&msg->dfree)) + kfree(msg->data[loop].iov_base); + + __RXACCT(atomic_dec(&rxrpc_message_count)); + kfree(msg); + + _leave(""); +} /* end __rxrpc_put_message() */ + +/*****************************************************************************/ +/* + * send a message out through the transport endpoint + */ +int rxrpc_conn_sendmsg(struct rxrpc_connection *conn, struct rxrpc_message *msg) +{ + struct msghdr msghdr; + mm_segment_t oldfs; + int ret; + + _enter("%p{%d}",conn,ntohs(conn->addr.sin_port)); + + /* fill in some fields in the header */ + spin_lock(&conn->lock); + msg->hdr.serial = htonl(++conn->serial_counter); + msg->rttdone = 0; + spin_unlock(&conn->lock); + + /* set up the message to be transmitted */ + msghdr.msg_name = &conn->addr; + msghdr.msg_namelen = sizeof(conn->addr); + msghdr.msg_iov = msg->data; + msghdr.msg_iovlen = msg->dcount; + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + msghdr.msg_flags = MSG_CONFIRM|MSG_DONTWAIT; + + _net("Sending message type %d of %d bytes to %08x:%d", + msg->hdr.type, + msg->dsize, + htonl(conn->addr.sin_addr.s_addr), + htons(conn->addr.sin_port)); + + /* send the message */ + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = sock_sendmsg(conn->trans->socket,&msghdr,msg->dsize); + set_fs(oldfs); + + if (ret<0) { + msg->state = RXRPC_MSG_ERROR; + } + else { + msg->state = RXRPC_MSG_SENT; + ret = 0; + + spin_lock(&conn->lock); + msg->stamp = conn->atime = xtime; + spin_unlock(&conn->lock); + } + + _leave(" = %d",ret); + + return ret; +} /* end rxrpc_conn_sendmsg() */ + +/*****************************************************************************/ +/* + * deal with a subsequent call packet + */ +int rxrpc_conn_receive_call_packet(struct rxrpc_connection *conn, + struct rxrpc_call *call, + struct rxrpc_message *msg) +{ + struct rxrpc_message *pmsg; + struct list_head *_p; + unsigned cix, seq; + int ret = 0; + + _enter("%p,%p,%p",conn,call,msg); + + if (!call) { + cix = ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK; + + spin_lock(&conn->lock); + call = conn->channels[cix]; + + if (!call || call->call_id != msg->hdr.callNumber) { + spin_unlock(&conn->lock); + rxrpc_trans_immediate_abort(conn->trans,msg,-ENOENT); + goto out; + } + else { + rxrpc_get_call(call); + spin_unlock(&conn->lock); + } + } + else { + rxrpc_get_call(call); + } + + _proto("Received packet %%%u [%u] on call %hu:%u:%u", + htonl(msg->hdr.serial), + htonl(msg->hdr.seq), + htons(msg->hdr.serviceId), + htonl(conn->conn_id), + htonl(call->call_id)); + + call->pkt_rcv_count++; + + if (msg->pkt->dst && msg->pkt->dst->dev) + conn->peer->if_mtu = msg->pkt->dst->dev->mtu - msg->pkt->dst->dev->hard_header_len; + + /* queue on the call in seq order */ + rxrpc_get_message(msg); + seq = msg->seq; + + spin_lock(&call->lock); + list_for_each(_p,&call->rcv_receiveq) { + pmsg = list_entry(_p,struct rxrpc_message,link); + if (pmsg->seq>seq) + break; + } + list_add_tail(&msg->link,_p); + + /* reset the activity timeout */ + call->flags |= RXRPC_CALL_RCV_PKT; + mod_timer(&call->rcv_timeout,jiffies + rxrpc_call_rcv_timeout * HZ); + + spin_unlock(&call->lock); + + rxrpc_krxiod_queue_call(call); + + rxrpc_put_call(call); + out: + _leave(" = %d",ret); + + return ret; +} /* end rxrpc_conn_receive_call_packet() */ + +/*****************************************************************************/ +/* + * handle an ICMP error being applied to a connection + */ +void rxrpc_conn_handle_error(struct rxrpc_connection *conn, int local, int errno) +{ + struct rxrpc_call *calls[4]; + int loop; + + _enter("%p{%d},%d",conn,ntohs(conn->addr.sin_port),errno); + + /* get a ref to all my calls in one go */ + memset(calls,0,sizeof(calls)); + spin_lock(&conn->lock); + + for (loop=3; loop>=0; loop--) { + if (conn->channels[loop]) { + calls[loop] = conn->channels[loop]; + rxrpc_get_call(calls[loop]); + } + } + + spin_unlock(&conn->lock); + + /* now kick them all */ + for (loop=3; loop>=0; loop--) { + if (calls[loop]) { + rxrpc_call_handle_error(calls[loop],local,errno); + rxrpc_put_call(calls[loop]); + } + } + + _leave(""); +} /* end rxrpc_conn_handle_error() */ diff -urNp linux-5240/net/rxrpc/internal.h linux-5250/net/rxrpc/internal.h --- linux-5240/net/rxrpc/internal.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/internal.h @@ -0,0 +1,96 @@ +/* internal.h: internal Rx RPC stuff + * + * Copyright (c) 2002 David Howells (dhowells@redhat.com). + */ + +#ifndef RXRPC_INTERNAL_H +#define RXRPC_INTERNAL_H + +#include +#include + +/* + * debug accounting + */ +#if 1 +#define __RXACCT_DECL(X) X +#define __RXACCT(X) do { X; } while(0) +#else +#define __RXACCT_DECL(X) +#define __RXACCT(X) do { } while(0) +#endif + +__RXACCT_DECL(extern atomic_t rxrpc_transport_count); +__RXACCT_DECL(extern atomic_t rxrpc_peer_count); +__RXACCT_DECL(extern atomic_t rxrpc_connection_count); +__RXACCT_DECL(extern atomic_t rxrpc_call_count); +__RXACCT_DECL(extern atomic_t rxrpc_message_count); + +/* + * debug tracing + */ +#define kenter(FMT,...) printk("==> %s("FMT")\n",__FUNCTION__,##__VA_ARGS__) +#define kleave(FMT,...) printk("<== %s()"FMT"\n",__FUNCTION__,##__VA_ARGS__) +#define kdebug(FMT,...) printk(" "FMT"\n",##__VA_ARGS__) +#define kproto(FMT,...) printk("### "FMT"\n",##__VA_ARGS__) +#define knet(FMT,...) printk(" "FMT"\n",##__VA_ARGS__) + +#if 0 +#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__) +#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__) +#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__) +#define _proto(FMT,...) kproto(FMT,##__VA_ARGS__) +#define _net(FMT,...) knet(FMT,##__VA_ARGS__) +#else +#define _enter(FMT,...) do { if (rxrpc_ktrace) kenter(FMT,##__VA_ARGS__); } while(0) +#define _leave(FMT,...) do { if (rxrpc_ktrace) kleave(FMT,##__VA_ARGS__); } while(0) +#define _debug(FMT,...) do { if (rxrpc_kdebug) kdebug(FMT,##__VA_ARGS__); } while(0) +#define _proto(FMT,...) do { if (rxrpc_kproto) kproto(FMT,##__VA_ARGS__); } while(0) +#define _net(FMT,...) do { if (rxrpc_knet) knet (FMT,##__VA_ARGS__); } while(0) +#endif + +/* + * call.c + */ +extern struct list_head rxrpc_calls; +extern struct rw_semaphore rxrpc_calls_sem; + +/* + * connection.c + */ +extern struct list_head rxrpc_conns; +extern struct rw_semaphore rxrpc_conns_sem; + +extern void rxrpc_conn_do_timeout(struct rxrpc_connection *conn); +extern void rxrpc_conn_clearall(struct rxrpc_peer *peer); + +/* + * peer.c + */ +extern struct list_head rxrpc_peers; +extern struct rw_semaphore rxrpc_peers_sem; + +extern void rxrpc_peer_calculate_rtt(struct rxrpc_peer *peer, + struct rxrpc_message *msg, + struct rxrpc_message *resp); + +extern void rxrpc_peer_clearall(struct rxrpc_transport *trans); + +extern void rxrpc_peer_do_timeout(struct rxrpc_peer *peer); + + +/* + * proc.c + */ +#ifdef CONFIG_PROC_FS +extern int rxrpc_proc_init(void); +extern void rxrpc_proc_cleanup(void); +#endif + +/* + * transport.c + */ +extern struct list_head rxrpc_proc_transports; +extern struct rw_semaphore rxrpc_proc_transports_sem; + +#endif /* RXRPC_INTERNAL_H */ diff -urNp linux-5240/net/rxrpc/krxiod.c linux-5250/net/rxrpc/krxiod.c --- linux-5240/net/rxrpc/krxiod.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/krxiod.c @@ -0,0 +1,268 @@ +/* krxiod.c: Rx I/O daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxiod_sleepq); +static DECLARE_COMPLETION(rxrpc_krxiod_dead); + +static atomic_t rxrpc_krxiod_qcount = ATOMIC_INIT(0); + +static LIST_HEAD(rxrpc_krxiod_transportq); +static spinlock_t rxrpc_krxiod_transportq_lock = SPIN_LOCK_UNLOCKED; + +static LIST_HEAD(rxrpc_krxiod_callq); +static spinlock_t rxrpc_krxiod_callq_lock = SPIN_LOCK_UNLOCKED; + +static volatile int rxrpc_krxiod_die; + +/*****************************************************************************/ +/* + * Rx I/O daemon + */ +static int rxrpc_krxiod(void *arg) +{ + DECLARE_WAITQUEUE(krxiod,current); + + siginfo_t sinfo; + + printk("Started krxiod %d\n",current->pid); + strcpy(current->comm,"krxiod"); + + daemonize(); + + /* only certain signals are of interest */ + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked,0); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3) + recalc_sigpending(); +#else + recalc_sigpending(current); +#endif + spin_unlock_irq(¤t->sigmask_lock); + + /* loop around waiting for work to do */ + do { + /* wait for work or to be told to exit */ + _debug("### Begin Wait"); + if (!atomic_read(&rxrpc_krxiod_qcount)) { + set_current_state(TASK_INTERRUPTIBLE); + + add_wait_queue(&rxrpc_krxiod_sleepq,&krxiod); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (atomic_read(&rxrpc_krxiod_qcount) || + rxrpc_krxiod_die || + signal_pending(current)) + break; + + schedule(); + } + + remove_wait_queue(&rxrpc_krxiod_sleepq,&krxiod); + set_current_state(TASK_RUNNING); + } + _debug("### End Wait"); + + /* do work if been given some to do */ + _debug("### Begin Work"); + + /* see if there's a transport in need of attention */ + if (!list_empty(&rxrpc_krxiod_transportq)) { + struct rxrpc_transport *trans = NULL; + + spin_lock_irq(&rxrpc_krxiod_transportq_lock); + + if (!list_empty(&rxrpc_krxiod_transportq)) { + trans = list_entry(rxrpc_krxiod_transportq.next, + struct rxrpc_transport,krxiodq_link); + list_del_init(&trans->krxiodq_link); + atomic_dec(&rxrpc_krxiod_qcount); + + /* make sure it hasn't gone away and doesn't go away */ + if (atomic_read(&trans->usage)>0) + rxrpc_get_transport(trans); + else + trans = NULL; + } + + spin_unlock_irq(&rxrpc_krxiod_transportq_lock); + + if (trans) { + rxrpc_trans_receive_packet(trans); + rxrpc_put_transport(trans); + } + } + + /* see if there's a call in need of attention */ + if (!list_empty(&rxrpc_krxiod_callq)) { + struct rxrpc_call *call = NULL; + + spin_lock_irq(&rxrpc_krxiod_callq_lock); + + if (!list_empty(&rxrpc_krxiod_callq)) { + call = list_entry(rxrpc_krxiod_callq.next, + struct rxrpc_call,rcv_krxiodq_lk); + list_del_init(&call->rcv_krxiodq_lk); + atomic_dec(&rxrpc_krxiod_qcount); + + /* make sure it hasn't gone away and doesn't go away */ + if (atomic_read(&call->usage)>0) { + _debug("@@@ KRXIOD Begin Attend Call %p",call); + rxrpc_get_call(call); + } + else { + call = NULL; + } + } + + spin_unlock_irq(&rxrpc_krxiod_callq_lock); + + if (call) { + rxrpc_call_do_stuff(call); + rxrpc_put_call(call); + _debug("@@@ KRXIOD End Attend Call %p",call); + } + } + + _debug("### End Work"); + + /* discard pending signals */ + while (signal_pending(current)) { + spin_lock_irq(¤t->sigmask_lock); + dequeue_signal(¤t->blocked,&sinfo); + spin_unlock_irq(¤t->sigmask_lock); + } + + } while (!rxrpc_krxiod_die); + + /* and that's all */ + complete_and_exit(&rxrpc_krxiod_dead,0); + +} /* end rxrpc_krxiod() */ + +/*****************************************************************************/ +/* + * start up a krxiod daemon + */ +int __init rxrpc_krxiod_init(void) +{ + return kernel_thread(rxrpc_krxiod,NULL,0); + +} /* end rxrpc_krxiod_init() */ + +/*****************************************************************************/ +/* + * kill the krxiod daemon and wait for it to complete + */ +void rxrpc_krxiod_kill(void) +{ + rxrpc_krxiod_die = 1; + wake_up_all(&rxrpc_krxiod_sleepq); + wait_for_completion(&rxrpc_krxiod_dead); + +} /* end rxrpc_krxiod_kill() */ + +/*****************************************************************************/ +/* + * queue a transport for attention by krxiod + */ +void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans) +{ + unsigned long flags; + + _enter(""); + + if (list_empty(&trans->krxiodq_link)) { + spin_lock_irqsave(&rxrpc_krxiod_transportq_lock,flags); + + if (list_empty(&trans->krxiodq_link)) { + if (atomic_read(&trans->usage)>0) { + list_add_tail(&trans->krxiodq_link,&rxrpc_krxiod_transportq); + atomic_inc(&rxrpc_krxiod_qcount); + } + } + + spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock,flags); + wake_up_all(&rxrpc_krxiod_sleepq); + } + + _leave(""); + +} /* end rxrpc_krxiod_queue_transport() */ + +/*****************************************************************************/ +/* + * dequeue a transport from krxiod's attention queue + */ +void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans) +{ + unsigned long flags; + + _enter(""); + + spin_lock_irqsave(&rxrpc_krxiod_transportq_lock,flags); + if (!list_empty(&trans->krxiodq_link)) { + list_del_init(&trans->krxiodq_link); + atomic_dec(&rxrpc_krxiod_qcount); + } + spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock,flags); + + _leave(""); + +} /* end rxrpc_krxiod_dequeue_transport() */ + +/*****************************************************************************/ +/* + * queue a call for attention by krxiod + */ +void rxrpc_krxiod_queue_call(struct rxrpc_call *call) +{ + unsigned long flags; + + if (list_empty(&call->rcv_krxiodq_lk)) { + spin_lock_irqsave(&rxrpc_krxiod_callq_lock,flags); + if (atomic_read(&call->usage)>0) { + list_add_tail(&call->rcv_krxiodq_lk,&rxrpc_krxiod_callq); + atomic_inc(&rxrpc_krxiod_qcount); + } + spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock,flags); + } + wake_up_all(&rxrpc_krxiod_sleepq); + +} /* end rxrpc_krxiod_queue_call() */ + +/*****************************************************************************/ +/* + * dequeue a call from krxiod's attention queue + */ +void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call) +{ + unsigned long flags; + + spin_lock_irqsave(&rxrpc_krxiod_callq_lock,flags); + if (!list_empty(&call->rcv_krxiodq_lk)) { + list_del_init(&call->rcv_krxiodq_lk); + atomic_dec(&rxrpc_krxiod_qcount); + } + spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock,flags); + +} /* end rxrpc_krxiod_dequeue_call() */ diff -urNp linux-5240/net/rxrpc/krxsecd.c linux-5250/net/rxrpc/krxsecd.c --- linux-5240/net/rxrpc/krxsecd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/krxsecd.c @@ -0,0 +1,283 @@ +/* krxsecd.c: Rx security daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This daemon deals with: + * - consulting the application as to whether inbound peers and calls should be authorised + * - generating security challenges for inbound connections + * - responding to security challenges on outbound connections + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxsecd_sleepq); +static DECLARE_COMPLETION(rxrpc_krxsecd_dead); +static volatile int rxrpc_krxsecd_die; + +static atomic_t rxrpc_krxsecd_qcount; + +/* queue of unprocessed inbound messages with seqno #1 and RXRPC_CLIENT_INITIATED flag set */ +static LIST_HEAD(rxrpc_krxsecd_initmsgq); +static spinlock_t rxrpc_krxsecd_initmsgq_lock = SPIN_LOCK_UNLOCKED; + +static void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg); + +/*****************************************************************************/ +/* + * Rx security daemon + */ +static int rxrpc_krxsecd(void *arg) +{ + DECLARE_WAITQUEUE(krxsecd,current); + + siginfo_t sinfo; + int die; + + printk("Started krxsecd %d\n",current->pid); + strcpy(current->comm,"krxsecd"); + + daemonize(); + + /* only certain signals are of interest */ + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked,0); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3) + recalc_sigpending(); +#else + recalc_sigpending(current); +#endif + spin_unlock_irq(¤t->sigmask_lock); + + /* loop around waiting for work to do */ + do { + /* wait for work or to be told to exit */ + _debug("### Begin Wait"); + if (!atomic_read(&rxrpc_krxsecd_qcount)) { + set_current_state(TASK_INTERRUPTIBLE); + + add_wait_queue(&rxrpc_krxsecd_sleepq,&krxsecd); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (atomic_read(&rxrpc_krxsecd_qcount) || + rxrpc_krxsecd_die || + signal_pending(current)) + break; + + schedule(); + } + + remove_wait_queue(&rxrpc_krxsecd_sleepq,&krxsecd); + set_current_state(TASK_RUNNING); + } + die = rxrpc_krxsecd_die; + _debug("### End Wait"); + + /* see if there're incoming calls in need of authenticating */ + _debug("### Begin Inbound Calls"); + + if (!list_empty(&rxrpc_krxsecd_initmsgq)) { + struct rxrpc_message *msg = NULL; + + spin_lock(&rxrpc_krxsecd_initmsgq_lock); + + if (!list_empty(&rxrpc_krxsecd_initmsgq)) { + msg = list_entry(rxrpc_krxsecd_initmsgq.next, + struct rxrpc_message,link); + list_del_init(&msg->link); + atomic_dec(&rxrpc_krxsecd_qcount); + } + + spin_unlock(&rxrpc_krxsecd_initmsgq_lock); + + if (msg) { + rxrpc_krxsecd_process_incoming_call(msg); + rxrpc_put_message(msg); + } + } + + _debug("### End Inbound Calls"); + + /* discard pending signals */ + while (signal_pending(current)) { + spin_lock_irq(¤t->sigmask_lock); + dequeue_signal(¤t->blocked,&sinfo); + spin_unlock_irq(¤t->sigmask_lock); + } + + } while (!die); + + /* and that's all */ + complete_and_exit(&rxrpc_krxsecd_dead,0); + +} /* end rxrpc_krxsecd() */ + +/*****************************************************************************/ +/* + * start up a krxsecd daemon + */ +int __init rxrpc_krxsecd_init(void) +{ + return kernel_thread(rxrpc_krxsecd,NULL,0); + +} /* end rxrpc_krxsecd_init() */ + +/*****************************************************************************/ +/* + * kill the krxsecd daemon and wait for it to complete + */ +void rxrpc_krxsecd_kill(void) +{ + rxrpc_krxsecd_die = 1; + wake_up_all(&rxrpc_krxsecd_sleepq); + wait_for_completion(&rxrpc_krxsecd_dead); + +} /* end rxrpc_krxsecd_kill() */ + +/*****************************************************************************/ +/* + * clear all pending incoming calls for the specified transport + */ +void rxrpc_krxsecd_clear_transport(struct rxrpc_transport *trans) +{ + LIST_HEAD(tmp); + + struct rxrpc_message *msg; + struct list_head *_p, *_n; + + _enter("%p",trans); + + /* move all the messages for this transport onto a temp list */ + spin_lock(&rxrpc_krxsecd_initmsgq_lock); + + list_for_each_safe(_p,_n,&rxrpc_krxsecd_initmsgq) { + msg = list_entry(_p,struct rxrpc_message,link); + if (msg->trans==trans) { + list_del(&msg->link); + list_add_tail(&msg->link,&tmp); + atomic_dec(&rxrpc_krxsecd_qcount); + } + } + + spin_unlock(&rxrpc_krxsecd_initmsgq_lock); + + /* zap all messages on the temp list */ + while (!list_empty(&tmp)) { + msg = list_entry(tmp.next,struct rxrpc_message,link); + list_del_init(&msg->link); + rxrpc_put_message(msg); + } + + _leave(""); +} /* end rxrpc_krxsecd_clear_transport() */ + +/*****************************************************************************/ +/* + * queue a message on the incoming calls list + */ +void rxrpc_krxsecd_queue_incoming_call(struct rxrpc_message *msg) +{ + _enter("%p",msg); + + /* queue for processing by krxsecd */ + spin_lock(&rxrpc_krxsecd_initmsgq_lock); + + if (!rxrpc_krxsecd_die) { + rxrpc_get_message(msg); + list_add_tail(&msg->link,&rxrpc_krxsecd_initmsgq); + atomic_inc(&rxrpc_krxsecd_qcount); + } + + spin_unlock(&rxrpc_krxsecd_initmsgq_lock); + + wake_up(&rxrpc_krxsecd_sleepq); + + _leave(""); +} /* end rxrpc_krxsecd_queue_incoming_call() */ + +/*****************************************************************************/ +/* + * process the initial message of an incoming call + */ +void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg) +{ + struct rxrpc_transport *trans = msg->trans; + struct rxrpc_service *srv; + struct rxrpc_call *call; + struct list_head *_p; + unsigned short sid; + int ret; + + _enter("%p{tr=%p}",msg,trans); + + ret = rxrpc_incoming_call(msg->conn,msg,&call); + if (ret<0) + goto out; + + /* find the matching service on the transport */ + sid = ntohs(msg->hdr.serviceId); + srv = NULL; + + spin_lock(&trans->lock); + list_for_each(_p,&trans->services) { + srv = list_entry(_p,struct rxrpc_service,link); + if (srv->service_id==sid && try_inc_mod_count(srv->owner)) { + /* found a match (made sure it won't vanish) */ + _debug("found service '%s'",srv->name); + call->owner = srv->owner; + break; + } + } + spin_unlock(&trans->lock); + + /* report the new connection + * - the func must inc the call's usage count to keep it + */ + ret = -ENOENT; + if (_p!=&trans->services) { + /* attempt to accept the call */ + call->conn->service = srv; + call->app_attn_func = srv->attn_func; + call->app_error_func = srv->error_func; + call->app_aemap_func = srv->aemap_func; + + ret = srv->new_call(call); + + /* send an abort if an error occurred */ + if (ret<0) { + rxrpc_call_abort(call,ret); + } + else { + /* formally receive and ACK the new packet */ + ret = rxrpc_conn_receive_call_packet(call->conn,call,msg); + } + } + + rxrpc_put_call(call); + out: + if (ret<0) + rxrpc_trans_immediate_abort(trans,msg,ret); + + _leave(" (%d)",ret); +} /* end rxrpc_krxsecd_process_incoming_call() */ diff -urNp linux-5240/net/rxrpc/krxtimod.c linux-5250/net/rxrpc/krxtimod.c --- linux-5240/net/rxrpc/krxtimod.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/krxtimod.c @@ -0,0 +1,216 @@ +/* krxtimod.c: RXRPC timeout daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static DECLARE_COMPLETION(krxtimod_alive); +static DECLARE_COMPLETION(krxtimod_dead); +static DECLARE_WAIT_QUEUE_HEAD(krxtimod_sleepq); +static int krxtimod_die; + +static LIST_HEAD(krxtimod_list); +static spinlock_t krxtimod_lock = SPIN_LOCK_UNLOCKED; + +static int krxtimod(void *arg); + +/*****************************************************************************/ +/* + * start the timeout daemon + */ +int rxrpc_krxtimod_start(void) +{ + int ret; + + ret = kernel_thread(krxtimod,NULL,0); + if (ret<0) + return ret; + + wait_for_completion(&krxtimod_alive); + + return ret; +} /* end rxrpc_krxtimod_start() */ + +/*****************************************************************************/ +/* + * stop the timeout daemon + */ +void rxrpc_krxtimod_kill(void) +{ + /* get rid of my daemon */ + krxtimod_die = 1; + wake_up(&krxtimod_sleepq); + wait_for_completion(&krxtimod_dead); + +} /* end rxrpc_krxtimod_kill() */ + +/*****************************************************************************/ +/* + * timeout processing daemon + */ +static int krxtimod(void *arg) +{ + DECLARE_WAITQUEUE(myself,current); + + rxrpc_timer_t *timer; + + printk("Started krxtimod %d\n",current->pid); + strcpy(current->comm,"krxtimod"); + + daemonize(); + + complete(&krxtimod_alive); + + /* only certain signals are of interest */ + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked,0); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3) + recalc_sigpending(); +#else + recalc_sigpending(current); +#endif + spin_unlock_irq(¤t->sigmask_lock); + + /* loop around looking for things to attend to */ + loop: + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&krxtimod_sleepq,&myself); + + for (;;) { + unsigned long jif; + signed long timeout; + + /* deal with the server being asked to die */ + if (krxtimod_die) { + remove_wait_queue(&krxtimod_sleepq,&myself); + _leave(""); + complete_and_exit(&krxtimod_dead,0); + } + + /* discard pending signals */ + while (signal_pending(current)) { + siginfo_t sinfo; + + spin_lock_irq(¤t->sigmask_lock); + dequeue_signal(¤t->blocked,&sinfo); + spin_unlock_irq(¤t->sigmask_lock); + } + + /* work out the time to elapse before the next event */ + spin_lock(&krxtimod_lock); + if (list_empty(&krxtimod_list)) { + timeout = MAX_SCHEDULE_TIMEOUT; + } + else { + timer = list_entry(krxtimod_list.next,rxrpc_timer_t,link); + timeout = timer->timo_jif; + jif = jiffies; + + if (time_before_eq(timeout,jif)) + goto immediate; + + else { + timeout = (long)timeout - (long)jiffies; + } + } + spin_unlock(&krxtimod_lock); + + schedule_timeout(timeout); + + set_current_state(TASK_INTERRUPTIBLE); + } + + /* the thing on the front of the queue needs processing + * - we come here with the lock held and timer pointing to the expired entry + */ + immediate: + remove_wait_queue(&krxtimod_sleepq,&myself); + set_current_state(TASK_RUNNING); + + _debug("@@@ Begin Timeout of %p",timer); + + /* dequeue the timer */ + list_del_init(&timer->link); + spin_unlock(&krxtimod_lock); + + /* call the timeout function */ + timer->ops->timed_out(timer); + + _debug("@@@ End Timeout"); + goto loop; + +} /* end krxtimod() */ + +/*****************************************************************************/ +/* + * (re-)queue a timer + */ +void rxrpc_krxtimod_add_timer(rxrpc_timer_t *timer, unsigned long timeout) +{ + struct list_head *_p; + rxrpc_timer_t *ptimer; + + _enter("%p,%lu",timer,timeout); + + spin_lock(&krxtimod_lock); + + list_del(&timer->link); + + /* the timer was deferred or reset - put it back in the queue at the right place */ + timer->timo_jif = jiffies + timeout; + + list_for_each(_p,&krxtimod_list) { + ptimer = list_entry(_p,rxrpc_timer_t,link); + if (time_before(timer->timo_jif,ptimer->timo_jif)) + break; + } + + list_add_tail(&timer->link,_p); /* insert before stopping point */ + + spin_unlock(&krxtimod_lock); + + wake_up(&krxtimod_sleepq); + + _leave(""); +} /* end rxrpc_krxtimod_queue_vlocation() */ + +/*****************************************************************************/ +/* + * dequeue a timer + * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued + */ +int rxrpc_krxtimod_del_timer(rxrpc_timer_t *timer) +{ + int ret = 0; + + _enter("%p",timer); + + spin_lock(&krxtimod_lock); + + if (list_empty(&timer->link)) + ret = -ENOENT; + else + list_del_init(&timer->link); + + spin_unlock(&krxtimod_lock); + + wake_up(&krxtimod_sleepq); + + _leave(" = %d",ret); + return ret; +} /* end rxrpc_krxtimod_del_timer() */ diff -urNp linux-5240/net/rxrpc/main.c linux-5250/net/rxrpc/main.c --- linux-5240/net/rxrpc/main.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/main.c @@ -0,0 +1,127 @@ +/* main.c: Rx RPC interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static int rxrpc_initialise(void); +static void rxrpc_cleanup(void); + +module_init(rxrpc_initialise); +module_exit(rxrpc_cleanup); + +MODULE_DESCRIPTION("Rx RPC implementation"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +u32 rxrpc_epoch; + +/*****************************************************************************/ +/* + * initialise the Rx module + */ +static int rxrpc_initialise(void) +{ + int ret; + + /* my epoch value */ + rxrpc_epoch = htonl(xtime.tv_sec); + + /* register the /proc interface */ +#ifdef CONFIG_PROC_FS + ret = rxrpc_proc_init(); + if (ret<0) + return ret; +#endif + + /* register the sysctl files */ +#ifdef CONFIG_SYSCTL + ret = rxrpc_sysctl_init(); + if (ret<0) + goto error_proc; +#endif + + /* start the krxtimod daemon */ + ret = rxrpc_krxtimod_start(); + if (ret<0) + goto error_sysctl; + + /* start the krxiod daemon */ + ret = rxrpc_krxiod_init(); + if (ret<0) + goto error_krxtimod; + + /* start the krxsecd daemon */ + ret = rxrpc_krxsecd_init(); + if (ret<0) + goto error_krxiod; + + kdebug("\n\n"); + + return 0; + + error_krxiod: + rxrpc_krxiod_kill(); + error_krxtimod: + rxrpc_krxtimod_kill(); + error_sysctl: +#ifdef CONFIG_SYSCTL + rxrpc_sysctl_cleanup(); +#endif + error_proc: +#ifdef CONFIG_PROC_FS + rxrpc_proc_cleanup(); +#endif + return ret; +} /* end rxrpc_initialise() */ + +/*****************************************************************************/ +/* + * clean up the Rx module + */ +static void rxrpc_cleanup(void) +{ + kenter(""); + + __RXACCT(printk("Outstanding Messages : %d\n",atomic_read(&rxrpc_message_count))); + __RXACCT(printk("Outstanding Calls : %d\n",atomic_read(&rxrpc_call_count))); + __RXACCT(printk("Outstanding Connections: %d\n",atomic_read(&rxrpc_connection_count))); + __RXACCT(printk("Outstanding Peers : %d\n",atomic_read(&rxrpc_peer_count))); + __RXACCT(printk("Outstanding Transports : %d\n",atomic_read(&rxrpc_transport_count))); + + rxrpc_krxsecd_kill(); + rxrpc_krxiod_kill(); + rxrpc_krxtimod_kill(); +#ifdef CONFIG_SYSCTL + rxrpc_sysctl_cleanup(); +#endif +#ifdef CONFIG_PROC_FS + rxrpc_proc_cleanup(); +#endif + + __RXACCT(printk("Outstanding Messages : %d\n",atomic_read(&rxrpc_message_count))); + __RXACCT(printk("Outstanding Calls : %d\n",atomic_read(&rxrpc_call_count))); + __RXACCT(printk("Outstanding Connections: %d\n",atomic_read(&rxrpc_connection_count))); + __RXACCT(printk("Outstanding Peers : %d\n",atomic_read(&rxrpc_peer_count))); + __RXACCT(printk("Outstanding Transports : %d\n",atomic_read(&rxrpc_transport_count))); + + kleave(); +} /* end rxrpc_cleanup() */ diff -urNp linux-5240/net/rxrpc/Makefile linux-5250/net/rxrpc/Makefile --- linux-5240/net/rxrpc/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/Makefile @@ -0,0 +1,31 @@ +# +# Makefile for Linux kernel Rx RPC +# + +export-objs := rxrpc_syms.o + +rxrpc-objs := \ + call.o \ + connection.o \ + krxiod.o \ + krxsecd.o \ + krxtimod.o \ + main.o \ + peer.o \ + rxrpc_syms.o \ + transport.o + +#ifeq ($(CONFIG_PROC_FS),y) +rxrpc-objs += proc.o +#endif +#ifeq ($(CONFIG_SYSCTL),y) +rxrpc-objs += sysctl.o +#endif + +obj-m := rxrpc.o + +# superfluous for 2.5, but needed for 2.4.. +rxrpc.o: $(rxrpc-objs) + $(LD) -r -o $@ $(rxrpc-objs) + +include $(TOPDIR)/Rules.make diff -urNp linux-5240/net/rxrpc/peer.c linux-5250/net/rxrpc/peer.c --- linux-5240/net/rxrpc/peer.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/peer.c @@ -0,0 +1,380 @@ +/* peer.c: Rx RPC peer management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +__RXACCT_DECL(atomic_t rxrpc_peer_count); +LIST_HEAD(rxrpc_peers); +DECLARE_RWSEM(rxrpc_peers_sem); + +static void __rxrpc_peer_timeout(rxrpc_timer_t *timer) +{ + struct rxrpc_peer *peer = list_entry(timer,struct rxrpc_peer,timeout); + + _debug("Rx PEER TIMEOUT [%p{u=%d}]",peer,atomic_read(&peer->usage)); + + rxrpc_peer_do_timeout(peer); +} + +static const struct rxrpc_timer_ops rxrpc_peer_timer_ops = { + timed_out: __rxrpc_peer_timeout, +}; + +/*****************************************************************************/ +/* + * create a peer record + */ +static int __rxrpc_create_peer(struct rxrpc_transport *trans, u32 addr, struct rxrpc_peer **_peer) +{ + struct rxrpc_peer *peer; + + _enter("%p,%08x",trans,ntohl(addr)); + + /* allocate and initialise a peer record */ + peer = kmalloc(sizeof(struct rxrpc_peer),GFP_KERNEL); + if (!peer) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(peer,0,sizeof(struct rxrpc_peer)); + atomic_set(&peer->usage,1); + + INIT_LIST_HEAD(&peer->link); + INIT_LIST_HEAD(&peer->proc_link); + INIT_LIST_HEAD(&peer->conn_active); + INIT_LIST_HEAD(&peer->conn_graveyard); + spin_lock_init(&peer->conn_gylock); + init_waitqueue_head(&peer->conn_gy_waitq); + rwlock_init(&peer->conn_lock); + atomic_set(&peer->conn_count,0); + spin_lock_init(&peer->lock); + rxrpc_timer_init(&peer->timeout,&rxrpc_peer_timer_ops); + + peer->addr.s_addr = addr; + + peer->trans = trans; + peer->ops = trans->peer_ops; + + __RXACCT(atomic_inc(&rxrpc_peer_count)); + *_peer = peer; + _leave(" = 0 (%p)",peer); + + return 0; +} /* end __rxrpc_create_peer() */ + +/*****************************************************************************/ +/* + * find a peer record on the specified transport + * - returns (if successful) with peer record usage incremented + * - resurrects it from the graveyard if found there + */ +int rxrpc_peer_lookup(struct rxrpc_transport *trans, u32 addr, struct rxrpc_peer **_peer) +{ + struct rxrpc_peer *peer, *candidate = NULL; + struct list_head *_p; + int ret; + + _enter("%p{%hu},%08x",trans,trans->port,ntohl(addr)); + + /* [common case] search the transport's active list first */ + read_lock(&trans->peer_lock); + list_for_each(_p,&trans->peer_active) { + peer = list_entry(_p,struct rxrpc_peer,link); + if (peer->addr.s_addr==addr) + goto found_active; + } + read_unlock(&trans->peer_lock); + + /* [uncommon case] not active - create a candidate for a new record */ + ret = __rxrpc_create_peer(trans,addr,&candidate); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + /* search the active list again, just in case it appeared whilst we were busy */ + write_lock(&trans->peer_lock); + list_for_each(_p,&trans->peer_active) { + peer = list_entry(_p,struct rxrpc_peer,link); + if (peer->addr.s_addr==addr) + goto found_active_second_chance; + } + + /* search the transport's graveyard list */ + spin_lock(&trans->peer_gylock); + list_for_each(_p,&trans->peer_graveyard) { + peer = list_entry(_p,struct rxrpc_peer,link); + if (peer->addr.s_addr==addr) + goto found_in_graveyard; + } + spin_unlock(&trans->peer_gylock); + + /* we can now add the new candidate to the list + * - tell the application layer that this peer has been added + */ + rxrpc_get_transport(trans); + peer = candidate; + candidate = NULL; + + if (peer->ops && peer->ops->adding) { + ret = peer->ops->adding(peer); + if (ret<0) { + write_unlock(&trans->peer_lock); + __RXACCT(atomic_dec(&rxrpc_peer_count)); + kfree(peer); + rxrpc_put_transport(trans); + _leave(" = %d",ret); + return ret; + } + } + + atomic_inc(&trans->peer_count); + + make_active: + list_add_tail(&peer->link,&trans->peer_active); + + success_uwfree: + write_unlock(&trans->peer_lock); + + if (candidate) { + __RXACCT(atomic_dec(&rxrpc_peer_count)); + kfree(candidate); + } + + if (list_empty(&peer->proc_link)) { + down_write(&rxrpc_peers_sem); + list_add_tail(&peer->proc_link,&rxrpc_peers); + up_write(&rxrpc_peers_sem); + } + + success: + *_peer = peer; + + _leave(" = 0 (%p{u=%d cc=%d})", + peer,atomic_read(&peer->usage),atomic_read(&peer->conn_count)); + return 0; + + /* handle the peer being found in the active list straight off */ + found_active: + rxrpc_get_peer(peer); + read_unlock(&trans->peer_lock); + goto success; + + /* handle resurrecting a peer from the graveyard */ + found_in_graveyard: + rxrpc_get_peer(peer); + rxrpc_get_transport(peer->trans); + rxrpc_krxtimod_del_timer(&peer->timeout); + list_del_init(&peer->link); + spin_unlock(&trans->peer_gylock); + goto make_active; + + /* handle finding the peer on the second time through the active list */ + found_active_second_chance: + rxrpc_get_peer(peer); + goto success_uwfree; + +} /* end rxrpc_peer_lookup() */ + +/*****************************************************************************/ +/* + * finish with a peer record + * - it gets sent to the graveyard from where it can be resurrected or timed out + */ +void rxrpc_put_peer(struct rxrpc_peer *peer) +{ + struct rxrpc_transport *trans = peer->trans; + + _enter("%p{cc=%d a=%08x}",peer,atomic_read(&peer->conn_count),ntohl(peer->addr.s_addr)); + + /* sanity check */ + if (atomic_read(&peer->usage)<=0) + BUG(); + + write_lock(&trans->peer_lock); + spin_lock(&trans->peer_gylock); + if (likely(!atomic_dec_and_test(&peer->usage))) { + spin_unlock(&trans->peer_gylock); + write_unlock(&trans->peer_lock); + _leave(""); + return; + } + + /* move to graveyard queue */ + list_del(&peer->link); + write_unlock(&trans->peer_lock); + + list_add_tail(&peer->link,&trans->peer_graveyard); + + if (!list_empty(&peer->conn_active)) BUG(); + + /* discard in 600 secs */ + rxrpc_krxtimod_add_timer(&peer->timeout,100*HZ); + + spin_unlock(&trans->peer_gylock); + + rxrpc_put_transport(trans); + + _leave(" [killed]"); +} /* end rxrpc_put_peer() */ + +/*****************************************************************************/ +/* + * handle a peer timing out in the graveyard + * - called from krxtimod + */ +void rxrpc_peer_do_timeout(struct rxrpc_peer *peer) +{ + struct rxrpc_transport *trans = peer->trans; + + _enter("%p{u=%d cc=%d a=%08x}", + peer,atomic_read(&peer->usage),atomic_read(&peer->conn_count), + ntohl(peer->addr.s_addr)); + + if (atomic_read(&peer->usage)<0) + BUG(); + + /* remove from graveyard if still dead */ + spin_lock(&trans->peer_gylock); + if (atomic_read(&peer->usage)==0) + list_del_init(&peer->link); + else + peer = NULL; + spin_unlock(&trans->peer_gylock); + + if (!peer) { + _leave(""); + return; /* resurrected */ + } + + /* clear all connections on this peer */ + rxrpc_conn_clearall(peer); + + if (!list_empty(&peer->conn_active)) BUG(); + if (!list_empty(&peer->conn_graveyard)) BUG(); + + /* inform the application layer */ + if (peer->ops && peer->ops->discarding) + peer->ops->discarding(peer); + + if (!list_empty(&peer->proc_link)) { + down_write(&rxrpc_peers_sem); + list_del(&peer->proc_link); + up_write(&rxrpc_peers_sem); + } + + __RXACCT(atomic_dec(&rxrpc_peer_count)); + kfree(peer); + + /* if the graveyard is now empty, wake up anyone waiting for that */ + if (atomic_dec_and_test(&trans->peer_count)) + wake_up(&trans->peer_gy_waitq); + + _leave(" [destroyed]"); +} /* end rxrpc_peer_do_timeout() */ + +/*****************************************************************************/ +/* + * clear all peer records from a transport endpoint + */ +void rxrpc_peer_clearall(struct rxrpc_transport *trans) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_peer *peer; + int err; + + _enter("%p",trans); + + /* there shouldn't be any active peers remaining */ + if (!list_empty(&trans->peer_active)) + BUG(); + + /* manually timeout all peers in the graveyard */ + spin_lock(&trans->peer_gylock); + while (!list_empty(&trans->peer_graveyard)) { + peer = list_entry(trans->peer_graveyard.next,struct rxrpc_peer,link); + _debug("Clearing peer %p\n",peer); + err = rxrpc_krxtimod_del_timer(&peer->timeout); + spin_unlock(&trans->peer_gylock); + + if (err==0) + rxrpc_peer_do_timeout(peer); + + spin_lock(&trans->peer_gylock); + } + spin_unlock(&trans->peer_gylock); + + /* wait for the the peer graveyard to be completely cleared */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&trans->peer_gy_waitq,&myself); + + while (atomic_read(&trans->peer_count)!=0) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } + + remove_wait_queue(&trans->peer_gy_waitq,&myself); + set_current_state(TASK_RUNNING); + + _leave(""); + +} /* end rxrpc_peer_clearall() */ + +/*****************************************************************************/ +/* + * calculate and cache the Round-Trip-Time for a message and its response + */ +void rxrpc_peer_calculate_rtt(struct rxrpc_peer *peer, + struct rxrpc_message *msg, + struct rxrpc_message *resp) +{ + unsigned long long rtt; + int loop; + + _enter("%p,%p,%p",peer,msg,resp); + + /* calculate the latest RTT */ + rtt = resp->stamp.tv_sec - msg->stamp.tv_sec; + rtt *= 1000000UL; + rtt += resp->stamp.tv_usec - msg->stamp.tv_usec; + + /* add to cache */ + peer->rtt_cache[peer->rtt_point] = rtt; + peer->rtt_point++; + peer->rtt_point %= RXRPC_RTT_CACHE_SIZE; + + if (peer->rtt_usagertt_usage++; + + /* recalculate RTT */ + for (loop=peer->rtt_usage-1; loop>=0; loop--) + rtt += peer->rtt_cache[loop]; + + peer->rtt = do_div(rtt,peer->rtt_usage); + + _leave(" RTT=%lu.%lums",peer->rtt/1000,peer->rtt%1000); + +} /* end rxrpc_peer_calculate_rtt() */ diff -urNp linux-5240/net/rxrpc/proc.c linux-5250/net/rxrpc/proc.c --- linux-5240/net/rxrpc/proc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/proc.c @@ -0,0 +1,612 @@ +/* proc.c: /proc interface for RxRPC + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +static inline struct proc_dir_entry *PDE(const struct inode *inode) +{ + return (struct proc_dir_entry *)inode->u.generic_ip; +} +#endif + +static struct proc_dir_entry *proc_rxrpc; + +static int rxrpc_proc_transports_open(struct inode *inode, struct file *file); +static void *rxrpc_proc_transports_start(struct seq_file *p, loff_t *pos); +static void *rxrpc_proc_transports_next(struct seq_file *p, void *v, loff_t *pos); +static void rxrpc_proc_transports_stop(struct seq_file *p, void *v); +static int rxrpc_proc_transports_show(struct seq_file *m, void *v); + +static struct seq_operations rxrpc_proc_transports_ops = { + start: rxrpc_proc_transports_start, + next: rxrpc_proc_transports_next, + stop: rxrpc_proc_transports_stop, + show: rxrpc_proc_transports_show, +}; + +static struct file_operations rxrpc_proc_transports_fops = { + open: rxrpc_proc_transports_open, + read: seq_read, + llseek: seq_lseek, + release: seq_release, +}; + +static int rxrpc_proc_peers_open(struct inode *inode, struct file *file); +static void *rxrpc_proc_peers_start(struct seq_file *p, loff_t *pos); +static void *rxrpc_proc_peers_next(struct seq_file *p, void *v, loff_t *pos); +static void rxrpc_proc_peers_stop(struct seq_file *p, void *v); +static int rxrpc_proc_peers_show(struct seq_file *m, void *v); + +static struct seq_operations rxrpc_proc_peers_ops = { + start: rxrpc_proc_peers_start, + next: rxrpc_proc_peers_next, + stop: rxrpc_proc_peers_stop, + show: rxrpc_proc_peers_show, +}; + +static struct file_operations rxrpc_proc_peers_fops = { + open: rxrpc_proc_peers_open, + read: seq_read, + llseek: seq_lseek, + release: seq_release, +}; + +static int rxrpc_proc_conns_open(struct inode *inode, struct file *file); +static void *rxrpc_proc_conns_start(struct seq_file *p, loff_t *pos); +static void *rxrpc_proc_conns_next(struct seq_file *p, void *v, loff_t *pos); +static void rxrpc_proc_conns_stop(struct seq_file *p, void *v); +static int rxrpc_proc_conns_show(struct seq_file *m, void *v); + +static struct seq_operations rxrpc_proc_conns_ops = { + start: rxrpc_proc_conns_start, + next: rxrpc_proc_conns_next, + stop: rxrpc_proc_conns_stop, + show: rxrpc_proc_conns_show, +}; + +static struct file_operations rxrpc_proc_conns_fops = { + open: rxrpc_proc_conns_open, + read: seq_read, + llseek: seq_lseek, + release: seq_release, +}; + +static int rxrpc_proc_calls_open(struct inode *inode, struct file *file); +static void *rxrpc_proc_calls_start(struct seq_file *p, loff_t *pos); +static void *rxrpc_proc_calls_next(struct seq_file *p, void *v, loff_t *pos); +static void rxrpc_proc_calls_stop(struct seq_file *p, void *v); +static int rxrpc_proc_calls_show(struct seq_file *m, void *v); + +static struct seq_operations rxrpc_proc_calls_ops = { + start: rxrpc_proc_calls_start, + next: rxrpc_proc_calls_next, + stop: rxrpc_proc_calls_stop, + show: rxrpc_proc_calls_show, +}; + +static struct file_operations rxrpc_proc_calls_fops = { + open: rxrpc_proc_calls_open, + read: seq_read, + llseek: seq_lseek, + release: seq_release, +}; + +static const char *rxrpc_call_states7[] = { + "complet", + "error ", + "rcv_op ", + "rcv_arg", + "got_arg", + "snd_rpl", + "fin_ack", + "snd_arg", + "rcv_rpl", + "got_rpl" +}; + +static const char *rxrpc_call_error_states7[] = { + "no_err ", + "loc_abt", + "rmt_abt", + "loc_err", + "rmt_err" +}; + +/*****************************************************************************/ +/* + * initialise the /proc/net/rxrpc/ directory + */ +int rxrpc_proc_init(void) +{ + struct proc_dir_entry *p; + + proc_rxrpc = proc_mkdir("rxrpc",proc_net); + if (!proc_rxrpc) + goto error; + proc_rxrpc->owner = THIS_MODULE; + + p = create_proc_entry("calls",0,proc_rxrpc); + if (!p) + goto error_proc; + p->proc_fops = &rxrpc_proc_calls_fops; + p->owner = THIS_MODULE; + + p = create_proc_entry("connections",0,proc_rxrpc); + if (!p) + goto error_calls; + p->proc_fops = &rxrpc_proc_conns_fops; + p->owner = THIS_MODULE; + + p = create_proc_entry("peers",0,proc_rxrpc); + if (!p) + goto error_calls; + p->proc_fops = &rxrpc_proc_peers_fops; + p->owner = THIS_MODULE; + + p = create_proc_entry("transports",0,proc_rxrpc); + if (!p) + goto error_conns; + p->proc_fops = &rxrpc_proc_transports_fops; + p->owner = THIS_MODULE; + + return 0; + + error_conns: + remove_proc_entry("conns",proc_rxrpc); + error_calls: + remove_proc_entry("calls",proc_rxrpc); + error_proc: + remove_proc_entry("rxrpc",proc_net); + error: + return -ENOMEM; +} /* end rxrpc_proc_init() */ + +/*****************************************************************************/ +/* + * clean up the /proc/net/rxrpc/ directory + */ +void rxrpc_proc_cleanup(void) +{ + remove_proc_entry("transports",proc_rxrpc); + remove_proc_entry("peers",proc_rxrpc); + remove_proc_entry("connections",proc_rxrpc); + remove_proc_entry("calls",proc_rxrpc); + + remove_proc_entry("rxrpc",proc_net); + +} /* end rxrpc_proc_cleanup() */ + +/*****************************************************************************/ +/* + * open "/proc/net/rxrpc/transports" which provides a summary of extant transports + */ +static int rxrpc_proc_transports_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file,&rxrpc_proc_transports_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = PDE(inode)->data; + + return 0; +} /* end rxrpc_proc_transports_open() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the transports list and return the first item + */ +static void *rxrpc_proc_transports_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + /* lock the list against modification */ + down_read(&rxrpc_proc_transports_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&rxrpc_proc_transports) + if (!pos--) + break; + + return _p!=&rxrpc_proc_transports ? _p : NULL; +} /* end rxrpc_proc_transports_start() */ + +/*****************************************************************************/ +/* + * move to next call in transports list + */ +static void *rxrpc_proc_transports_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = v==(void*)1 ? rxrpc_proc_transports.next : _p->next; + + return _p!=&rxrpc_proc_transports ? _p : NULL; +} /* end rxrpc_proc_transports_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the transports list + */ +static void rxrpc_proc_transports_stop(struct seq_file *p, void *v) +{ + up_read(&rxrpc_proc_transports_sem); + +} /* end rxrpc_proc_transports_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of call lines + */ +static int rxrpc_proc_transports_show(struct seq_file *m, void *v) +{ + struct rxrpc_transport *trans = list_entry(v,struct rxrpc_transport,proc_link); + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m, "LOCAL USE\n"); + return 0; + } + + /* display one transport per line on subsequent lines */ + seq_printf(m,"%5hu %3d\n", + trans->port, + atomic_read(&trans->usage) + ); + + return 0; +} /* end rxrpc_proc_transports_show() */ + +/*****************************************************************************/ +/* + * open "/proc/net/rxrpc/peers" which provides a summary of extant peers + */ +static int rxrpc_proc_peers_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file,&rxrpc_proc_peers_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = PDE(inode)->data; + + return 0; +} /* end rxrpc_proc_peers_open() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the peers list and return the first item + */ +static void *rxrpc_proc_peers_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + /* lock the list against modification */ + down_read(&rxrpc_peers_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&rxrpc_peers) + if (!pos--) + break; + + return _p!=&rxrpc_peers ? _p : NULL; +} /* end rxrpc_proc_peers_start() */ + +/*****************************************************************************/ +/* + * move to next conn in peers list + */ +static void *rxrpc_proc_peers_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = v==(void*)1 ? rxrpc_peers.next : _p->next; + + return _p!=&rxrpc_peers ? _p : NULL; +} /* end rxrpc_proc_peers_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the peers list + */ +static void rxrpc_proc_peers_stop(struct seq_file *p, void *v) +{ + up_read(&rxrpc_peers_sem); + +} /* end rxrpc_proc_peers_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of conn lines + */ +static int rxrpc_proc_peers_show(struct seq_file *m, void *v) +{ + struct rxrpc_peer *peer = list_entry(v,struct rxrpc_peer,proc_link); + signed long timeout; + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m,"LOCAL REMOTE USAGE CONNS TIMEOUT MTU RTT(uS)\n"); + return 0; + } + + /* display one peer per line on subsequent lines */ + timeout = 0; + if (!list_empty(&peer->timeout.link)) + timeout = (signed long)peer->timeout.timo_jif - (signed long)jiffies; + + seq_printf(m,"%5hu %08x %5d %5d %8ld %5u %7lu\n", + peer->trans->port, + ntohl(peer->addr.s_addr), + atomic_read(&peer->usage), + atomic_read(&peer->conn_count), + timeout, + peer->if_mtu, + peer->rtt + ); + + return 0; +} /* end rxrpc_proc_peers_show() */ + +/*****************************************************************************/ +/* + * open "/proc/net/rxrpc/connections" which provides a summary of extant connections + */ +static int rxrpc_proc_conns_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file,&rxrpc_proc_conns_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = PDE(inode)->data; + + return 0; +} /* end rxrpc_proc_conns_open() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the conns list and return the first item + */ +static void *rxrpc_proc_conns_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + /* lock the list against modification */ + down_read(&rxrpc_conns_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&rxrpc_conns) + if (!pos--) + break; + + return _p!=&rxrpc_conns ? _p : NULL; +} /* end rxrpc_proc_conns_start() */ + +/*****************************************************************************/ +/* + * move to next conn in conns list + */ +static void *rxrpc_proc_conns_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = v==(void*)1 ? rxrpc_conns.next : _p->next; + + return _p!=&rxrpc_conns ? _p : NULL; +} /* end rxrpc_proc_conns_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the conns list + */ +static void rxrpc_proc_conns_stop(struct seq_file *p, void *v) +{ + up_read(&rxrpc_conns_sem); + +} /* end rxrpc_proc_conns_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of conn lines + */ +static int rxrpc_proc_conns_show(struct seq_file *m, void *v) +{ + struct rxrpc_connection *conn = list_entry(v,struct rxrpc_connection,proc_link); + signed long timeout; + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m, + "LOCAL REMOTE RPORT SRVC CONN END SERIALNO CALLNO MTU TIMEOUT" + "\n"); + return 0; + } + + /* display one conn per line on subsequent lines */ + timeout = 0; + if (!list_empty(&conn->timeout.link)) + timeout = (signed long)conn->timeout.timo_jif - (signed long)jiffies; + + seq_printf(m,"%5hu %08x %5hu %04hx %08x %-3.3s %08x %08x %5u %8ld\n", + conn->trans->port, + ntohl(conn->addr.sin_addr.s_addr), + ntohs(conn->addr.sin_port), + ntohs(conn->service_id), + ntohl(conn->conn_id), + conn->out_clientflag ? "CLT" : "SRV", + conn->serial_counter, + conn->call_counter, + conn->mtu_size, + timeout + ); + + return 0; +} /* end rxrpc_proc_conns_show() */ + +/*****************************************************************************/ +/* + * open "/proc/net/rxrpc/calls" which provides a summary of extant calls + */ +static int rxrpc_proc_calls_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file,&rxrpc_proc_calls_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = PDE(inode)->data; + + return 0; +} /* end rxrpc_proc_calls_open() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the calls list and return the first item + */ +static void *rxrpc_proc_calls_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + /* lock the list against modification */ + down_read(&rxrpc_calls_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&rxrpc_calls) + if (!pos--) + break; + + return _p!=&rxrpc_calls ? _p : NULL; +} /* end rxrpc_proc_calls_start() */ + +/*****************************************************************************/ +/* + * move to next call in calls list + */ +static void *rxrpc_proc_calls_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = v==(void*)1 ? rxrpc_calls.next : _p->next; + + return _p!=&rxrpc_calls ? _p : NULL; +} /* end rxrpc_proc_calls_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the calls list + */ +static void rxrpc_proc_calls_stop(struct seq_file *p, void *v) +{ + up_read(&rxrpc_calls_sem); + +} /* end rxrpc_proc_calls_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of call lines + */ +static int rxrpc_proc_calls_show(struct seq_file *m, void *v) +{ + struct rxrpc_call *call = list_entry(v,struct rxrpc_call,call_link); + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m, + "LOCAL REMOT SRVC CONN CALL DIR USE " + " L STATE OPCODE ABORT ERRNO\n" + ); + return 0; + } + + /* display one call per line on subsequent lines */ + seq_printf(m, + "%5hu %5hu %04hx %08x %08x %s %3u%c" + " %c %-7.7s %6d %08x %5d\n", + call->conn->trans->port, + ntohs(call->conn->addr.sin_port), + ntohs(call->conn->service_id), + ntohl(call->conn->conn_id), + ntohl(call->call_id), + call->conn->service ? "SVC" : "CLT", + atomic_read(&call->usage), + waitqueue_active(&call->waitq) ? 'w' : ' ', + call->app_last_rcv ? 'Y' : '-', + (call->app_call_state!=RXRPC_CSTATE_ERROR ? + rxrpc_call_states7[call->app_call_state] : + rxrpc_call_error_states7[call->app_err_state]), + call->app_opcode, + call->app_abort_code, + call->app_errno + ); + + return 0; +} /* end rxrpc_proc_calls_show() */ diff -urNp linux-5240/net/rxrpc/rxrpc_syms.c linux-5250/net/rxrpc/rxrpc_syms.c --- linux-5240/net/rxrpc/rxrpc_syms.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/rxrpc_syms.c @@ -0,0 +1,51 @@ +/* rxrpc_syms.c: exported Rx RPC layer interface symbols + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include + +/* call.c */ +EXPORT_SYMBOL(rxrpc_call_rcv_timeout); +EXPORT_SYMBOL(rxrpc_call_acks_timeout); +EXPORT_SYMBOL(rxrpc_call_dfr_ack_timeout); +EXPORT_SYMBOL(rxrpc_call_max_resend); +EXPORT_SYMBOL(rxrpc_call_states); +EXPORT_SYMBOL(rxrpc_call_error_states); + +EXPORT_SYMBOL(rxrpc_create_call); +EXPORT_SYMBOL(rxrpc_incoming_call); +EXPORT_SYMBOL(rxrpc_put_call); +EXPORT_SYMBOL(rxrpc_call_abort); +EXPORT_SYMBOL(rxrpc_call_read_data); +EXPORT_SYMBOL(rxrpc_call_write_data); +EXPORT_SYMBOL(rxrpc_call_flush); + +/* connection.c */ +EXPORT_SYMBOL(rxrpc_create_connection); +EXPORT_SYMBOL(rxrpc_put_connection); + +/* sysctl.c */ +EXPORT_SYMBOL(rxrpc_ktrace); +EXPORT_SYMBOL(rxrpc_kdebug); +EXPORT_SYMBOL(rxrpc_kproto); +EXPORT_SYMBOL(rxrpc_knet); + +/* transport.c */ +EXPORT_SYMBOL(rxrpc_create_transport); +EXPORT_SYMBOL(rxrpc_clear_transport); +EXPORT_SYMBOL(rxrpc_put_transport); +EXPORT_SYMBOL(rxrpc_add_service); +EXPORT_SYMBOL(rxrpc_del_service); diff -urNp linux-5240/net/rxrpc/sysctl.c linux-5250/net/rxrpc/sysctl.c --- linux-5240/net/rxrpc/sysctl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/sysctl.c @@ -0,0 +1,73 @@ +/* sysctl.c: Rx RPC control + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +int rxrpc_ktrace; +int rxrpc_kdebug; +int rxrpc_kproto; +int rxrpc_knet; + +#ifdef CONFIG_SYSCTL +static struct ctl_table_header *rxrpc_sysctl = NULL; + +static ctl_table rxrpc_sysctl_table[] = { + { 1, "kdebug", &rxrpc_kdebug, sizeof(int), 0644, NULL, &proc_dointvec }, + { 2, "ktrace", &rxrpc_ktrace, sizeof(int), 0644, NULL, &proc_dointvec }, + { 3, "kproto", &rxrpc_kproto, sizeof(int), 0644, NULL, &proc_dointvec }, + { 4, "knet", &rxrpc_knet, sizeof(int), 0644, NULL, &proc_dointvec }, + { 0 } +}; + +static ctl_table rxrpc_dir_sysctl_table[] = { + { 1, "rxrpc", NULL, 0, 0555, rxrpc_sysctl_table }, + { 0 } +}; +#endif /* CONFIG_SYSCTL */ + +/*****************************************************************************/ +/* + * initialise the sysctl stuff for Rx RPC + */ +int rxrpc_sysctl_init(void) +{ +#ifdef CONFIG_SYSCTL + rxrpc_sysctl = register_sysctl_table(rxrpc_dir_sysctl_table,0); + if (!rxrpc_sysctl) + return -ENOMEM; +#endif /* CONFIG_SYSCTL */ + + return 0; +} /* end rxrpc_sysctl_init() */ + +/*****************************************************************************/ +/* + * clean up the sysctl stuff for Rx RPC + */ +void rxrpc_sysctl_cleanup(void) +{ +#ifdef CONFIG_SYSCTL + if (rxrpc_sysctl) { + unregister_sysctl_table(rxrpc_sysctl); + rxrpc_sysctl = NULL; + } +#endif /* CONFIG_SYSCTL */ + +} /* end rxrpc_sysctl_cleanup() */ diff -urNp linux-5240/net/rxrpc/transport.c linux-5250/net/rxrpc/transport.c --- linux-5240/net/rxrpc/transport.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-5250/net/rxrpc/transport.c @@ -0,0 +1,824 @@ +/* transport.c: Rx Transport routines + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) +#include /* this should _really_ be in errqueue.h.. */ +#endif +#include +#include +#include +#include "internal.h" + +struct errormsg { + struct cmsghdr cmsg; /* control message header */ + struct sock_extended_err ee; /* extended error information */ + struct sockaddr_in icmp_src; /* ICMP packet source address */ +}; + +static spinlock_t rxrpc_transports_lock = SPIN_LOCK_UNLOCKED; +static struct list_head rxrpc_transports = LIST_HEAD_INIT(rxrpc_transports); + +__RXACCT_DECL(atomic_t rxrpc_transport_count); +LIST_HEAD(rxrpc_proc_transports); +DECLARE_RWSEM(rxrpc_proc_transports_sem); + +static void rxrpc_data_ready(struct sock *sk, int count); +static void rxrpc_error_report(struct sock *sk); +static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans, + struct list_head *msgq); +static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans); + +/*****************************************************************************/ +/* + * create a new transport endpoint using the specified UDP port + */ +int rxrpc_create_transport(unsigned short port, struct rxrpc_transport **_trans) +{ + struct rxrpc_transport *trans; + struct sockaddr_in sin; + mm_segment_t oldfs; + struct sock *sock; + int ret, opt; + + _enter("%hu",port); + + trans = kmalloc(sizeof(struct rxrpc_transport),GFP_KERNEL); + if (!trans) + return -ENOMEM; + + memset(trans,0,sizeof(struct rxrpc_transport)); + atomic_set(&trans->usage,1); + INIT_LIST_HEAD(&trans->services); + INIT_LIST_HEAD(&trans->link); + INIT_LIST_HEAD(&trans->krxiodq_link); + spin_lock_init(&trans->lock); + INIT_LIST_HEAD(&trans->peer_active); + INIT_LIST_HEAD(&trans->peer_graveyard); + spin_lock_init(&trans->peer_gylock); + init_waitqueue_head(&trans->peer_gy_waitq); + rwlock_init(&trans->peer_lock); + atomic_set(&trans->peer_count,0); + trans->port = port; + + /* create a UDP socket to be my actual transport endpoint */ + ret = sock_create(PF_INET,SOCK_DGRAM,IPPROTO_UDP,&trans->socket); + if (ret<0) + goto error; + + /* use the specified port */ + if (port) { + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + ret = trans->socket->ops->bind(trans->socket,(struct sockaddr *)&sin,sizeof(sin)); + if (ret<0) + goto error; + } + + opt = 1; + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = trans->socket->ops->setsockopt(trans->socket,SOL_IP,IP_RECVERR, + (char*)&opt,sizeof(opt)); + set_fs(oldfs); + + spin_lock(&rxrpc_transports_lock); + list_add(&trans->link,&rxrpc_transports); + spin_unlock(&rxrpc_transports_lock); + + /* set the socket up */ + sock = trans->socket->sk; + sock->user_data = trans; + sock->data_ready = rxrpc_data_ready; + sock->error_report = rxrpc_error_report; + + down_write(&rxrpc_proc_transports_sem); + list_add_tail(&trans->proc_link,&rxrpc_proc_transports); + up_write(&rxrpc_proc_transports_sem); + + __RXACCT(atomic_inc(&rxrpc_transport_count)); + + *_trans = trans; + _leave(" = 0 (%p)",trans); + return 0; + + error: + rxrpc_put_transport(trans); + + _leave(" = %d",ret); + + return ret; + +} /* end rxrpc_create_transport() */ + +/*****************************************************************************/ +/* + * clear the connections on a transport endpoint + */ +void rxrpc_clear_transport(struct rxrpc_transport *trans) +{ + //struct rxrpc_connection *conn; + +} /* end rxrpc_clear_transport() */ + +/*****************************************************************************/ +/* + * destroy a transport endpoint + */ +void rxrpc_put_transport(struct rxrpc_transport *trans) +{ + _enter("%p{u=%d p=%hu}",trans,atomic_read(&trans->usage),trans->port); + + if (atomic_read(&trans->usage)<=0) + BUG(); + + /* to prevent a race, the decrement and the dequeue must be effectively atomic */ + spin_lock(&rxrpc_transports_lock); + if (likely(!atomic_dec_and_test(&trans->usage))) { + spin_unlock(&rxrpc_transports_lock); + _leave(""); + return; + } + + list_del(&trans->link); + spin_unlock(&rxrpc_transports_lock); + + /* finish cleaning up the transport */ + if (trans->socket) + trans->socket->ops->shutdown(trans->socket,2); + + rxrpc_krxsecd_clear_transport(trans); + rxrpc_krxiod_dequeue_transport(trans); + + /* discard all peer information */ + rxrpc_peer_clearall(trans); + + down_write(&rxrpc_proc_transports_sem); + list_del(&trans->proc_link); + up_write(&rxrpc_proc_transports_sem); + __RXACCT(atomic_dec(&rxrpc_transport_count)); + + /* close the socket */ + if (trans->socket) { + trans->socket->sk->user_data = NULL; + sock_release(trans->socket); + trans->socket = NULL; + } + + kfree(trans); + + _leave(""); + +} /* end rxrpc_put_transport() */ + +/*****************************************************************************/ +/* + * add a service to a transport to be listened upon + */ +int rxrpc_add_service(struct rxrpc_transport *trans, struct rxrpc_service *newsrv) +{ + struct rxrpc_service *srv; + struct list_head *_p; + int ret = -EEXIST; + + _enter("%p{%hu},%p{%hu}",trans,trans->port,newsrv,newsrv->service_id); + + /* verify that the service ID is not already present */ + spin_lock(&trans->lock); + + list_for_each(_p,&trans->services) { + srv = list_entry(_p,struct rxrpc_service,link); + if (srv->service_id==newsrv->service_id) + goto out; + } + + /* okay - add the transport to the list */ + list_add_tail(&newsrv->link,&trans->services); + rxrpc_get_transport(trans); + ret = 0; + + out: + spin_unlock(&trans->lock); + + _leave("= %d",ret); + return ret; + +} /* end rxrpc_add_service() */ + +/*****************************************************************************/ +/* + * remove a service from a transport + */ +void rxrpc_del_service(struct rxrpc_transport *trans, struct rxrpc_service *srv) +{ + _enter("%p{%hu},%p{%hu}",trans,trans->port,srv,srv->service_id); + + spin_lock(&trans->lock); + list_del(&srv->link); + spin_unlock(&trans->lock); + + rxrpc_put_transport(trans); + + _leave(""); + +} /* end rxrpc_del_service() */ + +/*****************************************************************************/ +/* + * INET callback when data has been received on the socket. + */ +static void rxrpc_data_ready(struct sock *sk, int count) +{ + struct rxrpc_transport *trans; + + _enter("%p{t=%p},%d",sk,sk->user_data,count); + + /* queue the transport for attention by krxiod */ + trans = (struct rxrpc_transport *) sk->user_data; + if (trans) + rxrpc_krxiod_queue_transport(trans); + + /* wake up anyone waiting on the socket */ + if (sk->sleep && waitqueue_active(sk->sleep)) + wake_up_interruptible(sk->sleep); + + _leave(""); + +} /* end rxrpc_data_ready() */ + +/*****************************************************************************/ +/* + * INET callback when an ICMP error packet is received + * - sk->err is error (EHOSTUNREACH, EPROTO or EMSGSIZE) + */ +static void rxrpc_error_report(struct sock *sk) +{ + struct rxrpc_transport *trans; + + _enter("%p{t=%p}",sk,sk->user_data); + + /* queue the transport for attention by krxiod */ + trans = (struct rxrpc_transport *) sk->user_data; + if (trans) { + trans->error_rcvd = 1; + rxrpc_krxiod_queue_transport(trans); + } + + /* wake up anyone waiting on the socket */ + if (sk->sleep && waitqueue_active(sk->sleep)) + wake_up_interruptible(sk->sleep); + + _leave(""); + +} /* end rxrpc_error_report() */ + +/*****************************************************************************/ +/* + * split a message up, allocating message records and filling them in from the contents of a + * socket buffer + */ +static int rxrpc_incoming_msg(struct rxrpc_transport *trans, + struct sk_buff *pkt, + struct list_head *msgq) +{ + struct rxrpc_message *msg; + int ret; + + _enter(""); + + msg = kmalloc(sizeof(struct rxrpc_message),GFP_KERNEL); + if (!msg) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(msg,0,sizeof(*msg)); + atomic_set(&msg->usage,1); + list_add_tail(&msg->link,msgq); + + /* dig out the Rx routing parameters */ + if (skb_copy_bits(pkt,sizeof(struct udphdr),&msg->hdr,sizeof(msg->hdr))<0) { + ret = -EBADMSG; + goto error; + } + + msg->trans = trans; + msg->state = RXRPC_MSG_RECEIVED; + msg->stamp = pkt->stamp; + msg->seq = ntohl(msg->hdr.seq); + + /* attach the packet */ + skb_get(pkt); + msg->pkt = pkt; + + msg->offset = sizeof(struct udphdr) + sizeof(struct rxrpc_header); + msg->dsize = msg->pkt->len - msg->offset; + + _net("Rx Received packet from %s (%08x;%08x,%1x,%d,%s,%02x,%d,%d)", + msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server", + ntohl(msg->hdr.epoch), + (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT, + ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK, + ntohl(msg->hdr.callNumber), + rxrpc_pkts[msg->hdr.type], + msg->hdr.flags, + ntohs(msg->hdr.serviceId), + msg->hdr.securityIndex); + + __RXACCT(atomic_inc(&rxrpc_message_count)); + + /* split off jumbo packets */ + while (msg->hdr.type==RXRPC_PACKET_TYPE_DATA && msg->hdr.flags & RXRPC_JUMBO_PACKET) { + struct rxrpc_jumbo_header jumbo; + struct rxrpc_message *jumbomsg = msg; + + _debug("split jumbo packet"); + + /* quick sanity check */ + ret = -EBADMSG; + if (msg->dsize < RXRPC_JUMBO_DATALEN+sizeof(struct rxrpc_jumbo_header)) + goto error; + if (msg->hdr.flags & RXRPC_LAST_PACKET) + goto error; + + /* dig out the secondary header */ + if (skb_copy_bits(pkt,msg->offset+RXRPC_JUMBO_DATALEN,&jumbo,sizeof(jumbo))<0) + goto error; + + /* allocate a new message record */ + ret = -ENOMEM; + msg = kmalloc(sizeof(struct rxrpc_message),GFP_KERNEL); + if (!msg) + goto error; + + memcpy(msg,jumbomsg,sizeof(*msg)); + list_add_tail(&msg->link,msgq); + + /* adjust the jumbo packet */ + jumbomsg->dsize = RXRPC_JUMBO_DATALEN; + + /* attach the packet here too */ + skb_get(pkt); + + /* adjust the parameters */ + msg->seq++; + msg->hdr.seq = htonl(msg->seq); + msg->hdr.serial = htonl(ntohl(msg->hdr.serial) + 1); + msg->offset += RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header); + msg->dsize -= RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header); + msg->hdr.flags = jumbo.flags; + msg->hdr._rsvd = jumbo._rsvd; + + _net("Rx Split jumbo packet from %s (%08x;%08x,%1x,%d,%s,%02x,%d,%d)", + msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server", + ntohl(msg->hdr.epoch), + (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT, + ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK, + ntohl(msg->hdr.callNumber), + rxrpc_pkts[msg->hdr.type], + msg->hdr.flags, + ntohs(msg->hdr.serviceId), + msg->hdr.securityIndex); + + __RXACCT(atomic_inc(&rxrpc_message_count)); + } + + _leave(" = 0 #%d",atomic_read(&rxrpc_message_count)); + return 0; + + error: + while (!list_empty(msgq)) { + msg = list_entry(msgq->next,struct rxrpc_message,link); + list_del_init(&msg->link); + + rxrpc_put_message(msg); + } + + _leave(" = %d",ret); + return ret; +} /* end rxrpc_incoming_msg() */ + +/*****************************************************************************/ +/* + * accept a new call + * - called from krxiod in process context + */ +void rxrpc_trans_receive_packet(struct rxrpc_transport *trans) +{ + struct rxrpc_message *msg; + struct rxrpc_peer *peer; + struct sk_buff *pkt; + int ret; + u32 addr; + u16 port; + + LIST_HEAD(msgq); + + _enter("%p{%d}",trans,trans->port); + + for (;;) { + /* deal with outstanting errors first */ + if (trans->error_rcvd) + rxrpc_trans_receive_error_report(trans); + + /* attempt to receive a packet */ + pkt = skb_recv_datagram(trans->socket->sk,0,1,&ret); + if (!pkt) { + if (ret==-EAGAIN) { + _leave(" EAGAIN"); + return; + } + + /* an icmp error may have occurred */ + rxrpc_krxiod_queue_transport(trans); + _leave(" error %d\n",ret); + return; + } + + /* we'll probably need to checksum it (didn't call sock_recvmsg) */ + if (pkt->ip_summed != CHECKSUM_UNNECESSARY) { + if ((unsigned short)csum_fold(skb_checksum(pkt,0,pkt->len,pkt->csum))) { + kfree_skb(pkt); + rxrpc_krxiod_queue_transport(trans); + _leave(" CSUM failed"); + return; + } + } + + addr = pkt->nh.iph->saddr; + port = pkt->h.uh->source; + + _net("Rx Received UDP packet from %08x:%04hu",ntohl(addr),ntohs(port)); + + /* unmarshall the Rx parameters and split jumbo packets */ + ret = rxrpc_incoming_msg(trans,pkt,&msgq); + if (ret<0) { + kfree_skb(pkt); + rxrpc_krxiod_queue_transport(trans); + _leave(" bad packet"); + return; + } + + if (list_empty(&msgq)) BUG(); + + msg = list_entry(msgq.next,struct rxrpc_message,link); + + /* locate the record for the peer from which it originated */ + ret = rxrpc_peer_lookup(trans,addr,&peer); + if (ret<0) { + kdebug("Rx No connections from that peer"); + rxrpc_trans_immediate_abort(trans,msg,-EINVAL); + goto finished_msg; + } + + /* try and find a matching connection */ + ret = rxrpc_connection_lookup(peer,msg,&msg->conn); + if (ret<0) { + kdebug("Rx Unknown Connection"); + rxrpc_trans_immediate_abort(trans,msg,-EINVAL); + rxrpc_put_peer(peer); + goto finished_msg; + } + rxrpc_put_peer(peer); + + /* deal with the first packet of a new call */ + if (msg->hdr.flags & RXRPC_CLIENT_INITIATED && + msg->hdr.type==RXRPC_PACKET_TYPE_DATA && + ntohl(msg->hdr.seq)==1 + ) { + _debug("Rx New server call"); + rxrpc_trans_receive_new_call(trans,&msgq); + goto finished_msg; + } + + /* deal with subsequent packet(s) of call */ + _debug("Rx Call packet"); + while (!list_empty(&msgq)) { + msg = list_entry(msgq.next,struct rxrpc_message,link); + list_del_init(&msg->link); + + ret = rxrpc_conn_receive_call_packet(msg->conn,NULL,msg); + if (ret<0) { + rxrpc_trans_immediate_abort(trans,msg,ret); + rxrpc_put_message(msg); + goto finished_msg; + } + + rxrpc_put_message(msg); + } + + goto finished_msg; + + /* dispose of the packets */ + finished_msg: + while (!list_empty(&msgq)) { + msg = list_entry(msgq.next,struct rxrpc_message,link); + list_del_init(&msg->link); + + rxrpc_put_message(msg); + } + kfree_skb(pkt); + } + + _leave(""); + +} /* end rxrpc_trans_receive_packet() */ + +/*****************************************************************************/ +/* + * accept a new call from a client trying to connect to one of my services + * - called in process context + */ +static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans, + struct list_head *msgq) +{ + struct rxrpc_message *msg; + + _enter(""); + + /* only bother with the first packet */ + msg = list_entry(msgq->next,struct rxrpc_message,link); + list_del_init(&msg->link); + rxrpc_krxsecd_queue_incoming_call(msg); + rxrpc_put_message(msg); + + _leave(" = 0"); + + return 0; +} /* end rxrpc_trans_receive_new_call() */ + +/*****************************************************************************/ +/* + * perform an immediate abort without connection or call structures + */ +int rxrpc_trans_immediate_abort(struct rxrpc_transport *trans, + struct rxrpc_message *msg, + int error) +{ + struct rxrpc_header ahdr; + struct sockaddr_in sin; + struct msghdr msghdr; + struct iovec iov[2]; + mm_segment_t oldfs; + int len, ret; + u32 _error; + + _enter("%p,%p,%d",trans,msg,error); + + /* don't abort an abort packet */ + if (msg->hdr.type==RXRPC_PACKET_TYPE_ABORT) { + _leave(" = 0"); + return 0; + } + + _error = htonl(-error); + + /* set up the message to be transmitted */ + memcpy(&ahdr,&msg->hdr,sizeof(ahdr)); + ahdr.epoch = msg->hdr.epoch; + ahdr.serial = htonl(1); + ahdr.seq = 0; + ahdr.type = RXRPC_PACKET_TYPE_ABORT; + ahdr.flags = RXRPC_LAST_PACKET | (~msg->hdr.flags & RXRPC_CLIENT_INITIATED); + + iov[0].iov_len = sizeof(ahdr); + iov[0].iov_base = &ahdr; + iov[1].iov_len = sizeof(_error); + iov[1].iov_base = &_error; + + len = sizeof(ahdr) + sizeof(_error); + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = msg->pkt->h.uh->source; + sin.sin_addr.s_addr = msg->pkt->nh.iph->saddr; + + msghdr.msg_name = &sin; + msghdr.msg_namelen = sizeof(sin); + msghdr.msg_iov = iov; + msghdr.msg_iovlen = 2; + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + msghdr.msg_flags = MSG_DONTWAIT; + + _net("Sending message type %d of %d bytes to %08x:%d", + ahdr.type, + len, + htonl(sin.sin_addr.s_addr), + htons(sin.sin_port)); + + /* send the message */ + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = sock_sendmsg(trans->socket,&msghdr,len); + set_fs(oldfs); + + _leave(" = %d",ret); + return ret; +} /* end rxrpc_trans_immediate_abort() */ + +/*****************************************************************************/ +/* + * receive an ICMP error report and percolate it to all connections heading to the affected + * host or port + */ +static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans) +{ + struct rxrpc_connection *conn; + struct sockaddr_in sin; + struct rxrpc_peer *peer; + struct list_head connq, *_p; + struct errormsg emsg; + struct msghdr msg; + mm_segment_t oldfs; + int local, err; + u16 port; + + _enter("%p",trans); + + for (;;) { + trans->error_rcvd = 0; + + /* try and receive an error message */ + msg.msg_name = &sin; + msg.msg_namelen = sizeof(sin); + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + msg.msg_control = &emsg; + msg.msg_controllen = sizeof(emsg); + msg.msg_flags = 0; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sock_recvmsg(trans->socket,&msg,0,MSG_ERRQUEUE|MSG_DONTWAIT|MSG_TRUNC); + set_fs(oldfs); + + if (err==-EAGAIN) { + _leave(""); + return; + } + + if (err<0) { + printk("%s: unable to recv an error report: %d\n",__FUNCTION__,err); + _leave(""); + return; + } + + msg.msg_controllen = (char*)msg.msg_control - (char*)&emsg; + + if (msg.msg_controllenconn_lock); + list_for_each(_p,&peer->conn_active) { + conn = list_entry(_p,struct rxrpc_connection,link); + if (port && conn->addr.sin_port!=port) + continue; + if (!list_empty(&conn->err_link)) + continue; + + rxrpc_get_connection(conn); + list_add_tail(&conn->err_link,&connq); + } + read_unlock(&peer->conn_lock); + + /* service all those connections */ + while (!list_empty(&connq)) { + conn = list_entry(connq.next,struct rxrpc_connection,err_link); + list_del(&conn->err_link); + + rxrpc_conn_handle_error(conn,local,err); + + rxrpc_put_connection(conn); + } + + rxrpc_put_peer(peer); + } + } + + _leave(""); + return; +} /* end rxrpc_trans_receive_error_report() */