/* * xen_segment.c */ #include #include #include #include #include #include #include #include #include #include #include #include segment_t xsegments[XEN_MAX_SEGMENTS]; /* * xen_segment_map_request * * xen_device must be a valid device. */ /* * NB. Al offsets and sizes here are in sector units. * eg. 'size == 1' means an actual size of 512 bytes. */ int xen_segment_map_request( phys_seg_t *pseg, struct task_struct *p, int operation, unsigned short segment_number, unsigned long sect_nr, unsigned long buffer, unsigned short nr_sects) { segment_t *seg; extent_t *ext; int sum, i; if ( segment_number >= XEN_MAX_SEGMENTS ) goto fail; seg = p->segment_list[segment_number]; if ( seg == NULL ) goto fail; /* check domain permissions */ if ( seg->domain != p->domain ) goto fail; /* check rw access */ if ((operation == WRITE && seg->mode != XEN_SEGMENT_RW) || (operation == READ && seg->mode == XEN_SEGMENT_UNUSED)) goto fail; /* find extent, check size */ sum = 0; i = 0; ext = seg->extents; while ( (i < seg->num_extents) && ((sum + ext->size) <= sect_nr) ) { sum += ext->size; ext++; i++; } if ( (sum + ext->size) <= sect_nr ) goto fail; pseg->sector_number = sect_nr + ext->offset - sum; pseg->buffer = buffer; pseg->nr_sects = nr_sects; pseg->dev = xendev_to_physdev(ext->disk); if ( pseg->dev == 0 ) goto fail; /* We're finished if the virtual extent didn't overrun the phys extent. */ if ( (sum + ext->size) >= (sect_nr + nr_sects) ) return 1; /* Just one more physical extent. */ /* Hmmm... make sure there's another extent to overrun onto! */ if ( (i+1) == seg->num_extents ) goto fail; pseg[1].nr_sects = (sect_nr + nr_sects) - (sum + ext->size); pseg[0].nr_sects = sum + ext->size - sect_nr; pseg[1].buffer = buffer + (pseg->nr_sects << 9); pseg[1].sector_number = ext[1].offset; pseg[1].dev = xendev_to_physdev(ext[1].disk); if ( pseg[1].dev == 0 ) goto fail; /* We don't allow overrun onto a third physical extent. */ if ( (sum + ext[0].size + ext[1].size) < (pseg[1].sector_number + pseg[1].nr_sects) ) goto fail; return 2; /* We overran onto a second physical es\xtent. */ fail: return -1; } /* * xen_segment_probe * * return a list of segments to the guestos */ void xen_segment_probe(struct task_struct *p, xen_disk_info_t *raw_xdi) { int loop, i; xen_disk_info_t *xdi = map_domain_mem(virt_to_phys(raw_xdi)); for ( loop = 0; loop < XEN_MAX_SEGMENTS; loop++ ) { if ( (xsegments[loop].mode == XEN_SEGMENT_UNUSED) || (xsegments[loop].domain != p->domain) ) continue; xdi->disks[xdi->count].device = MK_VIRTUAL_XENDEV(xsegments[loop].segment_number); for ( i = 0; i < xsegments[loop].num_extents; i++ ) { xdi->disks[xdi->count].capacity += xsegments[loop].extents[i].size; } xdi->count++; } unmap_domain_mem(xdi); } /* * xen_refresh_segment_list * * find all segments associated with a domain and assign * them to the domain */ void xen_refresh_segment_list (struct task_struct *p) { int loop; for (loop = 0; loop < XEN_MAX_SEGMENTS; loop++) { if ( (xsegments[loop].mode == XEN_SEGMENT_UNUSED) || (xsegments[loop].domain != p->domain) ) continue; p->segment_list[xsegments[loop].segment_number] = &xsegments[loop]; p->segment_count++; } } /* * create a new segment for a domain * * return 0 on success, 1 on failure * * TODO: need to check to see if the DOM#/SEG# combination * already exists. if so, reuse the slot in the segment table. */ int xen_segment_create(xv_disk_t *xvd_in) { int idx; int loop; xv_disk_t *xvd = map_domain_mem(virt_to_phys(xvd_in)); for (idx = 0; idx < XEN_MAX_SEGMENTS; idx++) { if (xsegments[idx].mode == XEN_SEGMENT_UNUSED) break; } if (idx == XEN_MAX_SEGMENTS) { printk (KERN_ALERT "xen_segment_create: unable to find free slot\n"); unmap_domain_mem(xvd); return 1; } xsegments[idx].mode = xvd->mode; xsegments[idx].domain = xvd->domain; xsegments[idx].segment_number = xvd->segment; xsegments[idx].num_extents = xvd->ext_count; xsegments[idx].extents = (extent_t *)kmalloc( sizeof(extent_t)*xvd->ext_count, GFP_KERNEL); /* could memcpy, but this is safer */ for (loop = 0; loop < xvd->ext_count; loop++) { xsegments[idx].extents[loop].disk = xvd->extents[loop].disk; xsegments[idx].extents[loop].offset = xvd->extents[loop].offset; xsegments[idx].extents[loop].size = xvd->extents[loop].size; if (xsegments[idx].extents[loop].size == 0) { printk("xen_segment_create: extent %d is zero length\n", loop); unmap_domain_mem(xvd); return 1; } } unmap_domain_mem(xvd); return 0; } /* * delete a segment from a domain * * return 0 on success, 1 on failure * * TODO: caller must ensure that only domain 0 calls this function */ int xen_segment_delete(struct task_struct *p, xv_disk_t *xvd) { return 0; } static void dump_segments(u_char key, void *dev_id, struct pt_regs *regs) { int loop, i; struct task_struct *p; printk("segment list\n"); for (loop = 0; loop < XEN_MAX_SEGMENTS; loop++) { if (xsegments[loop].mode != XEN_SEGMENT_UNUSED) { printk(" %2d: %s dom%d, seg# %d, num_exts: %d\n", loop, xsegments[loop].mode == XEN_SEGMENT_RO ? "RO" : "RW", xsegments[loop].domain, xsegments[loop].segment_number, xsegments[loop].num_extents); for (i = 0; i < xsegments[loop].num_extents; i++) { printk(" ext %d: disk %d, offset 0x%lx, size 0x%lx\n", i, xsegments[loop].extents[i].disk, xsegments[loop].extents[i].offset, xsegments[loop].extents[i].size); } } } printk("segments by domain\n"); p = current->next_task; do { printk(" domain: %d\n", p->domain); for (loop = 0; loop < p->segment_count; loop++) { printk(" mode:%d domain:%d seg:%d exts:%d\n", p->segment_list[loop]->mode, p->segment_list[loop]->domain, p->segment_list[loop]->segment_number, p->segment_list[loop]->num_extents); } p = p->next_task; } while (p != current); } /* * initialize segments */ void xen_segment_initialize(void) { memset (xsegments, 0, sizeof(xsegments)); add_key_handler('S', dump_segments, "dump segments"); }