⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scsi.c

📁 create raid tool at linux
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * scsi.c * * Copyright (C) 2001, Red Hat, Inc. * * 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, 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; see the file COPYING.  If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. *  * 08/27/01 - Initial version snagged by Doug Ledford from the scsires * 		package. */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>#include <errno.h>#include <unistd.h>#include <sys/types.h>#include <sys/ioctl.h>#include <scsi/scsi.h>#include <scsi/sg.h>#include <scsi/scsi_ioctl.h>#include "scsi.h"/* * Function declarations that are for internal functions and therefore not * included in the scsires.h header file. */static int scsi_get_device_id_page(scsi_sg_dev_t *);static int scsi_get_serial_number_page(scsi_sg_dev_t *);/* * Now to the functions *//* * Function: scsi_print_device_info * * Inputs: *	scsi_sg_dev_t * - A pointer to a device structure.  Device *		information contained in this structure will be used *		to print out a description of the device in question. * * Outputs: *	None, except to stdout * * Purpose: Print out some basic information about a device for debugging *	use. */voidscsi_print_device_info(scsi_sg_dev_t * sg_dev){    if (!sg_dev)	return;    printf("\nName:\t\t%s\nModel:\t\t%s %s %s\nSCSI Rev:\t%d\n"	   "Device ID:\t%d:%d:%d:%d\nBlock Size:\t%d bytes\n"	   "Total Size:\t%d MBytes\n",	   sg_dev->name, sg_dev->vendor, sg_dev->product,	   sg_dev->revision, sg_dev->scsi_rev, sg_dev->scsi_dev_host,	   sg_dev->scsi_dev_bus, sg_dev->scsi_dev_id, sg_dev->scsi_dev_lun,	   sg_dev->block_size,	   (sg_dev->block_size * (sg_dev->num_blocks / 1024) / 1024));    if (sg_dev->fd != -1) {	if (sg_dev->sg_name)	    printf("SG Name:\t%s\n", sg_dev->sg_name);    }}/* * Function: scsi_init_sg_device * * Inputs: *	int - This is a file descriptor that points to a disk device *		that the programmer wishes to place a SCSI reservation on. *	const char * - This is a pointer to a string that should be *		the printable name of the device (possibly as passed in *		on the command line) * Outputs: *	struct scsi_sg_dev* - A pointer to a newly allocated structure *		is returned on success and on partial success.  A NULL pointer *		is returned on permanent failure.  Partial success is defined *		as any condition that causes an otherwise init'able drive *		to fail to init for temporary conditions (such as we found *		all the needed devices and started to set up the device struct *		and then found out that we are getting I/O errors on most *		commands because someone else already has a reservation on the *		device).  A calling function should check the returned sg_dev *		struct to see if initialized == TRUE.  If it does, then the *		setup was complete.  If it doesn't, then the setup was only *		partial and the device is not yet ready for use.  The calling *		code should then be ready to recall this function later when *		it has some reason to believe the code would actually succeed. *		After a fully init'ed sg_dev struct is returned, the partially *		init'ed sg_dev struct should be free()'d by the calling code. * * Purpose: Initialize an sg device for use by this code.  Once the sg *	device that corresponds to disk_fd has been found, this function will *	issue a couple test commands to the device to test its capabilities, *	and will then fill out a newly allocated scsi_sg_dev struct *	with the information it finds.  Specifically, it will fill in the *	following items with the information it finds: * *	fd - the new fd that points to the sg device will be put here *	disk_fd - the original fd will be put here *	scsi_rev - the SCSI revision of the device will be filled in (we *		only support revision 2 or 3 devices at the moment) *	block_size - the block size of the device as the *device* says it is, *		not the block size that the block device layer reports.  It *		is imperative that the user not confuse this number with *		anything else when making reservations.  The OS may claim *		that a device has a 4K block size when using a 4K *		filesystem, but if the device is actually using 512byte blocks, *		then we *MUST* use that number when calculating block offsets *		or else the reservation will be in the wrong place. *	scsi_dev_{id,lun,bus,host} - these will be filled in with the values *		that actually belong to this device * *	If the function is unable to access the sg devices (the sg driver is *	not present in the kernel) or is unable to match any sg device to *	the passed in disk_fd device, then a NULL pointer will be returned *	instead of a pointer to a filled out structure.  (Other mundane *	errors may also result in a NULL pointer return) */extern scsi_sg_dev_t *scsi_init_sg_device(int disk_fd, const char *argv){    scsi_sg_dev_t *sg_dev;    unsigned int int_array[3], i, fd;    scsi_send_command_t cmd;    char buffer[128];    if ((sg_dev = malloc(sizeof(scsi_sg_dev_t))) == NULL) {	printf("Unable to allocate memory for sg_dev\n");	return (NULL);    }    memset((void *)sg_dev, 0, sizeof(struct scsi_sg_dev));    sg_dev->fd = -1;    sg_dev->disk_fd = disk_fd;    sg_dev->name = (char *)argv;    sg_dev->initialized = FALSE;    /*     * Get the device ID parameters from the linux kernel for the disk     * device (aka, /dev/sda or whatever was passed in on the command     * line or config file).  These numbers are unique for any given     * device.     */    if ((ioctl(disk_fd, SCSI_IOCTL_GET_IDLUN, &int_array[0]) == -1) ||	(ioctl(disk_fd, SCSI_IOCTL_GET_BUS_NUMBER, &int_array[2]) == -1)) {	perror("scsi_init_sg_device: "	       "Unable to get ID, LUN, bus, or host of drive");	scsi_release_sg_device(sg_dev);	return (NULL);    }    sg_dev->scsi_dev_id = int_array[0] & 0xff;    sg_dev->scsi_dev_lun = (int_array[0] >> 8) & 0xff;    sg_dev->scsi_dev_bus = (int_array[0] >> 16) & 0xff;    sg_dev->scsi_dev_host = int_array[2];    /*     * Open the /dev/sg? entries one at a time and check each of them     * to see if the device ID values match the device ID values we have     * on our disk device.  If they do, then we've found the /dev/sg entry     * that matches our /dev/sd entry.  If we run out of entries, then we     * can't go any further reliably.     */    i = 0;    sprintf(buffer, "/dev/sg%d", i++);    while ((fd = open(buffer, O_RDWR | O_NDELAY)) != -1) {	if ((ioctl(fd, SCSI_IOCTL_GET_IDLUN, &int_array[0]) == -1) ||	    (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &int_array[2]) == -1)) {	    perror(buffer);	    close(fd);	    sprintf(buffer, "/dev/sg%d", i++);	    continue;	}	if ((sg_dev->scsi_dev_id == (int_array[0] & 0xff)) &&	    (sg_dev->scsi_dev_lun == ((int_array[0] >> 8) & 0xff)) &&	    (sg_dev->scsi_dev_bus == ((int_array[0] >> 16) & 0xff)) &&	    (sg_dev->scsi_dev_host == int_array[2])) {	    sg_dev->fd = fd;	    sg_dev->sg_name = malloc(strlen(buffer) + 2);	    if (sg_dev->sg_name != NULL)		sprintf((void *)sg_dev->sg_name, "%s", buffer);	    break;	}	close(fd);	sprintf(buffer, "/dev/sg%d", i++);    }    if (sg_dev->fd == -1) {	printf("scsi_init_sg_device: unable to find the matching "	       "sg device\nto go with the target disk device %s\n",	       sg_dev->name);	scsi_release_sg_device(sg_dev);	return (NULL);    }    /*     * Be prepared to do the INQUIRY twice incase this is our first     * command since a reset.  We might have to absorb one failed     * INQUIRY due to a UNIT_ATTENTION sense return from the device.     */    do {	cmd.outsize = 0;	cmd.insize = 56;	memset(cmd.buf, 0, 56);	cmd.buf[0] = INQUIRY;	cmd.buf[4] = 56;	if (SEND_COMMAND(sg_dev, cmd) == -1) {	    perror("failed on INQUIRY");	    return (sg_dev);	}    } while (scsi_retryable_error(sg_dev, cmd, FALSE, DEF_WAIT));    if (!WAS_OK(cmd)) {	printf("%s: scsi error on INQUIRY, status byte = 0x%x\n",	       sg_dev->name, STATUS(cmd));	return (sg_dev);    }    /*     * Make sure the device meets all of our "strict" requirements.  It     * must be a disk drive (Direct Access medium, could actually be     * a Direct Access tape drive, but if you start putting reservations     * on one of those then what happens is your own fault), may not be     * a removable medium drive, and the peripheral qualifier returned     * from the INQUIRY command must indicate that this LUN is connected     * to the drive.     */    if ((cmd.buf[0] & 0x1f) != 0) {	printf("%s: non-disk devices are not supported\n", sg_dev->name);	scsi_release_sg_device(sg_dev);	return (NULL);    }    if ((cmd.buf[0] & 0xe0) != 0) {	printf("%s: this device is not currently ready for use\n",	       sg_dev->name);	scsi_release_sg_device(sg_dev);	return (NULL);    }    if (cmd.buf[1] & 0x80) {	printf("%s: removable devices are not supported\n", sg_dev->name);	scsi_release_sg_device(sg_dev);	return (NULL);    }    /*     * Grab the needed info from the INQUIRY results.     */    sg_dev->scsi_rev = cmd.buf[2] & 0x7;    memcpy((void *)&sg_dev->vendor[0], (const void *)&cmd.buf[8], 8);    memcpy((void *)&sg_dev->product[0], (const void *)&cmd.buf[16], 16);    memcpy((void *)&sg_dev->revision[0], (const void *)&cmd.buf[32], 4);    sg_dev->vendor[8] = '\0';    sg_dev->product[16] = '\0';    sg_dev->revision[5] = '\0';    /*     * First try the device ID page.  Then get the serial number.     * One of the two should provide reasonable information.     */    scsi_get_device_id_page(sg_dev);    scsi_get_serial_number_page(sg_dev);    /*     * Good, we've passed everything now, so mark the device as init'ed     * and send it back to the calling function.     */    sg_dev->initialized = TRUE;    return (sg_dev);}/* * Function: scsi_get_device_id_page * * Inputs: * 	scsi_sg_dev_t * - The sg_dev we want to get size information for * * Outputs: * 	int - 0 on success, 1 on any error or failure. * * Purpose: * 	Used to provide a unique identifier to each device.  This can then * 	be used to detect multipath drives on the fly.  This is the preferred * 	way to get the unique identifier.  However, when this isn't supported * 	and the serial number page is, then we use it as a backup. */static intscsi_get_device_id_page(scsi_sg_dev_t *sg_dev){    scsi_send_command_t cmd;    int i;    /*     * Get the Device Identification page from another INQUIRY command     */    do {	cmd.outsize = 0;	cmd.insize = 255;	memset(cmd.buf, 0, 255);	cmd.buf[0] = INQUIRY;	cmd.buf[1] = 1; /* EVPD bit */	cmd.buf[2] = 0x83; /* Device ID Page */	cmd.buf[4] = 255;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -