/*---------------------------------------------------------------- * disk log constructor/destructor * * argv contains log_device region_size followed optionally by [no]sync *--------------------------------------------------------------*/ static int disk_ctr(struct dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv) { int r; size_t size; struct log_c *lc; struct dm_dev *dev; if (argc < 2 || argc > 3) { DMWARN("wrong number of arguments to disk mirror log"); return -EINVAL; } r = dm_get_device(ti, argv[0], 0, 0 /* FIXME */, FMODE_READ | FMODE_WRITE, &dev); if (r) return r; r = core_ctr(log, ti, argc - 1, argv + 1); if (r) { dm_put_device(ti, dev); return r; } lc = (struct log_c *) log->context; lc->log_dev = dev; /* setup the disk header fields */ lc->header_location.bdev = lc->log_dev->bdev; lc->header_location.sector = 0; lc->header_location.count = 1; /* * We can't read less than this amount, even though we'll * not be using most of this space. */ lc->disk_header = vmalloc(1 << SECTOR_SHIFT); if (!lc->disk_header) goto bad; /* setup the disk bitset fields */ lc->bits_location.bdev = lc->log_dev->bdev; lc->bits_location.sector = LOG_OFFSET; size = dm_round_up(lc->bitset_uint32_count * sizeof(uint32_t), 1 << SECTOR_SHIFT); lc->bits_location.count = size >> SECTOR_SHIFT; lc->disk_bits = vmalloc(size); if (!lc->disk_bits) { vfree(lc->disk_header); goto bad; } return 0; bad: dm_put_device(ti, lc->log_dev); core_dtr(log); return -ENOMEM; }
/* * Parse a single <dev> <sector> pair */ static int get_vm(struct dm_target *ti, struct vm_c *vc, unsigned int vm, char **argv) { unsigned long long start; char dummy; if (sscanf(argv[1], "%llu%c", &start, &dummy) != 1) return -EINVAL; if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &vc->vm[vm].dev)) return -ENXIO; vc->vm[vm].physical_start = start; return 0; }
/*---------------------------------------------------------------- * disk log constructor/destructor * * argv contains log_device region_size followed optionally by [no]sync *--------------------------------------------------------------*/ static int disk_ctr(struct dm_dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv) { int r; struct dm_dev *dev; if (argc < 2 || argc > 3) { DMWARN("wrong number of arguments to disk dirty region log"); return -EINVAL; } r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &dev); if (r) return r; r = create_log_context(log, ti, argc - 1, argv + 1, dev); if (r) { dm_put_device(ti, dev); return r; } return 0; }
/*---------------------------------------------------------------- * disk log constructor/destructor * * argv contains log_device region_size followed optionally by [no]sync *--------------------------------------------------------------*/ static int disk_ctr(struct dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv) { int r; struct dm_dev *dev; if (argc < 2 || argc > 3) { DMWARN("wrong number of arguments to disk mirror log"); return -EINVAL; } r = dm_get_device(ti, argv[0], 0, 0 /* FIXME */, FMODE_READ | FMODE_WRITE, &dev); if (r) return r; r = create_log_context(log, ti, argc - 1, argv + 1, dev); if (r) { dm_put_device(ti, dev); return r; } return 0; }
/* * userspace_ctr * * argv contains: * <UUID> [integrated_flush] <other args> * Where 'other args' are the userspace implementation-specific log * arguments. * * Example: * <UUID> [integrated_flush] clustered-disk <arg count> <log dev> * <region_size> [[no]sync] * * This module strips off the <UUID> and uses it for identification * purposes when communicating with userspace about a log. * * If integrated_flush is defined, the kernel combines flush * and mark requests. * * The rest of the line, beginning with 'clustered-disk', is passed * to the userspace ctr function. */ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti, unsigned argc, char **argv) { int r = 0; int str_size; char *ctr_str = NULL; struct log_c *lc = NULL; uint64_t rdata; size_t rdata_size = sizeof(rdata); char *devices_rdata = NULL; size_t devices_rdata_size = DM_NAME_LEN; if (argc < 3) { DMWARN("Too few arguments to userspace dirty log"); return -EINVAL; } lc = kzalloc(sizeof(*lc), GFP_KERNEL); if (!lc) { DMWARN("Unable to allocate userspace log context."); return -ENOMEM; } /* The ptr value is sufficient for local unique id */ lc->luid = (unsigned long)lc; lc->ti = ti; if (strlen(argv[0]) > (DM_UUID_LEN - 1)) { DMWARN("UUID argument too long."); kfree(lc); return -EINVAL; } lc->usr_argc = argc; strncpy(lc->uuid, argv[0], DM_UUID_LEN); argc--; argv++; spin_lock_init(&lc->flush_lock); INIT_LIST_HEAD(&lc->mark_list); INIT_LIST_HEAD(&lc->clear_list); if (!strcasecmp(argv[0], "integrated_flush")) { lc->integrated_flush = 1; argc--; argv++; } str_size = build_constructor_string(ti, argc, argv, &ctr_str); if (str_size < 0) { kfree(lc); return str_size; } devices_rdata = kzalloc(devices_rdata_size, GFP_KERNEL); if (!devices_rdata) { DMERR("Failed to allocate memory for device information"); r = -ENOMEM; goto out; } lc->flush_entry_pool = mempool_create_slab_pool(FLUSH_ENTRY_POOL_SIZE, _flush_entry_cache); if (!lc->flush_entry_pool) { DMERR("Failed to create flush_entry_pool"); r = -ENOMEM; goto out; } /* * Send table string and get back any opened device. */ r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_CTR, ctr_str, str_size, devices_rdata, &devices_rdata_size); if (r < 0) { if (r == -ESRCH) DMERR("Userspace log server not found"); else DMERR("Userspace log server failed to create log"); goto out; } /* Since the region size does not change, get it now */ rdata_size = sizeof(rdata); r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_GET_REGION_SIZE, NULL, 0, (char *)&rdata, &rdata_size); if (r) { DMERR("Failed to get region size of dirty log"); goto out; } lc->region_size = (uint32_t)rdata; lc->region_count = dm_sector_div_up(ti->len, lc->region_size); if (devices_rdata_size) { if (devices_rdata[devices_rdata_size - 1] != '\0') { DMERR("DM_ULOG_CTR device return string not properly terminated"); r = -EINVAL; goto out; } r = dm_get_device(ti, devices_rdata, dm_table_get_mode(ti->table), &lc->log_dev); if (r) DMERR("Failed to register %s with device-mapper", devices_rdata); } if (lc->integrated_flush) { lc->dmlog_wq = alloc_workqueue("dmlogd", WQ_MEM_RECLAIM, 0); if (!lc->dmlog_wq) { DMERR("couldn't start dmlogd"); r = -ENOMEM; goto out; } INIT_DELAYED_WORK(&lc->flush_log_work, do_flush); atomic_set(&lc->sched_flush, 0); } out: kfree(devices_rdata); if (r) { if (lc->flush_entry_pool) mempool_destroy(lc->flush_entry_pool); kfree(lc); kfree(ctr_str); } else { lc->usr_argv_str = ctr_str; log->context = lc; } return r; }
/*---------------------------------------------------------------- * disk log constructor/destructor * * argv contains 2 - 4 arguments: * <log_device> <region_size> [[no]sync] [block_on_error] *--------------------------------------------------------------*/ static int disk_ctr(struct dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv) { int r; size_t size, bitset_size; struct log_c *lc; struct dm_dev *dev; uint32_t *clean_bits; if (argc < 2 || argc > 4) { DMWARN("wrong number of arguments to disk mirror log"); return -EINVAL; } r = dm_get_device(ti, argv[0], 0, 0 /* FIXME */, FMODE_READ | FMODE_WRITE, &dev); if (r) return r; r = core_ctr(log, ti, argc - 1, argv + 1); if (r) { dm_put_device(ti, dev); return r; } lc = (struct log_c *) log->context; lc->log_dev = dev; lc->log_dev_failed = 0; /* setup the disk header fields */ lc->header_location.bdev = lc->log_dev->bdev; lc->header_location.sector = 0; /* Include both the header and the bitset in one buffer. */ bitset_size = lc->bitset_uint32_count * sizeof(uint32_t); size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) + bitset_size, ti->limits.hardsect_size); if (size > dev->bdev->bd_inode->i_size) { DMWARN("log device %s too small: need %llu bytes", dev->name, (unsigned long long)size); r = -EINVAL; goto bad; } lc->header_location.count = size >> SECTOR_SHIFT; lc->disk_header = vmalloc(size); if (!lc->disk_header) { r = -ENOMEM; goto bad; } /* * Deallocate the clean_bits buffer that was allocated in core_ctr() * and point it at the appropriate place in the disk_header buffer. */ clean_bits = lc->clean_bits; lc->clean_bits = (void *)lc->disk_header + (LOG_OFFSET << SECTOR_SHIFT); memcpy(lc->clean_bits, clean_bits, bitset_size); vfree(clean_bits); lc->io_req.mem.type = DM_IO_VMA; lc->io_req.client = dm_io_client_create(dm_div_up(size, PAGE_SIZE)); if (IS_ERR(lc->io_req.client)) { r = PTR_ERR(lc->io_req.client); DMWARN("couldn't allocate disk io client"); vfree(lc->disk_header); goto bad; } return 0; bad: dm_put_device(ti, lc->log_dev); core_dtr(log); return r; }