view driver/cdev_handler.c @ 0:42f240cc4bc6

From: 太田 篤志 <atoc@namikilab.tuat.ac.jp>
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 08 Sep 2009 13:44:18 +0900
parents
children
line wrap: on
line source

/**
 * cdev_handler.c
 * SPE プロセスマネージャ spe_manager
 * キャラクタデバイスのハンドラ
 */

#include <linux/module.h>      // カーネルモジュール全般
#include <linux/fs.h>          // struct inode, struct file
#include <linux/kernel.h>      // printk
//#include <linux/slab.h>        // kmalloc, kfree
#include <linux/vmalloc.h>     // vmalloc, vfree
#include <linux/semaphore.h>   // down_interruptible, up
#include <linux/spinlock.h>    // spin_lock, spin_unlock
#include <asm/uaccess.h>       // copy_from_user, copy_to_user

#include "../include/spe_process.h"
#include "../include/ioctl.h"
#include "cdev_handler.h"
#include "lspe.h"
#include "main.h"
#include "critical.h"
#include "process_list.h"


// SPE プロセス ID 割り当て用 (open ハンドラが実行されるたびに加算される)
static int spe_pid = 0;



//=================================================================================================== spe_manager_open()
/**
 * spe_manager_open
 * キャラクタデバイス open イベントハンドラ
 *
 * @param  struct inode *  inode
 * @param  struct file  *  filp
 * @return int
 */
int spe_manager_open(struct inode *inode, struct file *filp)
{
	spe_process_context_list_t *this_process;
	struct semaphore           *semaphore_for_spe_pid = get_semaphore_for_spe_pid();
	struct semaphore           *semaphore_for_process_list = get_semaphore_for_process_list();


	// SPE プロセスコンテキスト用のメモリを確保 (確保するまでにスリープするかもしれない)
	if ((this_process = (spe_process_context_list_t *)vmalloc(sizeof(spe_process_context_list_t))) == NULL)
	{
		printk(KERN_ERR "[%s] Error: vmalloc() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
		return -ENOSPC;
	}

	// SPE プロセスコンテキストの初期化
	this_process->context.read.status = SPE_PROCESS_CONTEXT_STATUS_OPENED;
	this_process->prev_process        = NULL;
	this_process->next_process        = NULL;
//	this_process->context.read.spe_no = -1;   // 暫定


//	Critical Section >>>
	if (down_interruptible(semaphore_for_spe_pid))
		goto spe_manager_open__error1;

	// SPE プロセス ID 番号の割り当て
	if ((this_process->context.read.spe_pid = ++spe_pid) < 0)
	{
	}

	up(semaphore_for_spe_pid);
//	<<< Critical Section


//	Critical Section >>>
	if (down_interruptible(semaphore_for_process_list))
		goto spe_manager_open__error2;

		increment_opened();

	up(semaphore_for_process_list);
//	<<< Critical Section


	filp->private_data = (void *)this_process;
	return 0;


spe_manager_open__error2:
spe_manager_open__error1:
	return -ERESTARTSYS;
}


//================================================================================================== spe_manager_write()
/**
 * spe_manager_write
 * キャラクタデバイス write イベントハンドラ
 *
 * @param  struct file *  filp
 * @param  char        *  buf
 * @param  size_t         count
 * @param  loff_t      *  f_pos
 * @return ssize_t
 */
ssize_t spe_manager_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	spe_process_context_list_t *this_process = (spe_process_context_list_t *)(filp->private_data);
	struct semaphore           *semaphore_for_process_list = get_semaphore_for_process_list();


	if (this_process->context.read.status != SPE_PROCESS_CONTEXT_STATUS_OPENED &&
	     this_process->context.read.status != SPE_PROCESS_CONTEXT_STATUS_WRITTEN)
	{
		printk (
			KERN_ERR "[%s] Error: spe_manager_write() can't be executed in the status. (%s:%u)\n",
			SPE_MANAGER_MODULE_NAME,
			__FILE__,
			__LINE__
		);
		return -EFAULT;
	}

	//
	// Note: (1) 以降の処理が実行されるのは、
	//           SPE プロセスが OPENED 状態か WRITTEN 状態の時のみである。
	//       (2) OPENED → WRITTEN の状態遷移は、
	//           書き込みオフセットが spe_process_context_write_data_t のサイズぶん移動した際に行われる。
	//           fseek 等でオフセットを移動してしまうとデータが不足したまま WRITTEN 状態になるおそれがあるので注意。
	//

	if (*f_pos >= sizeof(spe_process_context_write_data_t))
	{
//		Critical Section >>>
		if (down_interruptible(semaphore_for_process_list))
			goto spe_manager_write__error1;

			decrement_opened();
			increment_written();

		up(semaphore_for_process_list);
//		<<< Critical Section

		// プロセス状態更新
		this_process->context.read.status = SPE_PROCESS_CONTEXT_STATUS_WRITTEN;
		return 0;
	}

	if (*f_pos + count > sizeof(spe_process_context_write_data_t))
		// そのまま転送するとサイズオーバーなので調節する
		count = sizeof(spe_process_context_write_data_t) - *f_pos;

	if (copy_from_user((void *)&(this_process->context.write) + *f_pos, buf, count))
	{
		printk(KERN_ERR "[%s] Error: copy_from_user() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
		return -EFAULT;
	}

	if ((*f_pos += count) >= sizeof(spe_process_context_write_data_t))
	{
//		Critical Section >>>
		if (down_interruptible(semaphore_for_process_list))
			goto spe_manager_write__error1;

			decrement_opened();
			increment_written();

		up(semaphore_for_process_list);
//		<<< Critical Section

		// プロセス状態更新
		this_process->context.read.status = SPE_PROCESS_CONTEXT_STATUS_WRITTEN;
	}

	return count;


spe_manager_write__error1:
	return -ERESTARTSYS;
}


//================================================================================================== spe_manager_ioctl()
/**
 * spe_manager_ioctl
 * キャラクタデバイス ioctl イベントハンドラ [要修正]
 *
 * @param  struct inode  *  inode
 * @param  struct file   *  filp
 * @param  unsigned int     cmd
 * @param  unsigned long    arg
 * @return int
 */
int spe_manager_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
	// マジック番号の判定
	if (_IOC_TYPE(cmd) != SPE_MANAGER_IOCTL_MAGIC) return -ENOTTY;

	switch (cmd)
	{
	// SPE プロセスを開始する
	case SPE_MANAGER_IOCTL_START_PROCESS:
		// データ転送方向判定
		if (_IOC_DIR(cmd) != _IOC_NONE) return -ENOTTY;

		{
			spe_process_context_list_t *this_process = (spe_process_context_list_t *)(filp->private_data);
			struct semaphore           *semaphore_for_process_list = get_semaphore_for_process_list();
			spinlock_t                 *spinlock_for_process_list  = get_spinlock_for_process_list();
			int i, lspe_count = get_lspe_count();


//			Critical Section (semaphore) >>>
			if (down_interruptible(semaphore_for_process_list))
				goto spe_manager_ioctl__error1;

				decrement_written();

//				Critical Section (spinlock) >>>
				spin_lock(spinlock_for_process_list);
				{
					for (i = 0; i < lspe_count; i++)
					{
						spe_process_context_list_t *running = get_running_process(i);

						// この論理 SPE でプロセスが実行中ではないか
						if (running == NULL)
						{
							// このプロセスを実行中にする
//							this_process->context.read.spe_no = i;
							this_process->context.read.status = SPE_PROCESS_CONTEXT_STATUS_RUNNING;

							set_running_process(i, this_process);
							break;
						}
					}
					if (i >= lspe_count)
					{
						// 空きの SPE がなかったので実行待ちプロセスリストへ追加する
						add_process_to_waiting(this_process);
					}
				}
				spin_unlock(spinlock_for_process_list);
//				<<< Critical Section (spinlock)

			up(semaphore_for_process_list);
//			<<< Critical Section (semaphore)


			// SPE プロセス開始
			if (i < lspe_count) start_spe_process(i, this_process);
		}

		break;

	// それ以外のコマンド
	default:
		return -ENOTTY;
	}

	return 0;


spe_manager_ioctl__error1:
	return -ERESTARTSYS;
}


//=================================================================================================== spe_manager_read()
/**
 * spe_manager_read
 * キャラクタデバイス read イベントハンドラ
 *
 * @param  struct file *  filp
 * @param  char        *  buf
 * @param  size_t         count
 * @param  loff_t      *  f_pos
 * @return ssize_t
 */
ssize_t spe_manager_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	spe_process_context_list_t *this_process = (spe_process_context_list_t *)(filp->private_data);


	if (*f_pos >= sizeof(spe_process_context_read_data_t))
		// これ以上転送しない
		return 0;

	if (*f_pos + count > sizeof(spe_process_context_read_data_t))
		// そのまま転送するとサイズオーバーなので調節する
		count = sizeof(spe_process_context_read_data_t) - *f_pos;

	if (copy_to_user(buf, (void *)&(this_process->context.read) + *f_pos, count))
	{
		printk(KERN_ERR "[%s] Error: copy_to_user() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
		return -EFAULT;
	}

	*f_pos += count;
	return count;
}


//================================================================================================ spe_manager_release()
/**
 * spe_manager_release
 * キャラクタデバイス release イベントハンドラ
 *
 * @param  struct inode *  inode
 * @param  struct file  *  filp
 * @return int
 */
int spe_manager_release(struct inode *inode, struct file *filp)
{
	spe_process_context_list_t *this_process = (spe_process_context_list_t *)(filp->private_data);
	struct semaphore           *semaphore_for_process_list = get_semaphore_for_process_list();
	spinlock_t                 *spinlock_for_process_list  = get_spinlock_for_process_list();


	//
	//
	// ToDo: SPE プロセスが NOT_RELEASED 状態でない時に
	//       突然このハンドラが呼び出された場合の後始末
	//
	//


//	Critical Section (semaphore) >>>
	if (down_interruptible(semaphore_for_process_list))
		goto spe_manager_release__error1;

//		Critical Section (spinlock) >>>
		spin_lock(spinlock_for_process_list);
		{
			decrement_not_released();
		}
		spin_unlock(spinlock_for_process_list);
//		<<< Critical Section (spinlock)

		increment_released();

	up(semaphore_for_process_list);
//	<<< Critical Section (semaphore)


	// SPE プロセスコンテキストのためのメモリを解放
	vfree(this_process);
	return 0;


spe_manager_release__error1:
	return -ERESTARTSYS;
}