*/
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/init.h>
//#define DCSSBLK_DEBUG /* Debug messages on/off */
#define DCSSBLK_NAME "dcssblk"
#define DCSSBLK_MINORS_PER_DISK 1
+#define DCSSBLK_PARM_LEN 400
#ifdef DCSSBLK_DEBUG
#define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSSBLK_NAME " debug: " x)
static int dcssblk_release(struct inode *inode, struct file *filp);
static int dcssblk_make_request(struct request_queue *q, struct bio *bio);
+static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0";
+
static int dcssblk_major;
static struct block_device_operations dcssblk_devops = {
.owner = THIS_MODULE,
};
static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices);
-static rwlock_t dcssblk_devices_lock = RW_LOCK_UNLOCKED;
-
+static struct rw_semaphore dcssblk_devices_sem;
/*
* release function for segment device.
/*
* get a minor number. needs to be called with
- * write_lock(&dcssblk_devices_lock) and the
- * device needs to be enqueued before the lock is
+ * down_write(&dcssblk_devices_sem) and the
+ * device needs to be enqueued before the semaphore is
* freed.
*/
static inline int
/*
* get the struct dcssblk_dev_info from dcssblk_devices
* for the given name.
- * read_lock(&dcssblk_devices_lock) must be held.
+ * down_read(&dcssblk_devices_sem) must be held.
*/
static struct dcssblk_dev_info *
dcssblk_get_device_by_name(char *name)
}
/*
- * register the device that represents a segment in sysfs,
- * also add the attributes for the device
+ * print appropriate error message for segment_load()/segment_type()
+ * return code
*/
-static inline int
-dcssblk_register_segment_device(struct device *dev)
+static void
+dcssblk_segment_warn(int rc, char* seg_name)
{
- int rc;
-
- rc = device_register(dev);
- if (rc)
- return rc;
- rc = device_create_file(dev, &dev_attr_shared);
- if (rc)
- goto unregister_dev;
- rc = device_create_file(dev, &dev_attr_save);
- if (rc)
- goto unregister_dev;
- return rc;
-
-unregister_dev:
- device_unregister(dev);
- return rc;
+ switch (rc) {
+ case -ENOENT:
+ PRINT_WARN("cannot load/query segment %s, does not exist\n",
+ seg_name);
+ break;
+ case -ENOSYS:
+ PRINT_WARN("cannot load/query segment %s, not running on VM\n",
+ seg_name);
+ break;
+ case -EIO:
+ PRINT_WARN("cannot load/query segment %s, hardware error\n",
+ seg_name);
+ break;
+ case -ENOTSUPP:
+ PRINT_WARN("cannot load/query segment %s, is a multi-part "
+ "segment\n", seg_name);
+ break;
+ case -ENOSPC:
+ PRINT_WARN("cannot load/query segment %s, overlaps with "
+ "storage\n", seg_name);
+ break;
+ case -EBUSY:
+ PRINT_WARN("cannot load/query segment %s, overlaps with "
+ "already loaded dcss\n", seg_name);
+ break;
+ case -EPERM:
+ PRINT_WARN("cannot load/query segment %s, already loaded in "
+ "incompatible mode\n", seg_name);
+ break;
+ case -ENOMEM:
+ PRINT_WARN("cannot load/query segment %s, out of memory\n",
+ seg_name);
+ break;
+ case -ERANGE:
+ PRINT_WARN("cannot load/query segment %s, exceeds kernel "
+ "mapping range\n", seg_name);
+ break;
+ default:
+ PRINT_WARN("cannot load/query segment %s, return value %i\n",
+ seg_name, rc);
+ break;
+ }
}
/*
PRINT_WARN("Invalid value, must be 0 or 1\n");
return -EINVAL;
}
- write_lock(&dcssblk_devices_lock);
+ down_write(&dcssblk_devices_sem);
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
if (atomic_read(&dev_info->use_count)) {
PRINT_ERR("share: segment %s is busy!\n",
dev_info->segment_name);
- write_unlock(&dcssblk_devices_lock);
- return -EBUSY;
- }
- if ((inbuf[0] == '1') && (dev_info->is_shared == 1)) {
- PRINT_WARN("Segment %s already loaded in shared mode!\n",
- dev_info->segment_name);
- write_unlock(&dcssblk_devices_lock);
- return count;
- }
- if ((inbuf[0] == '0') && (dev_info->is_shared == 0)) {
- PRINT_WARN("Segment %s already loaded in exclusive mode!\n",
- dev_info->segment_name);
- write_unlock(&dcssblk_devices_lock);
- return count;
+ rc = -EBUSY;
+ goto out;
}
if (inbuf[0] == '1') {
// reload segment in shared mode
- segment_unload(dev_info->segment_name);
- rc = segment_load(dev_info->segment_name, SEGMENT_SHARED_RO,
- &dev_info->start, &dev_info->end);
+ rc = segment_modify_shared(dev_info->segment_name,
+ SEGMENT_SHARED);
if (rc < 0) {
- PRINT_ERR("Segment %s not reloaded, rc=%d\n",
- dev_info->segment_name, rc);
- goto removeseg;
+ BUG_ON(rc == -EINVAL);
+ if (rc == -EIO || rc == -ENOENT)
+ goto removeseg;
+ } else {
+ dev_info->is_shared = 1;
+ switch (dev_info->segment_type) {
+ case SEG_TYPE_SR:
+ case SEG_TYPE_ER:
+ case SEG_TYPE_SC:
+ set_disk_ro(dev_info->gd,1);
+ }
}
- dev_info->is_shared = 1;
- PRINT_INFO("Segment %s reloaded, shared mode.\n",
- dev_info->segment_name);
} else if (inbuf[0] == '0') {
// reload segment in exclusive mode
- segment_unload(dev_info->segment_name);
- rc = segment_load(dev_info->segment_name, SEGMENT_EXCLUSIVE_RW,
- &dev_info->start, &dev_info->end);
+ if (dev_info->segment_type == SEG_TYPE_SC) {
+ PRINT_ERR("Segment type SC (%s) cannot be loaded in "
+ "non-shared mode\n", dev_info->segment_name);
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = segment_modify_shared(dev_info->segment_name,
+ SEGMENT_EXCLUSIVE);
if (rc < 0) {
- PRINT_ERR("Segment %s not reloaded, rc=%d\n",
- dev_info->segment_name, rc);
- goto removeseg;
+ BUG_ON(rc == -EINVAL);
+ if (rc == -EIO || rc == -ENOENT)
+ goto removeseg;
+ } else {
+ dev_info->is_shared = 0;
+ set_disk_ro(dev_info->gd, 0);
}
- dev_info->is_shared = 0;
- PRINT_INFO("Segment %s reloaded, exclusive (read-write) mode.\n",
- dev_info->segment_name);
} else {
- write_unlock(&dcssblk_devices_lock);
PRINT_WARN("Invalid value, must be 0 or 1\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
- dev_info->segment_type = rc;
rc = count;
-
- switch (dev_info->segment_type) {
- case SEGMENT_SHARED_RO:
- case SEGMENT_EXCLUSIVE_RO:
- set_disk_ro(dev_info->gd, 1);
- break;
- case SEGMENT_SHARED_RW:
- case SEGMENT_EXCLUSIVE_RW:
- set_disk_ro(dev_info->gd, 0);
- break;
- }
- if ((inbuf[0] == '1') &&
- ((dev_info->segment_type == SEGMENT_EXCLUSIVE_RO) ||
- (dev_info->segment_type == SEGMENT_EXCLUSIVE_RW))) {
- PRINT_WARN("Could not get shared copy of segment %s\n",
- dev_info->segment_name);
- rc = -EPERM;
- }
- if ((inbuf[0] == '0') &&
- ((dev_info->segment_type == SEGMENT_SHARED_RO) ||
- (dev_info->segment_type == SEGMENT_SHARED_RW))) {
- PRINT_WARN("Could not get exclusive copy of segment %s\n",
- dev_info->segment_name);
- rc = -EPERM;
- }
- write_unlock(&dcssblk_devices_lock);
goto out;
removeseg:
PRINT_ERR("Could not reload segment %s, removing it now!\n",
dev_info->segment_name);
list_del(&dev_info->lh);
- write_unlock(&dcssblk_devices_lock);
del_gendisk(dev_info->gd);
blk_put_queue(dev_info->dcssblk_queue);
device_unregister(dev);
put_device(dev);
out:
+ up_write(&dcssblk_devices_sem);
return rc;
}
}
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
- write_lock(&dcssblk_devices_lock);
+ down_write(&dcssblk_devices_sem);
if (inbuf[0] == '1') {
if (atomic_read(&dev_info->use_count) == 0) {
// device is idle => we save immediately
PRINT_INFO("Saving segment %s\n",
dev_info->segment_name);
- segment_replace(dev_info->segment_name);
+ segment_save(dev_info->segment_name);
} else {
// device is busy => we save it when it becomes
// idle in dcssblk_release
dev_info->segment_name);
}
} else {
- write_unlock(&dcssblk_devices_lock);
+ up_write(&dcssblk_devices_sem);
PRINT_WARN("Invalid value, must be 0 or 1\n");
return -EINVAL;
}
- write_unlock(&dcssblk_devices_lock);
+ up_write(&dcssblk_devices_sem);
return count;
}
/*
* already loaded?
*/
- read_lock(&dcssblk_devices_lock);
+ down_read(&dcssblk_devices_sem);
dev_info = dcssblk_get_device_by_name(local_buf);
- read_unlock(&dcssblk_devices_lock);
+ up_read(&dcssblk_devices_sem);
if (dev_info != NULL) {
PRINT_WARN("Segment %s already loaded!\n", local_buf);
rc = -EEXIST;
/*
* load the segment
*/
- rc = segment_load(local_buf, SEGMENT_SHARED_RO,
+ rc = segment_load(local_buf, SEGMENT_SHARED,
&dev_info->start, &dev_info->end);
if (rc < 0) {
- PRINT_ERR("Segment %s not loaded, rc=%d\n", local_buf, rc);
+ dcssblk_segment_warn(rc, dev_info->segment_name);
goto dealloc_gendisk;
}
seg_byte_size = (dev_info->end - dev_info->start + 1);
set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
- PRINT_INFO("Loaded segment %s from %p to %p, size = %lu Byte, "
- "capacity = %lu sectors (512 Byte)\n", local_buf,
- (void *) dev_info->start, (void *) dev_info->end,
- seg_byte_size, seg_byte_size >> 9);
+ PRINT_INFO("Loaded segment %s, size = %lu Byte, "
+ "capacity = %lu (512 Byte) sectors\n", local_buf,
+ seg_byte_size, seg_byte_size >> 9);
dev_info->segment_type = rc;
dev_info->save_pending = 0;
/*
* get minor, add to list
*/
- write_lock(&dcssblk_devices_lock);
+ down_write(&dcssblk_devices_sem);
rc = dcssblk_assign_free_minor(dev_info);
if (rc) {
- write_unlock(&dcssblk_devices_lock);
+ up_write(&dcssblk_devices_sem);
PRINT_ERR("No free minor number available! "
"Unloading segment...\n");
goto unload_seg;
sprintf(dev_info->gd->disk_name, "dcssblk%d",
dev_info->gd->first_minor);
list_add_tail(&dev_info->lh, &dcssblk_devices);
+
+ if (!try_module_get(THIS_MODULE)) {
+ rc = -ENODEV;
+ goto list_del;
+ }
/*
* register the device
*/
- rc = dcssblk_register_segment_device(&dev_info->dev);
+ rc = device_register(&dev_info->dev);
if (rc) {
PRINT_ERR("Segment %s could not be registered RC=%d\n",
local_buf, rc);
+ module_put(THIS_MODULE);
goto list_del;
}
-
- if (!try_module_get(THIS_MODULE)) {
- rc = -ENODEV;
- goto list_del;
- }
-
get_device(&dev_info->dev);
+ rc = device_create_file(&dev_info->dev, &dev_attr_shared);
+ if (rc)
+ goto unregister_dev;
+ rc = device_create_file(&dev_info->dev, &dev_attr_save);
+ if (rc)
+ goto unregister_dev;
+
add_disk(dev_info->gd);
blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
switch (dev_info->segment_type) {
- case SEGMENT_SHARED_RO:
- case SEGMENT_EXCLUSIVE_RO:
+ case SEG_TYPE_SR:
+ case SEG_TYPE_ER:
+ case SEG_TYPE_SC:
set_disk_ro(dev_info->gd,1);
break;
- case SEGMENT_SHARED_RW:
- case SEGMENT_EXCLUSIVE_RW:
+ default:
set_disk_ro(dev_info->gd,0);
break;
}
PRINT_DEBUG("Segment %s loaded successfully\n", local_buf);
- write_unlock(&dcssblk_devices_lock);
+ up_write(&dcssblk_devices_sem);
rc = count;
goto out;
+unregister_dev:
+ PRINT_ERR("device_create_file() failed!\n");
+ list_del(&dev_info->lh);
+ blk_put_queue(dev_info->dcssblk_queue);
+ dev_info->gd->queue = NULL;
+ put_disk(dev_info->gd);
+ device_unregister(&dev_info->dev);
+ segment_unload(dev_info->segment_name);
+ put_device(&dev_info->dev);
+ up_write(&dcssblk_devices_sem);
+ goto out;
list_del:
list_del(&dev_info->lh);
- write_unlock(&dcssblk_devices_lock);
+ up_write(&dcssblk_devices_sem);
unload_seg:
segment_unload(local_buf);
dealloc_gendisk:
goto out_buf;
}
- write_lock(&dcssblk_devices_lock);
+ down_write(&dcssblk_devices_sem);
dev_info = dcssblk_get_device_by_name(local_buf);
if (dev_info == NULL) {
- write_unlock(&dcssblk_devices_lock);
+ up_write(&dcssblk_devices_sem);
PRINT_WARN("Segment %s is not loaded!\n", local_buf);
rc = -ENODEV;
goto out_buf;
}
if (atomic_read(&dev_info->use_count) != 0) {
- write_unlock(&dcssblk_devices_lock);
+ up_write(&dcssblk_devices_sem);
PRINT_WARN("Segment %s is in use!\n", local_buf);
rc = -EBUSY;
goto out_buf;
}
list_del(&dev_info->lh);
- write_unlock(&dcssblk_devices_lock);
del_gendisk(dev_info->gd);
blk_put_queue(dev_info->dcssblk_queue);
PRINT_DEBUG("Segment %s unloaded successfully\n",
dev_info->segment_name);
put_device(&dev_info->dev);
+ up_write(&dcssblk_devices_sem);
+
rc = count;
out_buf:
kfree(local_buf);
rc = -ENODEV;
goto out;
}
- write_lock(&dcssblk_devices_lock);
+ down_write(&dcssblk_devices_sem);
if (atomic_dec_and_test(&dev_info->use_count)
&& (dev_info->save_pending)) {
PRINT_INFO("Segment %s became idle and is being saved now\n",
dev_info->segment_name);
- segment_replace(dev_info->segment_name);
+ segment_save(dev_info->segment_name);
dev_info->save_pending = 0;
}
- write_unlock(&dcssblk_devices_lock);
+ up_write(&dcssblk_devices_sem);
rc = 0;
out:
return rc;
dev_info = bio->bi_bdev->bd_disk->private_data;
if (dev_info == NULL)
goto fail;
- if ((bio->bi_sector & 3) != 0 || (bio->bi_size & 4095) != 0)
+ if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0)
/* Request is not page-aligned. */
goto fail;
if (((bio->bi_size >> 9) + bio->bi_sector)
return 0;
}
+static void
+dcssblk_check_params(void)
+{
+ int rc, i, j, k;
+ char buf[9];
+ struct dcssblk_dev_info *dev_info;
+
+ for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
+ i++) {
+ for (j = i; (dcssblk_segments[j] != ',') &&
+ (dcssblk_segments[j] != '\0') &&
+ (dcssblk_segments[j] != '(') &&
+ (j - i) < 8; j++)
+ {
+ buf[j-i] = dcssblk_segments[j];
+ }
+ buf[j-i] = '\0';
+ rc = dcssblk_add_store(dcssblk_root_dev, buf, j-i);
+ if ((rc >= 0) && (dcssblk_segments[j] == '(')) {
+ for (k = 0; buf[k] != '\0'; k++)
+ buf[k] = toupper(buf[k]);
+ if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
+ down_read(&dcssblk_devices_sem);
+ dev_info = dcssblk_get_device_by_name(buf);
+ up_read(&dcssblk_devices_sem);
+ if (dev_info)
+ dcssblk_shared_store(&dev_info->dev,
+ "0\n", 2);
+ }
+ }
+ while ((dcssblk_segments[j] != ',') &&
+ (dcssblk_segments[j] != '\0'))
+ {
+ j++;
+ }
+ if (dcssblk_segments[j] == '\0')
+ break;
+ i = j;
+ }
+}
+
/*
* The init/exit functions.
*/
return rc;
}
dcssblk_major = rc;
+ init_rwsem(&dcssblk_devices_sem);
+
+ dcssblk_check_params();
+
PRINT_DEBUG("...finished!\n");
return 0;
}
module_init(dcssblk_init);
module_exit(dcssblk_exit);
+module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444);
+MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
+ "comma-separated list, each name max. 8 chars.\n"
+ "Adding \"(local)\" to segment name equals echoing 0 to "
+ "/sys/devices/dcssblk/<segment name>/shared after loading "
+ "the segment - \n"
+ "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\"");
+
MODULE_LICENSE("GPL");