[RFC PATCH] sg3_utils: Added hybrid information log utility
From: Jason B. Akers
Date: Wed Oct 29 2014 - 16:09:59 EST
From: Kapil Karkra <kapil.karkra@xxxxxxxxx>
A solid state hybrid drive (SSHD) is a hard drive with a small embedded
NAND. Host drivers can manage this NAND using the commands defined by
SATA 3.2 standard. One SATA command that provides visibility into the
SSHD's cache behavior is get hybrid log command. This augmentation
allows users to issue the command as follows:
sg_sat_get_hybrid_log /dev/sdc
assuming /dev/sdc is an SSHD.
patch applies to the following sourcebase from where to clone:
https://github.com/hreinecke/sg3_utils.git
Signed-off-by: Kapil Karkra <kapil.karkra@xxxxxxxxx>
Signed-off-by: Jason B. Akers <jason.b.akers@xxxxxxxxx>
---
src/Makefile.am | 21 ++
src/sg_sat_get_hybrid_log.c | 419 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 434 insertions(+), 6 deletions(-)
create mode 100644 src/sg_sat_get_hybrid_log.c
diff --git a/src/Makefile.am b/src/Makefile.am
index de14608..3f8e052 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,7 +14,8 @@ bin_PROGRAMS = \
sg_raw sg_rbuf sg_rdac sg_read sg_readcap sg_read_block_limits \
sg_read_buffer sg_read_long sg_reassign sg_referrals \
sg_requests sg_reset sg_rmsn sg_rtpg sg_safte sg_sat_identify \
- sg_sat_phy_event sg_sat_set_features sg_scan sg_senddiag sg_ses \
+ sg_sat_phy_event sg_sat_set_features sg_sat_get_hybrid_log \
+ sg_scan sg_senddiag sg_ses \
sg_start sg_stpg sg_sync sg_test_rwbuf sg_turs sg_unmap sg_verify \
sg_vpd sg_write_buffer sg_write_long sg_write_same sg_wr_mode \
sg_xcopy sg_copy_results
@@ -36,7 +37,8 @@ bin_PROGRAMS = \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
sg_read_buffer sg_read_long sg_reassign sg_referrals sg_requests \
sg_rmsn sg_rtpg sg_safte sg_sat_identify sg_sat_phy_event \
- sg_sat_set_features sg_scan sg_senddiag sg_ses sg_start sg_stpg \
+ sg_sat_set_features sg_sat_get_hybrid_log \
+ sg_scan sg_senddiag sg_ses sg_start sg_stpg \
sg_sync sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer \
sg_write_long sg_write_same sg_wr_mode
@@ -57,7 +59,8 @@ bin_PROGRAMS = \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
sg_read_buffer sg_read_long sg_reassign sg_referrals sg_requests \
sg_rmsn sg_rtpg sg_safte sg_sat_identify sg_sat_phy_event \
- sg_sat_set_features sg_scan sg_senddiag sg_ses sg_start sg_stpg \
+ sg_sat_set_features sg_sat_get_hybrid_log sg_scan sg_senddiag sg_ses \
+ sg_start sg_stpg \
sg_sync sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer \
sg_write_long sg_write_same sg_wr_mode
@@ -78,7 +81,8 @@ bin_PROGRAMS = \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
sg_read_buffer sg_read_long sg_reassign sg_referrals sg_requests \
sg_rmsn sg_rtpg sg_safte sg_sat_identify sg_sat_phy_event \
- sg_sat_set_features sg_senddiag sg_ses sg_start sg_stpg \
+ sg_sat_set_features sg_sat_get_hybrid_log \
+ sg_senddiag sg_ses sg_start sg_stpg \
sg_sync sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer \
sg_write_long sg_write_same sg_wr_mode
@@ -93,7 +97,8 @@ bin_PROGRAMS = \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
sg_read_buffer sg_read_long sg_reassign sg_referrals sg_requests \
sg_rmsn sg_rtpg sg_safte sg_sat_identify sg_sat_phy_event \
- sg_sat_set_features sg_senddiag sg_ses sg_start sg_stpg \
+ sg_sat_set_features sg_sat_get_hybrid_log \
+ sg_senddiag sg_ses sg_start sg_stpg \
sg_sync sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer \
sg_write_long sg_write_same sg_wr_mode
@@ -108,7 +113,8 @@ bin_PROGRAMS = \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
sg_read_buffer sg_read_long sg_reassign sg_referrals sg_requests \
sg_rmsn sg_rtpg sg_safte sg_sat_identify sg_sat_phy_event \
- sg_sat_set_features sg_senddiag sg_ses sg_start sg_stpg \
+ sg_sat_set_features sg_sat_get_hybrid_log sg_senddiag \
+ sg_ses sg_start sg_stpg \
sg_sync sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer \
sg_write_long sg_write_same sg_wr_mode
@@ -229,6 +235,9 @@ sg_sat_phy_event_LDADD = ../lib/libsgutils2.la @os_libs@
sg_sat_set_features_SOURCES = sg_sat_set_features.c
sg_sat_set_features_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_sat_get_hybrid_log_SOURCES = sg_sat_get_hybrid_log.c
+sg_sat_get_hybrid_log_LDADD = ../lib/libsgutils2.la @os_libs@
+
sg_scan_SOURCES = sg_scan.c
sg_scan_LDADD = ../lib/libsgutils2.la @os_libs@
diff --git a/src/sg_sat_get_hybrid_log.c b/src/sg_sat_get_hybrid_log.c
new file mode 100644
index 0000000..909984e
--- /dev/null
+++ b/src/sg_sat_get_hybrid_log.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2014 Kapil Karkra.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+
+static const char * version_str = "1.03 20130507";
+
+/* This program uses a ATA PASS-THROUGH SCSI command. This usage is
+ * defined in the SCSI to ATA Translation (SAT) drafts and standards.
+ * See http://www.t10.org for drafts. SAT is a standard: SAT ANSI INCITS
+ * 431-2007 (draft prior to that is sat-r09.pdf). SAT-2 is also a
+ * standard: SAT-2 ANSI INCITS 465-2010 and the draft prior to that is
+ * sat2r09.pdf . The SAT-3 project has started and the most recent draft
+ * is sat3r01.pdf .
+ */
+
+/* This program uses a ATA PASS-THROUGH (16 or 12) SCSI command defined
+ * by SAT to package an ATA READ LOG EXT (2Fh) command to fetch
+ * log page 14h. That page contains SATA hybrid information log.
+ * For ATA READ LOG EXT command see ATA-8/ACS at www.t13.org .
+ * For SATA Hybrid information log see SATA 3.2 .
+ *
+ * Invocation: see the usage() function below
+ */
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK comand */
+#define SAT_ATA_PASS_THROUGH12_LEN 12
+#define SAT_ATA_RETURN_DESC 9 /* ATA Return (sense) Descriptor */
+#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
+
+#define ATA_READ_LOG_EXT 0x2f
+#define SATA_HYBRID_INFO_LPAGE 0x14
+#define READ_LOG_EXT_RESPONSE_LEN 512
+
+#define DEF_TIMEOUT 20
+
+#define EBUFF_SZ 256
+
+static struct option long_options[] = {
+ {"ck_cond", no_argument, 0, 'c'},
+ {"extend", no_argument, 0, 'e'},
+ {"hex", no_argument, 0, 'H'},
+ {"ignore", no_argument, 0, 'i'},
+ {"len", no_argument, 0, 'l'},
+ {"raw", no_argument, 0, 'r'},
+ {"reset", no_argument, 0, 'R'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: "
+ "sg_sat_get_hybrid_log [--ck_cond] [--extend] [--help] [--hex] "
+ "[--ignore]\n"
+ " [--len=16|12] [--raw] [--reset] "
+ "[--verbose]\n"
+ " [--version] DEVICE\n"
+ " where:\n"
+ " --ck_cond|-c sets ck_cond bit in cdb (def: 0)\n"
+ " --extend|-e sets extend bit in cdb (def: 0)\n"
+ " --help|-h print this usage message then exit\n"
+ " --hex|-H output response in hex bytes, use twice for\n"
+ " hex words\n"
+ " --ignore|-i ignore identifier names, output id value "
+ "instead\n"
+ " --len=16|12 | -l 16|12 cdb length: 16 or 12 bytes "
+ "(default: 16)\n"
+ " --raw|-r output response in binary to stdout\n"
+ " --reset|-R reset counters (after read)\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string then exit\n\n"
+ "Sends an ATA READ LOG EXT command via a SAT pass through to "
+ "fetch\nlog page 14h which contains SATA Hybrid Information Log\n");
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+ int k;
+
+ for (k = 0 ; k < len; ++k)
+ printf("%c", str[k]);
+}
+
+/* ATA READ LOG EXT command [2Fh, PIO data-in] */
+/* N.B. "log_addr" is the log page number, "page_in_log" is usually zero */
+static int
+do_read_log_ext(int sg_fd, int log_addr, int page_in_log, int feature,
+ int blk_count, void * resp, int mx_resp_len, int cdb_len,
+ int ck_cond, int extend, int do_hex, int do_raw, int verbose)
+{
+ int ok, res, ret;
+ int protocol = 4; /* PIO data-in */
+ int t_dir = 1; /* 0 -> to device, 1 -> from device */
+ int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+ int t_length = 2; /* 0 -> no data transferred, 2 -> sector count */
+ int resid = 0;
+ int got_ard = 0; /* got ATA result descriptor */
+ int sb_sz;
+ struct sg_scsi_sense_hdr ssh;
+ unsigned char sense_buffer[64];
+ unsigned char ata_return_desc[16];
+ unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+ {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char apt12CmdBlk[SAT_ATA_PASS_THROUGH12_LEN] =
+ {SAT_ATA_PASS_THROUGH12, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0};
+
+ sb_sz = sizeof(sense_buffer);
+ memset(sense_buffer, 0, sb_sz);
+ memset(ata_return_desc, 0, sizeof(ata_return_desc));
+ ok = 0;
+ if (SAT_ATA_PASS_THROUGH16_LEN == cdb_len) {
+ /* Prepare ATA PASS-THROUGH COMMAND (16) command */
+ aptCmdBlk[3] = (feature >> 8) & 0xff; /* feature(15:8) */
+ aptCmdBlk[4] = feature & 0xff; /* feature(7:0) */
+ aptCmdBlk[5] = (blk_count >> 8) & 0xff; /* sector_count(15:8) */
+ aptCmdBlk[6] = blk_count & 0xff; /* sector_count(7:0) */
+ aptCmdBlk[8] = log_addr & 0xff; /* lba_low(7:0) == LBA(7:0) */
+ aptCmdBlk[9] = (page_in_log >> 8) & 0xff;
+ /* lba_mid(15:8) == LBA(39:32) */
+ aptCmdBlk[10] = page_in_log & 0xff; /* lba_mid(7:0) == LBA(15:8) */
+ aptCmdBlk[14] = ATA_READ_LOG_EXT;
+ aptCmdBlk[1] = (protocol << 1) | extend;
+ aptCmdBlk[2] = (ck_cond << 5) | (t_dir << 3) |
+ (byte_block << 2) | t_length;
+ res = sg_ll_ata_pt(sg_fd, aptCmdBlk, cdb_len, DEF_TIMEOUT, resp,
+ NULL /* doutp */, mx_resp_len, sense_buffer,
+ sb_sz, ata_return_desc,
+ sizeof(ata_return_desc), &resid, verbose);
+ } else {
+ /* Prepare ATA PASS-THROUGH COMMAND (12) command */
+ apt12CmdBlk[3] = feature & 0xff; /* feature(7:0) */
+ apt12CmdBlk[4] = blk_count & 0xff; /* sector_count(7:0) */
+ apt12CmdBlk[5] = log_addr & 0xff; /* lba_low(7:0) == LBA(7:0) */
+ apt12CmdBlk[6] = page_in_log & 0xff; /* lba_mid(7:0) == LBA(15:8) */
+ apt12CmdBlk[9] = ATA_READ_LOG_EXT;
+ apt12CmdBlk[1] = (protocol << 1);
+ apt12CmdBlk[2] = (ck_cond << 5) | (t_dir << 3) |
+ (byte_block << 2) | t_length;
+ res = sg_ll_ata_pt(sg_fd, apt12CmdBlk, cdb_len, DEF_TIMEOUT, resp,
+ NULL /* doutp */, mx_resp_len, sense_buffer,
+ sb_sz, ata_return_desc,
+ sizeof(ata_return_desc), &resid, verbose);
+ }
+ if (0 == res) {
+ ok = 1;
+ if (verbose > 2)
+ fprintf(stderr, "command completed with SCSI GOOD status\n");
+ } else if ((res > 0) && (res & SAM_STAT_CHECK_CONDITION)) {
+ if (verbose > 1)
+ sg_print_sense("ATA pass through", sense_buffer, sb_sz,
+ ((verbose > 2) ? 1 : 0));
+ if (sg_scsi_normalize_sense(sense_buffer, sb_sz, &ssh)) {
+ switch (ssh.sense_key) {
+ case SPC_SK_ILLEGAL_REQUEST:
+ if ((0x20 == ssh.asc) && (0x0 == ssh.ascq)) {
+ ret = SG_LIB_CAT_INVALID_OP;
+ if (verbose < 2)
+ fprintf(stderr, "ATA PASS-THROUGH (%d) not "
+ "supported\n", cdb_len);
+ } else {
+ ret = SG_LIB_CAT_ILLEGAL_REQ;
+ if (verbose < 2)
+ fprintf(stderr, "ATA PASS-THROUGH (%d), bad "
+ "field in cdb\n", cdb_len);
+ }
+ return ret;
+ case SPC_SK_NO_SENSE:
+ case SPC_SK_RECOVERED_ERROR:
+ if ((0x0 == ssh.asc) &&
+ (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
+ if (SAT_ATA_RETURN_DESC != ata_return_desc[0]) {
+ if (verbose)
+ fprintf(stderr, "did not find ATA Return "
+ "(sense) Descriptor\n");
+ return SG_LIB_CAT_RECOVERED;
+ }
+ got_ard = 1;
+ break;
+ } else if (SPC_SK_RECOVERED_ERROR == ssh.sense_key)
+ return SG_LIB_CAT_RECOVERED;
+ else {
+ if ((0x0 == ssh.asc) && (0x0 == ssh.ascq))
+ break;
+ return SG_LIB_CAT_SENSE;
+ }
+ case SPC_SK_UNIT_ATTENTION:
+ if (verbose < 2)
+ fprintf(stderr, "ATA PASS-THROUGH (%d), Unit Attention "
+ "detected\n", cdb_len);
+ return SG_LIB_CAT_UNIT_ATTENTION;
+ case SPC_SK_NOT_READY:
+ if (verbose < 2)
+ fprintf(stderr, "ATA PASS-THROUGH (%d), device not "
+ "ready\n", cdb_len);
+ return SG_LIB_CAT_NOT_READY;
+ case SPC_SK_MEDIUM_ERROR:
+ case SPC_SK_HARDWARE_ERROR:
+ if (verbose < 2)
+ fprintf(stderr, "ATA PASS-THROUGH (%d), medium or "
+ "hardware error\n", cdb_len);
+ return SG_LIB_CAT_MEDIUM_HARD;
+ case SPC_SK_ABORTED_COMMAND:
+ fprintf(stderr, "Aborted command\n");
+ return SG_LIB_CAT_ABORTED_COMMAND;
+ default:
+ if (verbose < 2)
+ fprintf(stderr, "ATA PASS-THROUGH (%d), some sense "
+ "data, use '-v' for more information\n", cdb_len);
+ return SG_LIB_CAT_SENSE;
+ }
+ } else {
+ fprintf(stderr, "CHECK CONDITION without response code ??\n");
+ return SG_LIB_CAT_SENSE;
+ }
+ if (0x72 != (sense_buffer[0] & 0x7f)) {
+ fprintf(stderr, "expected descriptor sense format, response "
+ "code=0x%x\n", sense_buffer[0]);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ } else if (res > 0) {
+ fprintf(stderr, "Unexpected SCSI status=0x%x\n", res);
+ return SG_LIB_CAT_MALFORMED;
+ } else {
+ fprintf(stderr, "ATA pass through (%d) failed\n", cdb_len);
+ if (verbose < 2)
+ fprintf(stderr, " try adding '-v' for more information\n");
+ return -1;
+ }
+
+ if ((SAT_ATA_RETURN_DESC == ata_return_desc[0]) && (0 == got_ard))
+ fprintf(stderr, "Seem to have got ATA Result Descriptor but "
+ "it was not indicated\n");
+ if (got_ard) {
+ if (ata_return_desc[3] & 0x4) {
+ fprintf(stderr, "error indication in returned FIS: aborted "
+ "command\n");
+ return SG_LIB_CAT_ABORTED_COMMAND;
+ }
+ ok = 1;
+ }
+
+ if (ok) { /* output result if ok and --hex or --raw given */
+ if (do_raw)
+ dStrRaw((const char *)resp, mx_resp_len);
+ else if (1 == do_hex)
+ dStrHex((const char *)resp, mx_resp_len, 0);
+ else if (do_hex > 1)
+ dWordHex((const unsigned short *)resp, mx_resp_len / 2, 0,
+ sg_is_big_endian());
+ }
+ return 0;
+}
+
+
+int main(int argc, char * argv[])
+{
+ int sg_fd, c, k, res;
+ char * device_name = 0;
+ char ebuff[EBUFF_SZ];
+ unsigned char inBuff[READ_LOG_EXT_RESPONSE_LEN];
+ int cdb_len = 16;
+ int hex = 0;
+ int ignore = 0;
+ int raw = 0;
+ int reset = 0;
+ int verbose = 0;
+ int ck_cond = 0; /* set to 1 to read register(s) back */
+ int extend = 0;
+ int ret = 0;
+ int nvmSize = 0;
+
+ memset(inBuff, 0, sizeof(inBuff));
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "cehHil:rRvV",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'c':
+ ++ck_cond;
+ break;
+ case 'e':
+ ++extend;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ exit(0);
+ case 'H':
+ ++hex;
+ break;
+ case 'i':
+ ++ignore;
+ break;
+ case 'l':
+ cdb_len = sg_get_num(optarg);
+ if (! ((cdb_len == 12) || (cdb_len == 16))) {
+ fprintf(stderr, "argument to '--len' should be 12 or 16\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'r':
+ ++raw;
+ break;
+ case 'R':
+ ++reset;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'V':
+ fprintf(stderr, "version: %s\n", version_str);
+ exit(0);
+ default:
+ fprintf(stderr, "unrecognised option code %c [0x%x]\n", c, c);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (optind < argc) {
+ if (NULL == device_name) {
+ device_name = argv[optind];
+ ++optind;
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ fprintf(stderr, "Unexpected extra argument: %s\n",
+ argv[optind]);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (0 == device_name) {
+ fprintf(stderr, "no DEVICE name detected\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (raw) {
+ if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+ perror("sg_set_binary_mode");
+ return SG_LIB_FILE_ERROR;
+ }
+ }
+
+ if ((sg_fd = open(device_name, O_RDWR)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_sat_get_hybrid_log: error opening file: %s", device_name);
+ perror(ebuff);
+ return SG_LIB_FILE_ERROR;
+ }
+ ret = do_read_log_ext(sg_fd, SATA_HYBRID_INFO_LPAGE, 0 /* page_in_log */,
+ (reset ? 1 : 0) /* feature */,
+ 1 /* blk_count */, inBuff,
+ READ_LOG_EXT_RESPONSE_LEN, cdb_len, ck_cond,
+ extend, hex, raw, verbose);
+
+ if ((0 == ret) && (0 == hex) && (0 == raw)) {
+ printf("SATA hybrid information log:\n");
+ k = 0;
+ printf ("Number of Hybrid Descriptors=%d\n", inBuff[k]&0xF);
+ k = 2;
+ printf ("Enabled=%x\n", inBuff[k++]);
+ printf ("Hybrid Health =%x\n", inBuff[k++]);
+ printf ("Dirty Low Threshold =%x\n", inBuff[k++]);
+ printf ("Dirty High Threshold =%x\n", inBuff[k++]);
+ printf ("Optimal Write Granularity=%x\n", inBuff[k++]);
+ printf ("Maximum Priority Level=%d\n", inBuff[k++]&0xF);
+ k = 16;
+ nvmSize = (inBuff[k] | (inBuff[k+1]<<8) | (inBuff[k+2]<<16) | (inBuff[k+3]<<24) );
+ printf ("NVM Size=%x(%d GigaBytes)\n", nvmSize, nvmSize/(2*1024*1024));
+
+ for (k = 64; k < 512; k += 16) {
+ if (inBuff[k]) {
+ printf ("Hybrid Priority=%d\n", inBuff[k]);
+ printf ("Consumed NVM Size Fraction=%x\n", inBuff[k+1]);
+ printf ("Consumed Mapping Resource Fraction=%x\n", inBuff[k+2]);
+ printf ("Consumed NVM Size For Dirty Fraction=%x\n", inBuff[k+3]);
+ printf ("Consumed Mapping Resource For Dirty Data Fraction=%x\n", inBuff[k+4]);
+ }
+ }
+ }
+
+ res = close(sg_fd);
+ if (res < 0) {
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/