public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Kevin Corry <corryk@us.ibm.com>
To: torvalds@transmeta.com
Cc: linux-kernel@vger.kernel.org, evms-devel@lists.sourceforge.net
Subject: [PATCH] EVMS core (2/9) services.c
Date: Thu, 10 Oct 2002 14:35:14 -0500	[thread overview]
Message-ID: <02101014351404.17770@boiler> (raw)
In-Reply-To: <02101014305502.17770@boiler>

Greetings,

Part 2 of the EVMS core driver.

This file provides all of the common services that are available
to the EVMS plugins.

Kevin Corry
corryk@us.ibm.com
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/drivers/evms/core/services.c linux-2.5.41-evms/drivers/evms/core/services.c
--- linux-2.5.41/drivers/evms/core/services.c	Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/core/services.c	Thu Oct 10 13:14:27 2002
@@ -0,0 +1,1003 @@
+/*
+ *   Copyright (c) International Business Machines  Corp., 2000 - 2002
+ *
+ *   This program is free software;  you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program;  if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * EVMS Common Services
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bio.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+
+#include <linux/evms.h>
+#include <linux/evms_ioctl.h>
+#include "evms_core.h"
+
+/*
+ * evms services version
+ */
+struct evms_version evms_svc_version = {
+	.major = EVMS_COMMON_SERVICES_MAJOR,
+	.minor = EVMS_COMMON_SERVICES_MINOR,
+	.patchlevel = EVMS_COMMON_SERVICES_PATCHLEVEL
+};
+
+/**
+ * evms_cs_check_version - compares to evms_version structures
+ * @required:	required version level
+ * @actual:	actual version level
+ *
+ * Compares two evms_version structures and returns -EINVAL if the
+ * actual version does not meet the required version level.
+ **/
+int
+evms_cs_check_version(struct evms_version *required,
+		      struct evms_version *actual)
+{
+	if ((required->major != actual->major) ||
+	    (required->minor > actual->minor) ||
+	    ((required->minor == actual->minor) &&
+	     (required->patchlevel > actual->patchlevel)))
+		return -EINVAL;
+	return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_check_version);
+
+/**
+ * evms_cs_allocate_logical_node - allocates an evms logical node structure
+ * @pp:		address of the pointer which will contain the address of newly allocated node
+ *
+ * Allocates and zeros an evms_logical_node structure.
+ *
+ * returns: 0 if sucessful
+ *          -ENOMEM if unsuccessful
+ **/
+int
+evms_cs_allocate_logical_node(struct evms_logical_node **pp)
+{
+	*pp = kmalloc(sizeof (struct evms_logical_node), GFP_KERNEL);
+	if (*pp == NULL) {
+		return -ENOMEM;
+	}
+	memset(*pp, 0, sizeof (struct evms_logical_node));
+	INIT_LIST_HEAD(&(*pp)->discover);
+	INIT_LIST_HEAD(&(*pp)->device);
+	INIT_LIST_HEAD(&(*pp)->fbottom);
+	INIT_LIST_HEAD(&(*pp)->removable);
+	INIT_LIST_HEAD(&(*pp)->consumed);
+	INIT_LIST_HEAD(&(*pp)->produced);
+	return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_allocate_logical_node);
+
+/**
+ * evms_cs_deallocate_volume_info - frees any attached volume info structure
+ * @p:	address node to be freed
+ *
+ * If there is a evms_volume_info structure attached to this logical node, we
+ * know this node is a bottom-most storage object containing EVMS metadata for
+ * this volume. As such we must remove the node from the global feature node
+ * list, and then free the volume info structure.
+ **/
+void
+evms_cs_deallocate_volume_info(struct evms_logical_node *p)
+{
+	if (p->iflags & EVMS_FEATURE_BOTTOM) {
+		list_del_init(&p->fbottom);
+		kfree(p->volume_info);
+		p->volume_info = NULL;
+		p->iflags &= ~EVMS_FEATURE_BOTTOM;
+	}
+}
+
+EXPORT_SYMBOL(evms_cs_deallocate_volume_info);
+
+/**
+ * evms_cs_deallocate_logical_node - frees an evms logical node structure
+ * @p:	address of node to be freed
+ *
+ * Frees an allocated logical node, frees any attached volume info or feature
+ * header structures.
+ **/
+void
+evms_cs_deallocate_logical_node(struct evms_logical_node *p)
+{
+	evms_cs_deallocate_volume_info(p);
+	if (p->feature_header) {
+		kfree(p->feature_header);
+		p->feature_header = NULL;
+	}
+	BUG_ON(!list_empty(&p->discover));
+	BUG_ON(!list_empty(&p->device));
+	BUG_ON(!list_empty(&p->fbottom));
+	BUG_ON(!list_empty(&p->removable));
+	BUG_ON(!list_empty(&p->consumed));
+	BUG_ON(!list_empty(&p->produced));
+	kfree(p);
+}
+
+EXPORT_SYMBOL(evms_cs_deallocate_logical_node);
+
+/**
+ * evms_cs_register_plugin - validates and registers a newly loaded kernel plugin
+ * @plugin:	header of plugin attempting to register
+ *
+ * Validates and registers a new plugin.
+ **/
+int
+evms_cs_register_plugin(struct evms_plugin_header *plugin)
+{
+	int rc;
+	struct evms_plugin_header *p;
+
+	if (!(plugin_head.next && plugin_head.prev)) {
+		INIT_LIST_HEAD(&plugin_head);
+	}
+
+	LOG_EXTRA("registering plugin (id=%d.%d.%d, ver=%d.%d.%d)\n",
+		  GetPluginOEM(plugin->id), GetPluginType(plugin->id),
+		  GetPluginID(plugin->id), plugin->version.major,
+		  plugin->version.minor, plugin->version.patchlevel);
+
+	/* ensure a plugin with this feature id is
+	 * not already loaded.
+	 */
+	spin_lock(&plugin_lock);
+	list_for_each_entry(p, &plugin_head, headers) {
+		if (p->id == plugin->id) {
+			spin_unlock(&plugin_lock);
+			rc = -EBUSY;
+			LOG_ERROR("error(%d) attempting to load another "
+				  "plugin with id(%x).\n", rc, plugin->id);
+			return rc;
+		}
+	}
+	spin_unlock(&plugin_lock);
+
+	/* ensure the plugin has provided functions for
+	 * the mandatory entry points.
+	 */
+	if (!plugin->fops->discover ||
+	    !plugin->fops->sync_io ||
+	    !plugin->fops->ioctl ||
+	    !plugin->fops->submit_io ||
+	    !plugin->fops->open ||
+	    !plugin->fops->release ||
+	    !plugin->fops->check_media_change ||
+	    !plugin->fops->revalidate ||
+	    !plugin->fops->quiesce ||
+	    !plugin->fops->get_geo ||
+	    !plugin->fops->device_list ||
+	    !plugin->fops->device_status ||
+	    !plugin->fops->delete) {
+		return -EINVAL;
+	}
+	spin_lock(&plugin_lock);
+	list_add(&plugin->headers, &plugin_head);
+	spin_unlock(&plugin_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_register_plugin);
+
+/**
+ * evms_cs_unregister_plugin - unregisters a kernel plugin
+ * @plugin:	header of plugin to unregister
+ *
+ * Unregisters a loaded EVMS kernel plugin.
+ **/
+int
+evms_cs_unregister_plugin(struct evms_plugin_header *plugin)
+{
+	struct evms_plugin_header *p;
+
+	LOG_EXTRA("unregistering plugin (id=%d.%d.%d ver=%d.%d.%d)\n",
+		  GetPluginOEM(plugin->id), GetPluginType(plugin->id),
+		  GetPluginID(plugin->id), plugin->version.major,
+		  plugin->version.minor, plugin->version.patchlevel);
+
+	/* Ensure a plugin with this feature id is
+	 * currently loaded.
+	 */
+	spin_lock(&plugin_lock);
+	list_for_each_entry(p, &plugin_head, headers) {
+		if (p->id == plugin->id) {
+			list_del_init(&plugin->headers);
+			spin_unlock(&plugin_lock);
+			return 0;
+		}
+	}
+	spin_unlock(&plugin_lock);
+	LOG_ERROR("error(%d) attempt to unload a non-loaded plugin "
+		  "with id(%x).\n", -ENOPKG, plugin->id);
+	return -ENOPKG;
+}
+
+EXPORT_SYMBOL(evms_cs_unregister_plugin);
+
+/**
+ * evms_cs_kernel_ioctl - performs a userspace ioctl from within the kernel
+ * @node:	the storage object that is the target of this ioctl
+ * @cmd:	the ioctl command
+ * @arg:	the ioctl argument(s)
+ *
+ * Performs a userspace ioctl from within the kernel
+ **/
+int
+evms_cs_kernel_ioctl(struct evms_logical_node *node,
+		     unsigned int cmd, ulong arg)
+{
+	int rc;
+	struct inode tmp_inode;
+	mm_segment_t fs;
+
+	lock_kernel();
+	fs = get_fs();
+	set_fs(get_ds());
+	rc = IOCTL(node, &tmp_inode, NULL, cmd, arg);
+	set_fs(fs);
+	unlock_kernel();
+
+	return rc;
+}
+
+EXPORT_SYMBOL(evms_cs_kernel_ioctl);
+
+/**
+ * evms_cs_size_in_vsectors - returns rounded-up 512 byte unit value
+ * *item_size:	size of item in bytes
+ *
+ * Returns the # of 512 byte multiples an item occupies
+ **/
+inline ulong
+evms_cs_size_in_vsectors(long long item_size)
+{
+	long long sectors;
+	sectors = item_size >> EVMS_VSECTOR_SIZE_SHIFT;
+	if (item_size & (EVMS_VSECTOR_SIZE - 1))
+		sectors++;
+	return sectors;
+}
+
+EXPORT_SYMBOL(evms_cs_size_in_vsectors);
+
+/**
+ * evms_cs_log2 - computes the power of 2 of a specified value
+ * @value:	any value
+ *
+ * returns: -1 for value of 0
+ *	    -2 if value is not a power of 2
+ *	    power of 2
+ **/
+inline int
+evms_cs_log2(long long value)
+{
+	int result = -1;
+	long long tmp;
+
+	if (value) {
+		tmp = value;
+		result++;
+		while (!(tmp & 1)) {
+			result++;
+			tmp >>= 1;
+		}
+		if (tmp != 1) {
+			result = -2;
+		}
+	}
+	return result;
+}
+
+EXPORT_SYMBOL(evms_cs_log2);
+
+/*
+ * Defines and variables used by the CRC function
+ */
+#define CRC_POLYNOMIAL     0xEDB88320L
+static u32 crc_table[256];
+static u32 crc_table_built = 0;
+
+/**
+ * build_crc_table
+ *
+ * Initialzes the internal crc table
+ **/
+static void
+build_crc_table(void)
+{
+	u32 i, j, crc;
+
+	for (i = 0; i <= 255; i++) {
+		crc = i;
+		for (j = 8; j > 0; j--) {
+			if (crc & 1)
+				crc = (crc >> 1) ^ CRC_POLYNOMIAL;
+			else
+				crc >>= 1;
+		}
+		crc_table[i] = crc;
+	}
+	crc_table_built = 1;
+}
+
+/**
+ * evms_cs_calculate_crc
+ * @crc:       	the inital(0xFFFFFFFF) or rolling crc value
+ * @buffer:	address of buffer to compute crc
+ * @buffersize:	size of buffer
+ *
+ * This function calculates the crc value for the data 
+ * in the buffer specified by Buffer. 
+ **/
+u32
+evms_cs_calculate_crc(u32 crc, void *buffer, u32 buffersize)
+{
+	unsigned char *current_byte;
+	u32 temp1, temp2, i;
+
+	current_byte = (unsigned char *) buffer;
+	/* Make sure the crc table is available */
+	if (crc_table_built == 0)
+		build_crc_table();
+	/* Process each byte in the buffer. */
+	for (i = 0; i < buffersize; i++) {
+		temp1 = (crc >> 8) & 0x00FFFFFF;
+		temp2 = crc_table[(crc ^ (u32) * current_byte) & (u32) 0xff];
+		current_byte++;
+		crc = temp1 ^ temp2;
+	}
+	return crc;
+}
+
+EXPORT_SYMBOL(evms_cs_calculate_crc);
+
+/**
+ * evms_end_io
+ * @bio:	newly IO completed bio
+ *
+ * This is a support function for evms_cs_register_for_end_io_notification.
+ * This function is called during I/O completion on any bio that had its
+ * completion callback hooked by a plugin. Control is passed here
+ * and this routine will, thru the use of the I/O notify entry stored 
+ * in the bi_private field of the bio, restore the bi_rsector value and
+ * the bi_bdev value to the value bio had at the time of hook registration 
+ * and passes control to the registered callback_function, with pointers 
+ * to the bio and an optional plugin private data. Upon completion of the 
+ * callback_function, control is returned back here. The io notify list 
+ * entry is deleted. This process repeats until this routine detects that 
+ * all I/O notify entries registered by plugins have been called back and
+ * the bio's original end_io function has been called. At this point the 
+ * DONE flag is set, and we terminate the callback loop and exit.
+ *
+ * Plugins may desire to break or interrupt the callback sequence or chain. 
+ * This may be useful to redrive I/O or to wait for other bios to complete 
+ * before allowing the original bio callback to occur. To interrupt the
+ * callback "chain", a registered plugin's callback_function must return
+ * with the DONE flag set.
+ *
+ * NOTE: If a plugin sets the DONE flag, and wishes to redrive a bio, the
+ * plugin MUST reregister the bio to receive another callback on this bio.
+ * Also, the plugin MUST ensure that the original bio end_io function gets
+ * called at some point, either by reregistering this bio and receiving
+ * another callback, or by means of bio aggregation triggered by the callbacks
+ * of other bios.
+ */
+static int
+evms_end_io(struct bio *bio,
+	    unsigned int bytes_done,
+	    int err)
+{
+	struct evms_io_notify *entry;
+	int done;
+
+	if (bio->bi_size)
+		return 1;
+
+	done = 0;
+	while (!done) {
+		entry = (struct evms_io_notify *) bio->bi_private;
+		/* 
+		 * restore original values
+		 */
+		bio->bi_private = entry->b_private;
+		bio->bi_bdev = entry->bdev;
+		bio->bi_sector = entry->rsector;
+
+		if (entry->flags & EVMS_ORIGINAL_CALLBACK_FLAG) {
+			struct evms_logical_volume *lv;
+			lv = lookup_volume(minor(to_kdev_t(bio->bi_bdev->bd_dev)));
+			entry->flags &= ~EVMS_ORIGINAL_CALLBACK_FLAG;
+			if (atomic_dec_and_test(&lv->requests_in_progress) &&
+			    lv->quiesced) {
+				wake_up(&lv->quiesce_wait_queue);
+			}
+			bio->bi_end_io = (void *) entry->callback_function;
+			if (bio->bi_end_io) {
+				bio_endio(bio, bytes_done, err);
+			}
+			done = 1;
+		} else {
+			entry->callback_function(entry->private, bio, &done);
+		}
+		mempool_free(entry, evms_io_notify_pool);
+	}
+
+	return 0;
+}
+
+/**
+ * evms_cs_register_for_end_io_notification
+ * @private:		plugin private data
+ * @bio:		bio being hooked/registered for
+ * @callback_function:	plugin's callback function
+ *
+ * This routine allows a (plugin) function to register to participate
+ * in the io completion notification process. This is useful for plugins
+ * which alter data after it has been read from the disk (i.e. 
+ * encryption or compression).
+ *
+ * This routine also records the rsector and bdev values at the time of
+ * registration, so that they can be restored prior to the callback to
+ * a plugin, thus allowing that plugin to work with the values it had seen
+ * during its I/O request processing.
+ *
+ * This routine also records a private data pointer at the time of 
+ * registration that is returned to the plugin at callback time. This 
+ * private data pointer frees the plugin from having to create a place to
+ * store private data and later find that private data at the time of the
+ * callback. This field is not used by this function and is optional (NULL
+ * if unused). It is recorded and returned as a convenience for the plugins.
+ *
+ * DANGER!!! - WILL ROBINSON - DANGER!!!
+ * This routine uses the bi_private field in the bio structure. If any lower
+ * level driver uses this field and do NOT save and restore it, the I/O 
+ * callback will fail!!
+ */
+int
+evms_cs_register_for_end_io_notification(void *private,
+					 struct bio *bio,
+					 void *callback_function)
+{
+	int done;
+	struct evms_io_notify *new_entry;
+
+	done = 0;
+	while (!done) {
+		new_entry = mempool_alloc(evms_io_notify_pool, GFP_NOIO);
+		if (!new_entry) {
+			schedule();
+			continue;
+		}
+
+		new_entry->private = private;
+		new_entry->bio = bio;
+		new_entry->rsector = bio->bi_sector;
+		new_entry->bdev = bio->bi_bdev;
+		new_entry->b_private = bio->bi_private;
+		new_entry->flags = 0;
+
+		if (bio->bi_end_io != evms_end_io) {
+			struct evms_logical_volume *lv;
+			new_entry->flags |= EVMS_ORIGINAL_CALLBACK_FLAG;
+			lv = lookup_volume(minor(to_kdev_t(bio->bi_bdev->bd_dev)));
+			new_entry->callback_function = (void *) bio->bi_end_io;
+			atomic_inc(&lv->requests_in_progress);
+			bio->bi_end_io = evms_end_io;
+		} else {
+			new_entry->callback_function = callback_function;
+			done = 1;
+		}
+		bio->bi_private = new_entry;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_register_for_end_io_notification);
+
+/**
+ * evms_cs_signal_event - notify a PID of an event occurrence
+ * @eventid:	id of event to signal occurrence of
+ *
+ * Signal any registered PIDs that event corresponding to eventid has occurred.
+ **/
+void
+evms_cs_signal_event(int eventid)
+{
+	int rc;
+	struct evms_kevent *kevent;
+
+	/* signal PID(s) of specified event */
+	list_for_each_entry(kevent, &evms_notify_list, list) {
+		struct evms_event *event = &kevent->uevent;
+		if (event->eventid == eventid) {
+			struct task_struct *tsk;
+
+			tsk = find_task_by_pid(event->pid);
+			if (tsk) {
+				struct siginfo siginfo;
+
+				siginfo.si_signo = event->signo;
+				siginfo.si_errno = 0;
+				siginfo.si_code = 0;
+				rc = send_sig_info(event->signo, &siginfo, tsk);
+			} else {
+				/* TODO:
+				 * unregister this stale
+				 * notification record
+				 */
+			}
+		}
+	}
+}
+
+EXPORT_SYMBOL(evms_cs_signal_event);
+
+/**
+ * evms_flush_signals
+ *
+ * Flushes pending signals for the current process
+ **/
+static inline void
+evms_flush_signals(void)
+{
+	spin_lock(&current->sig->siglock);
+	flush_signals(current);
+	spin_unlock(&current->sig->siglock);
+}
+
+/**
+ * evms_init_signals
+ *
+ * Initialize the signal set for the current process
+ **/
+static inline void
+evms_init_signals(void)
+{
+	current->exit_signal = SIGCHLD;
+	siginitsetinv(&current->blocked, sigmask(SIGKILL));
+}
+
+/**
+ * evms_kernel_thread - generic evms kernel thread templace
+ * @arg:	thread structure
+ *
+ * Generic evms kernel thread template
+ **/
+static int
+evms_kernel_thread(void *arg)
+{
+	struct evms_thread *thread = arg;
+	lock_kernel();
+
+	/* Detach thread */
+	daemonize();
+
+	sprintf(current->comm, thread->name);
+	evms_init_signals();
+	evms_flush_signals();
+	thread->tsk = current;
+
+	unlock_kernel();
+
+	complete(thread->event);
+	while (thread->run) {
+		void (*run) (void *data);
+		DECLARE_WAITQUEUE(wait, current);
+
+		add_wait_queue(&thread->wqueue, &wait);
+		set_task_state(current, TASK_INTERRUPTIBLE);
+		if (!test_bit(EVMS_THREAD_WAKEUP, &thread->flags)) {
+			schedule();
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&thread->wqueue, &wait);
+		clear_bit(EVMS_THREAD_WAKEUP, &thread->flags);
+
+		run = thread->run;
+		if (run) {
+			run(thread->data);
+			blk_run_queues();
+		}
+		if (signal_pending(current)) {
+			evms_flush_signals();
+		}
+	}
+	complete(thread->event);
+	return 0;
+}
+
+/**
+ * evms_cs_register_thread
+ * @run:	function to be run by the kernel thread
+ * @data:	argument for function
+ * @name:	name for kernel thread
+ *
+ * Creates a kernel thread named @name, that invokes the function @run
+ * which accepts the argument @data.
+ *
+ * returns: ptr to thread control structure on success
+ *          NULL on error
+ **/
+struct evms_thread *
+evms_cs_register_thread(void (*run) (void *), void *data, const u8 * name)
+{
+	struct evms_thread *thread;
+	int ret;
+	struct completion event;
+
+	thread = kmalloc(sizeof (struct evms_thread), GFP_KERNEL);
+	if (!thread)
+		return NULL;
+
+	memset(thread, 0, sizeof (struct evms_thread));
+	init_waitqueue_head(&thread->wqueue);
+
+	init_completion(&event);
+	thread->event = &event;
+	thread->run = run;
+	thread->data = data;
+	thread->name = name;
+	ret = kernel_thread(evms_kernel_thread, thread, 0);
+	if (ret < 0) {
+		kfree(thread);
+		return NULL;
+	}
+	wait_for_completion(&event);
+	return thread;
+}
+
+EXPORT_SYMBOL(evms_cs_register_thread);
+
+/**
+ * evms_cs_unregister_thread
+ * @thread:	thread control structure (created when thread was registered)
+ *
+ * Destroys the specified kernel thread.
+ **/
+void
+evms_cs_unregister_thread(struct evms_thread *thread)
+{
+	struct completion event;
+
+	init_completion(&event);
+
+	thread->event = &event;
+	thread->run = NULL;
+	thread->name = NULL;
+	evms_cs_interrupt_thread(thread);
+	wait_for_completion(&event);
+	kfree(thread);
+}
+
+EXPORT_SYMBOL(evms_cs_unregister_thread);
+
+/**
+ * evms_cs_wakeup_thread
+ * @thread:	thread control structure of thread to wake up
+ *
+ * Causes the kernel thread to wake up and run.
+ **/
+void
+evms_cs_wakeup_thread(struct evms_thread *thread)
+{
+	set_bit(EVMS_THREAD_WAKEUP, &thread->flags);
+	wake_up(&thread->wqueue);
+}
+
+EXPORT_SYMBOL(evms_cs_wakeup_thread);
+
+/**
+ * evms_cs_interrupt_thread
+ * @thread:	thread control structure of thread to interrupt
+ *
+ * Signals a kernel thread.
+ **/
+void
+evms_cs_interrupt_thread(struct evms_thread *thread)
+{
+	if (!thread->tsk) {
+		LOG_ERROR("error: attempted to interrupt an invalid thread!\n");
+		return;
+	}
+	send_sig(SIGKILL, thread->tsk, 1);
+}
+
+EXPORT_SYMBOL(evms_cs_interrupt_thread);
+
+/**
+ * evms_cs_get_evms_proc_dir
+ *
+ * Retrieves the EVMS proc dir entry.
+ **/
+struct proc_dir_entry *
+evms_cs_get_evms_proc_dir(void)
+{
+	if (!evms_proc_dir) {
+		evms_proc_dir = create_proc_entry("evms", S_IFDIR, &proc_root);
+	}
+	return evms_proc_dir;
+}
+
+EXPORT_SYMBOL(evms_cs_get_evms_proc_dir);
+
+/**
+ * evms_cs_volume_request_in_progress - get/change a volume's request in progress count
+ * @dev:
+ * @operation:		input, > 0 = inc count, < 0 = dec count, 0 = query count
+ * @current_count:	output, current count
+ *
+ * Query or change a volume's request in progress count.
+ *
+ * returns: 0 = success
+ *          -ENODEV if dev points to a non-existent volume
+ **/
+int
+evms_cs_volume_request_in_progress(kdev_t dev,
+				   int operation, int *current_count)
+{
+	struct evms_logical_volume *volume = lookup_volume(minor(dev));
+	if (!volume || !volume->node) {
+		return -ENODEV;
+	}
+	if (operation > 0) {
+		atomic_inc(&volume->requests_in_progress);
+	} else if (operation < 0) {
+		if (atomic_dec_and_test(&volume->requests_in_progress) &&
+		    volume->quiesced) {
+			wake_up(&volume->quiesce_wait_queue);
+		}
+	}
+	if (current_count) {
+		*current_count = atomic_read(&volume->requests_in_progress);
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_volume_request_in_progress);
+
+/**
+ * evms_cs_invalidate_volume - invalidate a volume's cache blocks
+ * @node:		top-most storage object in a volume
+ *
+ * Invalidates the resources (inodes, cache blocks, etc) used by this volume.
+ **/
+void
+evms_cs_invalidate_volume(struct evms_logical_node *node)
+{
+	struct evms_logical_volume *lv = NULL;
+	while ((lv = find_next_volume(lv))) {
+		if (!lv->node || !node->name) {
+			continue;
+		}
+		if (strcmp(lv->node->name, node->name)) {
+			continue;
+		}
+		LOG_DETAILS("Invalidating EVMS device %s minor %d\n",
+			    node->name, lv->minor);
+		invalidate_device(mk_kdev(EVMS_MAJOR, lv->minor), 0);
+		break;
+	}
+}
+
+EXPORT_SYMBOL(evms_cs_invalidate_volume);
+
+/**
+ * evms_bio_collector - manages split bio and real bio I/O completions
+ * @bio:	a split bio
+ *
+ * Collects all the split bio end_io's and when all have completed, calls
+ * the original bio's end_io, and setting the uptodate flag accordingly.
+ **/
+int
+evms_bio_collector(struct bio *bio,
+		   unsigned int bytes_done,
+		   int err)
+{
+	struct bio_split_cb *evms_bio_split_record;
+
+	if (bio->bi_size)
+		return 1;
+
+	evms_bio_split_record = (struct bio_split_cb *) bio->bi_private;
+	if (err || !test_bit(BIO_UPTODATE, &bio->bi_flags)) {
+		evms_bio_split_record->rc = -EIO;
+	}
+	mempool_free(bio, evms_bio_split_record->bio_pool);
+	atomic_dec(&evms_bio_split_record->outstanding_bios);
+	if (!atomic_read(&evms_bio_split_record->outstanding_bios)) {
+		int rc = evms_bio_split_record->rc;
+		bio = evms_bio_split_record->original_bio;
+		if (!rc) {
+			set_bit(BIO_UPTODATE, &bio->bi_flags);
+		}
+		mempool_free(evms_bio_split_record, evms_bio_split_record->split_pool);
+		bio_endio(bio, bio->bi_size, rc);
+	}
+
+	return 0;
+}
+
+/**
+ * Convenience macro for initializing the bio split control block.
+ **/
+#define EVMS_SPLIT_CB_INIT(bio, split_rec, b_pool, s_pool) \
+	(split_rec)->rc = 0; \
+	(split_rec)->outstanding_bios = (atomic_t)ATOMIC_INIT(1); \
+	(split_rec)->original_bio = bio; \
+	(split_rec)->bio_pool = b_pool; \
+	(split_rec)->split_pool = s_pool;		
+
+/**
+ * Convenience macro for initializing a new bio.
+ **/
+#define EVMS_BIO_INIT(bio, split_rec) \
+	bio_init(bio); \
+       	(bio)->bi_end_io = evms_bio_collector; \
+	(bio)->bi_private = (split_rec);
+
+/**
+ * evms_bio_vcnt - computes the correct number of bio_vectors needed for a piece of storage
+ * @addr:	virtual address of storage
+ * @size:	size of storage
+ *
+ * Computes the correct number of required bio vectors, with respect to page
+ * size and alignment, for given a memory object starting address and size.
+ *
+ * returns: the count of bio vectors required.
+ **/
+static inline int
+evms_bio_vcnt(ulong addr, int size)
+{
+	int vcnt, offset, lskew, rskew;
+
+	vcnt = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+	rskew = size & (PAGE_SIZE - 1);
+	offset = addr & (PAGE_SIZE - 1);
+	lskew = PAGE_SIZE - offset;
+	if (lskew < rskew) {
+		vcnt++;
+	}
+	return vcnt;
+}
+
+/**
+ * evms_cs_split_bio - split a bio into two smaller bios.
+ * @source_bio:		the original bio
+ * @target_size:	the size of the target bio
+ * @target_bio:		the resulting target bio
+ * @remainder_bio:	the bio of the remaining data
+ *
+ * Split the source bio into a target bio, of a specified size, and remainder
+ * bio of the remaining size. The caller can then submit the target bio and if
+ * necessary split the remaining bio down futher with subsequent calls to this
+ * function. This function handles allocating and initializing the target bio
+ * & remainder bios as well as the bio split control block. All split bio's end
+ * io will pass control to the bio collector function which will collect the
+ * end ios until all the splits have completed and will then call the original
+ * bio's end io.
+ *
+ * returns: 0 on sucess, a target bio, a remainder bio
+ *	    error code on error
+ **/
+int
+evms_cs_split_bio(struct bio *source_bio,
+		  u64 target_size,
+		  struct bio **target_bio, struct bio **remainder_bio,
+		  mempool_t *my_bio_pool, mempool_t *my_bio_split_pool)
+{
+	int rc = 0;
+	struct bio_split_cb *evms_bio_split_record = NULL;
+
+	/* 
+	 * validate split target_size
+	 */
+	if (target_size >= source_bio->bi_size) {
+		rc = -EINVAL;
+		LOG_ERROR("error(%d): unable to split bio(size:%d) "
+			  "on specified boundary("PFU64").\n",
+			  rc, source_bio->bi_size, target_size);
+		return rc;
+	}
+
+	/*
+	 * allocate target bio and bio split record if needed
+	 */
+	if (source_bio->bi_end_io != evms_bio_collector) {
+		*target_bio = mempool_alloc(my_bio_pool, GFP_NOIO);
+		evms_bio_split_record =
+		    mempool_alloc(my_bio_split_pool, GFP_NOIO);
+		EVMS_SPLIT_CB_INIT(source_bio, evms_bio_split_record,
+				    my_bio_pool, my_bio_split_pool);
+		EVMS_BIO_INIT(*target_bio, evms_bio_split_record);
+		(*target_bio)->bi_rw = source_bio->bi_rw;
+		(*target_bio)->bi_bdev = source_bio->bi_bdev;
+	} else {
+		*target_bio = source_bio;
+		evms_bio_split_record =
+		    (struct bio_split_cb *) source_bio->bi_private;
+	}
+
+	/*
+	 * allocate remainder bio
+	 */
+	*remainder_bio = mempool_alloc(my_bio_pool, GFP_NOIO);
+	atomic_inc(&evms_bio_split_record->outstanding_bios);
+	EVMS_BIO_INIT(*remainder_bio, evms_bio_split_record);
+	(*remainder_bio)->bi_rw = source_bio->bi_rw;
+	(*remainder_bio)->bi_bdev = source_bio->bi_bdev;
+
+	/*
+	 * build the io vecs for target and remainder
+	 */
+	{
+		int remaining_bytes, i, src_vcnt, cur_vcnt;
+		struct bio_vec *src_io_vec, *cur_io_vec;
+
+		remaining_bytes = target_size;
+		src_io_vec = &source_bio->bi_io_vec[0];
+		cur_io_vec = &(*target_bio)->bi_io_vec[0];
+		src_vcnt = source_bio->bi_vcnt;
+		for (i = cur_vcnt = 0; i < src_vcnt; i++, src_io_vec++) {
+			unsigned int src_len = src_io_vec->bv_len;
+
+			cur_io_vec->bv_offset = src_io_vec->bv_offset;
+			cur_io_vec->bv_page = src_io_vec->bv_page;
+			if ((remaining_bytes <= 0)
+			    || (remaining_bytes > src_len)) {
+				cur_io_vec->bv_len = src_len;
+			} else {
+				unsigned int trg_len = remaining_bytes;
+
+				cur_io_vec->bv_len = remaining_bytes;
+				cur_vcnt++;
+				(*target_bio)->bi_vcnt = cur_vcnt;
+
+				cur_vcnt = remaining_bytes = 0;
+				cur_io_vec = &(*remainder_bio)->bi_io_vec[0];
+				if (!(src_len - trg_len))
+					continue;
+				cur_io_vec->bv_len = src_len - trg_len;
+				cur_io_vec->bv_offset =
+				    src_io_vec->bv_offset + trg_len;
+				cur_io_vec->bv_page = src_io_vec->bv_page;
+			}
+			cur_vcnt++;
+			remaining_bytes -= cur_io_vec->bv_len;
+			cur_io_vec++;
+		}
+		(*remainder_bio)->bi_vcnt = cur_vcnt;
+	}
+	/*
+	 * update the sector and size fields in the
+	 * target and remainder bio.
+	 */
+	(*remainder_bio)->bi_size = source_bio->bi_size - target_size;
+	(*remainder_bio)->bi_sector = source_bio->bi_sector;
+	(*remainder_bio)->bi_sector += target_size >> EVMS_VSECTOR_SIZE_SHIFT;
+	(*target_bio)->bi_size = target_size;
+	(*target_bio)->bi_sector = source_bio->bi_sector;
+
+	return rc;
+}
+
+EXPORT_SYMBOL(evms_cs_split_bio);
+

  parent reply	other threads:[~2002-10-10 20:09 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2002-10-10 19:30 [PATCH] EVMS core (0/9) Kevin Corry
2002-10-10 19:35 ` [PATCH] EVMS core (1/9) evms.c Kevin Corry
2002-10-10 19:35 ` Kevin Corry [this message]
2002-10-10 19:35 ` [PATCH] EVMS core (3/9) discover.c Kevin Corry
2002-10-10 19:37 ` [PATCH] EVMS core (4/9) passthru.c Kevin Corry
2002-10-10 19:37 ` [PATCH] EVMS core (5/9) evms_core.h Kevin Corry
2002-10-10 19:37 ` [PATCH] EVMS core (6/9) evms.h Kevin Corry
2002-10-10 19:39 ` [PATCH] EVMS core (7/9) evms_ioctl.h Kevin Corry
2002-10-10 19:39 ` [PATCH] EVMS core (8/9) evms_biosplit.h Kevin Corry
2002-10-10 19:39 ` [PATCH] EVMS core (9/9) misc Kevin Corry

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=02101014351404.17770@boiler \
    --to=corryk@us.ibm.com \
    --cc=evms-devel@lists.sourceforge.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@transmeta.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox