/* * get a minor number. needs to be called with * down_write(&dcssblk_devices_sem) and the * device needs to be enqueued before the semaphore is * freed.
*/ staticint
dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info)
{ int minor, found; struct dcssblk_dev_info *entry;
if (dev_info == NULL) return -EINVAL; for (minor = 0; minor < (1<<MINORBITS); minor++) {
found = 0; // test if minor available
list_for_each_entry(entry, &dcssblk_devices, lh) if (minor == entry->gd->first_minor)
found++; if (!found) break; // got unused minor
} if (found) return -EBUSY;
dev_info->gd->first_minor = minor; return 0;
}
/* * get the struct dcssblk_dev_info from dcssblk_devices * for the given name. * down_read(&dcssblk_devices_sem) must be held.
*/ staticstruct dcssblk_dev_info *
dcssblk_get_device_by_name(char *name)
{ struct dcssblk_dev_info *entry;
/* * get the struct segment_info from seg_list * for the given name. * down_read(&dcssblk_devices_sem) must be held.
*/ staticstruct segment_info *
dcssblk_get_segment_by_name(char *name)
{ struct dcssblk_dev_info *dev_info; struct segment_info *entry;
/* * get the lowest address of the multi-segment block.
*/ staticunsignedlong
dcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info)
{ int set_first; unsignedlong lowest_addr; struct segment_info *entry;
/* * device attribute for save operation on current copy * of the segment. If the segment is busy, saving will * become pending until it gets released, which can be * undone by storing a non-true value to this entry. * (show + store)
*/ static ssize_t
dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct dcssblk_dev_info *dev_info;
down_write(&dcssblk_devices_sem); if (inbuf[0] == '1') { if (atomic_read(&dev_info->use_count) == 0) { // device is idle => we save immediately
pr_info("All DCSSs that map to device %s are " "saved\n", dev_info->segment_name);
list_for_each_entry(entry, &dev_info->seg_list, lh) { if (entry->segment_type == SEG_TYPE_EN ||
entry->segment_type == SEG_TYPE_SN)
pr_warn("DCSS %s is of type SN or EN" " and cannot be saved\n",
entry->segment_name); else
segment_save(entry->segment_name);
}
} else { // device is busy => we save it when it becomes // idle in dcssblk_release
pr_info("Device %s is in use, its DCSSs will be " "saved when it becomes idle\n",
dev_info->segment_name);
dev_info->save_pending = 1;
}
} elseif (inbuf[0] == '0') { if (dev_info->save_pending) { // device is busy & the user wants to undo his save // request
dev_info->save_pending = 0;
pr_info("A pending save request for device %s " "has been canceled\n",
dev_info->segment_name);
}
} else {
up_write(&dcssblk_devices_sem); return -EINVAL;
}
up_write(&dcssblk_devices_sem); return count;
} static DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show,
dcssblk_save_store);
/* * device attribute for showing all segments in a device
*/ static ssize_t
dcssblk_seglist_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct dcssblk_dev_info *dev_info; struct segment_info *entry; int i;
i = 0;
down_read(&dcssblk_devices_sem);
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
list_for_each_entry(entry, &dev_info->seg_list, lh)
i += sysfs_emit_at(buf, i, "%s\n", entry->segment_name);
up_read(&dcssblk_devices_sem); return i;
} static DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL);
rc = dcssblk_load_segment(local_buf, &seg_info); if (rc < 0) goto seg_list_del; /* * get a struct dcssblk_dev_info
*/ if (num_of_segments == 0) {
dev_info = kzalloc(sizeof(struct dcssblk_dev_info),
GFP_KERNEL); if (dev_info == NULL) {
rc = -ENOMEM; goto out;
}
strscpy(dev_info->segment_name, local_buf);
dev_info->segment_type = seg_info->segment_type;
INIT_LIST_HEAD(&dev_info->seg_list);
}
list_add_tail(&seg_info->lh, &dev_info->seg_list);
num_of_segments++;
i = j;
if ((buf[j] == '\0') || (buf[j] == '\n')) break;
}
/* no trailing colon at the end of the input */ if ((i > 0) && (buf[i-1] == ':')) {
rc = -ENAMETOOLONG; goto seg_list_del;
}
strscpy(local_buf, buf, i + 1);
dev_info->num_of_segments = num_of_segments;
rc = dcssblk_is_continuous(dev_info); if (rc < 0) goto seg_list_del;
if (dev != dcssblk_root_dev) { return -EINVAL;
}
local_buf = kmalloc(count + 1, GFP_KERNEL); if (local_buf == NULL) { return -ENOMEM;
} /* * parse input
*/ for (i = 0; (i < count && (*(buf+i)!='\0') && (*(buf+i)!='\n')); i++) {
local_buf[i] = toupper(buf[i]);
}
local_buf[i] = '\0'; if ((i == 0) || (i > 8)) {
rc = -ENAMETOOLONG; goto out_buf;
}
down_write(&dcssblk_devices_sem);
dev_info = dcssblk_get_device_by_name(local_buf); if (dev_info == NULL) {
up_write(&dcssblk_devices_sem);
pr_warn("Device %s cannot be removed because it is not a known device\n",
local_buf);
rc = -ENODEV; goto out_buf;
} if (atomic_read(&dev_info->use_count) != 0) {
up_write(&dcssblk_devices_sem);
pr_warn("Device %s cannot be removed while it is in use\n",
local_buf);
rc = -EBUSY; goto out_buf;
}
list_del(&dev_info->lh); /* unload all related segments */
list_for_each_entry(entry, &dev_info->seg_list, lh)
segment_unload(entry->segment_name);
up_write(&dcssblk_devices_sem);
if (!dev_info) {
WARN_ON(1); return;
}
down_write(&dcssblk_devices_sem); if (atomic_dec_and_test(&dev_info->use_count)
&& (dev_info->save_pending)) {
pr_info("Device %s has become idle and is being saved " "now\n", dev_info->segment_name);
list_for_each_entry(entry, &dev_info->seg_list, lh) { if (entry->segment_type == SEG_TYPE_EN ||
entry->segment_type == SEG_TYPE_SN)
pr_warn("DCSS %s is of type SN or EN and cannot" " be saved\n", entry->segment_name); else
segment_save(entry->segment_name);
}
dev_info->save_pending = 0;
}
up_write(&dcssblk_devices_sem);
}
bytes_done = 0;
dev_info = bio->bi_bdev->bd_disk->private_data; if (dev_info == NULL) goto fail; if (!IS_ALIGNED(bio->bi_iter.bi_sector, 8) ||
!IS_ALIGNED(bio->bi_iter.bi_size, PAGE_SIZE)) /* Request is not page-aligned. */ goto fail; /* verify data transfer direction */ if (dev_info->is_shared) { switch (dev_info->segment_type) { case SEG_TYPE_SR: case SEG_TYPE_ER: case SEG_TYPE_SC: /* cannot write to these segments */ if (bio_data_dir(bio) == WRITE) {
pr_warn("Writing to %s failed because it is a read-only device\n",
dev_name(&dev_info->dev)); goto fail;
}
}
}
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, names in each set separated " "by commas are separated by colons, each set contains " "names of contiguous segments and each name max. 8 chars.\n" "Adding \"(local)\" to the end of each set equals echoing 0 " "to /sys/devices/dcssblk//shared after loading " "the contiguous segments - \n" "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\"");
MODULE_DESCRIPTION("S/390 block driver for DCSS memory");
MODULE_LICENSE("GPL");
Messung V0.5
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.