/** * Anemone Client Module **/ #include "blockdev.h" /* Globals */ static int hardsect_size = PAGE_SIZE; ulong nsectors = SIZE; /* size of block device */ struct anemone_client *client = NULL; static int major_num; spinlock_t globallock; static int init_log (struct anemone_client * dev) { dev->trace_file = filp_open("/home/qian/anemone-trace.log", O_CREAT, 0); if(IS_ERR(dev->trace_file)) { printk(KERN_INFO "anemone: init_log(): " "failed opening trace log: " "error: %ld\n", -PTR_ERR(dev->trace_file)); dev->trace_file = NULL; return -1; } return 0; } /* These first few functions included basic requirements to implement * the standard opening and closing of a block device. */ static int anemone_client_open(struct inode *inode, struct file *filp) { struct anemone_client *dev = inode->i_bdev->bd_disk->private_data; spin_lock(&dev->lock); dev->users++; spin_unlock(&dev->lock); return 0; } static int anemone_client_release(struct inode *inode, struct file *filp) { struct anemone_client *dev = inode->i_bdev->bd_disk->private_data; spin_lock(&dev->lock); dev->users--; spin_unlock(&dev->lock); return 0; } int anemone_client_media_changed(struct gendisk *gd) { return 0; } int anemone_client_revalidate_disk(struct gendisk *gd) { return 0; } /* We only support a couple of ioctl commands */ int anemone_client_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, ulong arg) { long size; struct hd_geometry geo; struct anemone_client *dev = filp->private_data; int ret = -ENOTTY; switch(cmd) { case HDIO_GETGEO: size = dev->size * (hardsect_size / KERNEL_SECTOR_SIZE); geo.cylinders = (size & ~0x3f) >> 6; geo.heads = 4; geo.sectors = 16; geo.start = 4; if(copy_to_user((void __user *) arg, &geo, sizeof(geo))) { ret = -EFAULT; break; } ret = 0; break; case 1: printk(KERN_ALERT "anemone_client_ioctl(): outstanding_reqs = %lu\n", (ulong)atomic64_read(&outstanding_reqs)); ret = 0; break; } return -ENOTTY; } /* Device Operations Structure, with various * function pointers that we implement */ static struct block_device_operations anemone_client_ops = { .owner = THIS_MODULE, .open = anemone_client_open, .release = anemone_client_release, .media_changed = anemone_client_media_changed, .revalidate_disk = anemone_client_revalidate_disk, .ioctl = anemone_client_ioctl }; /** * anemone_client_transfer - manages the interface between the block I/O, * and the cache. * @dev: the block device * @sector: the beginning sector where we will begin transfering * @nsect: the number of sectors to be transfered * @buffer: the buffer we will opperate on (read from OR write to) * @write: 1 for write; 0 for read * * Description: * Manages the transaction of @nsect sectors starting at @sector between * the cache and @buffer. If @write == 1, we copy data from @buffer to * the cache, else (@write == 0) we copy data from the cache to @buffer. * * Return: * 0 - opperation completed successfully, and it is ok to end its * corresponding request. * AC_PAGING_OVER_NET - return this value if the cache is sending * a paging request over the network **/ static int anemone_client_transfer(struct anemone_client *dev, ulong sector, ulong nsect, int write, void *bio) { //ulong offset = sector * KERNEL_SECTOR_SIZE; ulong offset = sector / (hardsect_size / KERNEL_SECTOR_SIZE); ulong nbytes = nsect * KERNEL_SECTOR_SIZE; ulong flags; char *data; int response = 0; char *buffer; if(offset > DIV(~0,12)) { printk(KERN_ALERT "anemone_client_transfer(): sector*512 really overflows page offset = %lu !!!!!!\n", offset); } if(!irqs_disabled()) { printk(KERN_ALERT "anemone_client_transfer(): irqs are not disabled!\n"); BUG(); } if(offset > dev->size) { printk(KERN_ALERT "Anemone_Client: Beyond-end write (%ld, %ld)\n", offset, nbytes); return 0; } if(write) { if(cache_add(&dev->cache, offset, nbytes, bio) == PAGE_OUT_OVER_NET) response = AC_PAGING_OVER_NET_WRITE; } else { if((buffer = cache_retrieve(&dev->cache, offset, bio)) != NULL) { data = bio_kmap_irq((struct bio*)bio, &flags); memcpy(data, buffer, nbytes); bio_kunmap_irq(data, &flags); cache_remove(&dev->cache, offset); response = 0; } else response = AC_PAGING_OVER_NET_READ; } return response; } static void anemone_bio_trace (struct anemone_client * dev, struct timeval * t, struct bio * bio) { mm_segment_t old_fs; struct timeval tv; memset(dev->trace_buffer,'\0',ANEMONE_LOG_BUFF_SIZE); if (t == NULL) { t = &tv; do_gettimeofday(t); } if(dev->trace_file != NULL) { sprintf(dev->trace_buffer,"%u,%llu,%u,%c,%lu.%06lu\n", 0, (unsigned long long)bio->bi_sector,bio->bi_size, bio_data_dir(bio) == WRITE?'W':'R', t->tv_sec,t->tv_usec); old_fs = get_fs(); set_fs(get_ds()); dev->trace_file->f_op->write(dev->trace_file,dev->trace_buffer, strlen(dev->trace_buffer),&dev->trace_file->f_pos); set_fs(old_fs); } } /* Entry point to the pseudo-block device by the Swap Daemon * * Responsible for calling anemone_client_transfer() */ static int anemone_client_mk_req(request_queue_t *q, struct bio *bio) { struct anemone_client *dev = q->queuedata; int response = 0; ulong flags; if(debug) anemone_bio_trace (dev, NULL, bio); spin_lock(&globallock); //reads can't proceed if there's no space in outstanding requests queue while(atomic64_read(&outstanding_reqs) >= (REQ_THRESH_UPPER - 3)); spin_lock_irqsave(&dev->lock, flags); if(!irqs_disabled()) { printk(KERN_ALERT "anemone_client_mk_req(): irqs are not disabled!\n"); BUG(); } response = anemone_client_transfer(dev, bio->bi_sector, bio_cur_sectors(bio), bio_data_dir(bio) == WRITE, bio); if(debug) { printk(KERN_ALERT "anemone_client_mk_req():bio_data buffer is null! offset = %d size = %d, write= %d, segments =%d\n", (int)bio->bi_sector, bio_cur_sectors(bio),(int)(bio_data_dir(bio) == WRITE), bio_segments(bio)); } if(response != AC_PAGING_OVER_NET_READ) bio_endio(bio, bio->bi_size, 0); spin_unlock_irqrestore(&dev->lock, flags); spin_unlock(&globallock); if(irqs_disabled()) { // printk(KERN_ALERT "anemone_client_mk_req(): irqs are disabled before return!\n"); // BUG(); } return 0; } /* Procfiles used for debugging in /proc */ int anemone_client_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int total = 0, running, num = 0; ulong ta = 0, tu = 0; Server *s = NULL; struct list_head *x, *y; ulong ctrl = (ctrlcount + success + retransmissions) * headers; ulong tdata = (success+retransmissions) * (PAGE_SIZE + headers) + ctrl; timeval curr; do_gettimeofday(&curr); running = curr.tv_sec - started.tv_sec; total += sprintf(buf + total, "Outstanding_Reqs: %lu\n", (ulong)atomic64_read(&outstanding_reqs)); total += sprintf(buf + total, "Read Goodput: %lu Mbit/s\n", rband); total += sprintf(buf + total, "Write Goodput: %lu Mbit/s\n", wband); total += sprintf(buf + total, "Retransmits: %lu\n", retransmissions); total += sprintf(buf + total, "Spaced: %lu\n", spaced); total += sprintf(buf + total, "Success: %lu\n", success); total += sprintf(buf + total, "Packet drops: %lu\n", packet_drops); total += sprintf(buf + total, "Retrans drops: %lu\n", retrans_drops); total += sprintf(buf + total, "Interface Drops: %d\n", if_drops); total += sprintf(buf + total, "Total Reads: %lu\n", reads); total += sprintf(buf + total, "Total Writes: %lu\n", writes); total += sprintf(buf + total, "Total Mapped Pages: %lu\n", mapping.count); total += sprintf(buf + total, "Avg Read Latency: %lu usec\n", readlat); total += sprintf(buf + total, "Avg Write Latency %lu usec\n", writelat); total += sprintf(buf + total, "Control Message Bytes: %lu Broadcast msg count: %lu data msg count: %lu\n", ctrl, ctrlcount, (success+retransmissions)); total += sprintf(buf + total, "Data Bytes : %lu Ctrl/Data rate %lu\n", tdata, (ctrl*100000/tdata) ); total += sprintf(buf + total, "\nCache HIT/MISS\n"); total += sprintf(buf + total, "\t HIT: %lu\n", cache_hits); total += sprintf(buf + total, "\tREAD: %lu\n", cache_reads); total += sprintf(buf + total, "\nRunning time: %d secs (%d min)\n\n", running, running / 60); total += sprintf(buf + total, "Server Summary:\n"); list_for_each_safe(x, y, &server_list) { s = list_entry(x, struct Server, queue); ta += s->avail_mem; tu += s->total_mem; total += sprintf(buf + total, " Server %d) %x, avail_mem = %luMB, %lu pages, percent= %lu, writes= %lu, reads=%lu.\n", ++num, s->mac[5], MB(s->avail_mem), s->avail_mem, (int)100*s->avail_mem/s->total_mem, s->writes, s->reads); } total += sprintf(buf + total, " Total Memory: %lu pages, %lu MB\n", tu, MB(tu)); total += sprintf(buf + total, " Total Available: %lu pages, %lu MB \n", ta, MB(ta)); return total; } /* Kernel module initialization routines. This includes initializing the * network subsystem and the caching subsystem */ static int anemone_client_setup(struct anemone_client *dev) { printk(KERN_ALERT "Device size: %lu (%lu MB)\n", nsectors, nsectors * 4 / 1024); dev->size = nsectors; dev->users = 0; spin_lock_init(&dev->lock); spin_lock_init(&globallock); dev->queue = blk_alloc_queue(GFP_KERNEL); /* Initialize the client's cache */ if(cache_init(&dev->cache, CACHE_SIZE, mru_replace, NULL, dev, &dev->lock, dev->queue)) { blk_cleanup_queue(client->queue); printk(KERN_ALERT "cache_init() failure!\n"); return -1; } /* Block device specific initializations */ blk_queue_make_request(dev->queue, anemone_client_mk_req); blk_queue_hardsect_size(dev->queue, hardsect_size); dev->queue->queuedata = dev; dev->gd = alloc_disk(ANEMONE_CLIENT_MINORS); if(!dev->gd) { printk(KERN_ALERT "alloc_disk() failure\n"); goto out_kfree; } dev->gd->major = major_num; dev->gd->first_minor = ANEMONE_CLIENT_MINORS; dev->gd->fops = &anemone_client_ops; dev->gd->queue = dev->queue; dev->gd->private_data = dev; snprintf(dev->gd->disk_name, 32, "anemone%c", 'a'); set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE)); add_disk(dev->gd); create_proc_read_entry("anemone_client", 0, NULL, anemone_client_read_procmem, NULL); out_kfree: return 0; } /* Kernel module initialization routines. This includes initializing the * network subsystem and the caching subsystem */ static int __init anemone_client_init(void) { client = kmalloc(sizeof(struct anemone_client), GFP_ATOMIC); major_num = 0; major_num = register_blkdev(major_num, "Anemone_Client"); if(major_num == 0) return -EBUSY; if(anemone_client_setup(client)) { printk(KERN_ALERT "Anemone Client FAILED to Load!\n"); unregister_blkdev(major_num, "Anemone_Client"); kfree(client); return -1; } init_log(client); printk(KERN_ALERT "Anemone Client Loaded\n"); return 0; } /* Kernel module exit routine */ static void __exit anemone_client_exit(void) { remove_proc_entry("anemone_client", NULL); if (client->trace_file != NULL) { filp_close(client->trace_file, NULL); client->trace_file = NULL; } /* Cleanup client's cache */ cache_cleanup(&client->cache); if (client->gd) { del_gendisk(client->gd); put_disk(client->gd); } if (client->queue) blk_cleanup_queue(client->queue); unregister_blkdev(major_num, "Anemone_Client"); kfree(client); printk(KERN_ALERT "Anemone Client Unloaded\n"); return; } module_init(anemone_client_init); module_exit(anemone_client_exit);