Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/hmcdrv: HMC drive CD/DVD access

This device driver allows accessing a HMC drive CD/DVD-ROM.
It can be used in a LPAR and z/VM environment.

Reviewed-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Ralf Hoppe <rhoppe@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

authored by

Ralf Hoppe and committed by
Martin Schwidefsky
8f933b10 ea61a579

+1793
+1
arch/s390/include/asm/irq.h
··· 51 51 IRQEXT_CMS, 52 52 IRQEXT_CMC, 53 53 IRQEXT_CMR, 54 + IRQEXT_FTP, 54 55 IRQIO_CIO, 55 56 IRQIO_QAI, 56 57 IRQIO_DAS,
+1
arch/s390/kernel/irq.c
··· 70 70 {.irq = IRQEXT_CMS, .name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"}, 71 71 {.irq = IRQEXT_CMC, .name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"}, 72 72 {.irq = IRQEXT_CMR, .name = "CMR", .desc = "[EXT] CPU-Measurement: RI"}, 73 + {.irq = IRQEXT_FTP, .name = "FTP", .desc = "[EXT] HMC FTP Service"}, 73 74 {.irq = IRQIO_CIO, .name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"}, 74 75 {.irq = IRQIO_QAI, .name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"}, 75 76 {.irq = IRQIO_DAS, .name = "DAS", .desc = "[I/O] DASD"},
+13
drivers/s390/char/Kconfig
··· 102 102 want for inform other people about your kernel panics, 103 103 need this feature and intend to run your kernel in LPAR. 104 104 105 + config HMC_DRV 106 + def_tristate m 107 + prompt "Support for file transfers from HMC drive CD/DVD-ROM" 108 + depends on 64BIT 109 + select CRC16 110 + help 111 + This option enables support for file transfers from a Hardware 112 + Management Console (HMC) drive CD/DVD-ROM. It is available as a 113 + module, called 'hmcdrv', and also as kernel built-in. There is one 114 + optional parameter for this module: cachesize=N, which modifies the 115 + transfer cache size from it's default value 0.5MB to N bytes. If N 116 + is zero, then no caching is performed. 117 + 105 118 config S390_TAPE 106 119 def_tristate m 107 120 prompt "S/390 tape device support"
+3
drivers/s390/char/Makefile
··· 33 33 34 34 zcore_mod-objs := sclp_sdias.o zcore.o 35 35 obj-$(CONFIG_CRASH_DUMP) += zcore_mod.o 36 + 37 + hmcdrv-objs := hmcdrv_mod.o hmcdrv_dev.o hmcdrv_ftp.o hmcdrv_cache.o diag_ftp.o sclp_ftp.o 38 + obj-$(CONFIG_HMC_DRV) += hmcdrv.o
+237
drivers/s390/char/diag_ftp.c
··· 1 + /* 2 + * DIAGNOSE X'2C4' instruction based HMC FTP services, useable on z/VM 3 + * 4 + * Copyright IBM Corp. 2013 5 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 6 + * 7 + */ 8 + 9 + #define KMSG_COMPONENT "hmcdrv" 10 + #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 11 + 12 + #include <linux/kernel.h> 13 + #include <linux/mm.h> 14 + #include <linux/irq.h> 15 + #include <linux/wait.h> 16 + #include <linux/string.h> 17 + #include <asm/ctl_reg.h> 18 + 19 + #include "hmcdrv_ftp.h" 20 + #include "diag_ftp.h" 21 + 22 + /* DIAGNOSE X'2C4' return codes in Ry */ 23 + #define DIAG_FTP_RET_OK 0 /* HMC FTP started successfully */ 24 + #define DIAG_FTP_RET_EBUSY 4 /* HMC FTP service currently busy */ 25 + #define DIAG_FTP_RET_EIO 8 /* HMC FTP service I/O error */ 26 + /* and an artificial extension */ 27 + #define DIAG_FTP_RET_EPERM 2 /* HMC FTP service privilege error */ 28 + 29 + /* FTP service status codes (after INTR at guest real location 133) */ 30 + #define DIAG_FTP_STAT_OK 0U /* request completed successfully */ 31 + #define DIAG_FTP_STAT_PGCC 4U /* program check condition */ 32 + #define DIAG_FTP_STAT_PGIOE 8U /* paging I/O error */ 33 + #define DIAG_FTP_STAT_TIMEOUT 12U /* timeout */ 34 + #define DIAG_FTP_STAT_EBASE 16U /* base of error codes from SCLP */ 35 + #define DIAG_FTP_STAT_LDFAIL (DIAG_FTP_STAT_EBASE + 1U) /* failed */ 36 + #define DIAG_FTP_STAT_LDNPERM (DIAG_FTP_STAT_EBASE + 2U) /* not allowed */ 37 + #define DIAG_FTP_STAT_LDRUNS (DIAG_FTP_STAT_EBASE + 3U) /* runs */ 38 + #define DIAG_FTP_STAT_LDNRUNS (DIAG_FTP_STAT_EBASE + 4U) /* not runs */ 39 + 40 + /** 41 + * struct diag_ftp_ldfpl - load file FTP parameter list (LDFPL) 42 + * @bufaddr: real buffer address (at 4k boundary) 43 + * @buflen: length of buffer 44 + * @offset: dir/file offset 45 + * @intparm: interruption parameter (unused) 46 + * @transferred: bytes transferred 47 + * @fsize: file size, filled on GET 48 + * @failaddr: failing address 49 + * @spare: padding 50 + * @fident: file name - ASCII 51 + */ 52 + struct diag_ftp_ldfpl { 53 + u64 bufaddr; 54 + u64 buflen; 55 + u64 offset; 56 + u64 intparm; 57 + u64 transferred; 58 + u64 fsize; 59 + u64 failaddr; 60 + u64 spare; 61 + u8 fident[HMCDRV_FTP_FIDENT_MAX]; 62 + } __packed; 63 + 64 + static DECLARE_COMPLETION(diag_ftp_rx_complete); 65 + static int diag_ftp_subcode; 66 + 67 + /** 68 + * diag_ftp_handler() - FTP services IRQ handler 69 + * @extirq: external interrupt (sub-) code 70 + * @param32: 32-bit interruption parameter from &struct diag_ftp_ldfpl 71 + * @param64: unused (for 64-bit interrupt parameters) 72 + */ 73 + static void diag_ftp_handler(struct ext_code extirq, 74 + unsigned int param32, 75 + unsigned long param64) 76 + { 77 + if ((extirq.subcode >> 8) != 8) 78 + return; /* not a FTP services sub-code */ 79 + 80 + inc_irq_stat(IRQEXT_FTP); 81 + diag_ftp_subcode = extirq.subcode & 0xffU; 82 + complete(&diag_ftp_rx_complete); 83 + } 84 + 85 + /** 86 + * diag_ftp_2c4() - DIAGNOSE X'2C4' service call 87 + * @fpl: pointer to prepared LDFPL 88 + * @cmd: FTP command to be executed 89 + * 90 + * Performs a DIAGNOSE X'2C4' call with (input/output) FTP parameter list 91 + * @fpl and FTP function code @cmd. In case of an error the function does 92 + * nothing and returns an (negative) error code. 93 + * 94 + * Notes: 95 + * 1. This function only initiates a transfer, so the caller must wait 96 + * for completion (asynchronous execution). 97 + * 2. The FTP parameter list @fpl must be aligned to a double-word boundary. 98 + * 3. fpl->bufaddr must be a real address, 4k aligned 99 + */ 100 + static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl, 101 + enum hmcdrv_ftp_cmdid cmd) 102 + { 103 + int rc; 104 + 105 + asm volatile( 106 + " diag %[addr],%[cmd],0x2c4\n" 107 + "0: j 2f\n" 108 + "1: la %[rc],%[err]\n" 109 + "2:\n" 110 + EX_TABLE(0b, 1b) 111 + : [rc] "=d" (rc), "+m" (*fpl) 112 + : [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)), 113 + [err] "i" (DIAG_FTP_RET_EPERM) 114 + : "cc"); 115 + 116 + switch (rc) { 117 + case DIAG_FTP_RET_OK: 118 + return 0; 119 + case DIAG_FTP_RET_EBUSY: 120 + return -EBUSY; 121 + case DIAG_FTP_RET_EPERM: 122 + return -EPERM; 123 + case DIAG_FTP_RET_EIO: 124 + default: 125 + return -EIO; 126 + } 127 + } 128 + 129 + /** 130 + * diag_ftp_cmd() - executes a DIAG X'2C4' FTP command, targeting a HMC 131 + * @ftp: pointer to FTP command specification 132 + * @fsize: return of file size (or NULL if undesirable) 133 + * 134 + * Attention: Notice that this function is not reentrant - so the caller 135 + * must ensure locking. 136 + * 137 + * Return: number of bytes read/written or a (negative) error code 138 + */ 139 + ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) 140 + { 141 + struct diag_ftp_ldfpl *ldfpl; 142 + ssize_t len; 143 + #ifdef DEBUG 144 + unsigned long start_jiffies; 145 + 146 + pr_debug("starting DIAG X'2C4' on '%s', requesting %zd bytes\n", 147 + ftp->fname, ftp->len); 148 + start_jiffies = jiffies; 149 + #endif 150 + init_completion(&diag_ftp_rx_complete); 151 + 152 + ldfpl = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 153 + if (!ldfpl) { 154 + len = -ENOMEM; 155 + goto out; 156 + } 157 + 158 + len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident)); 159 + if (len >= HMCDRV_FTP_FIDENT_MAX) { 160 + len = -EINVAL; 161 + goto out_free; 162 + } 163 + 164 + ldfpl->transferred = 0; 165 + ldfpl->fsize = 0; 166 + ldfpl->offset = ftp->ofs; 167 + ldfpl->buflen = ftp->len; 168 + ldfpl->bufaddr = virt_to_phys(ftp->buf); 169 + 170 + len = diag_ftp_2c4(ldfpl, ftp->id); 171 + if (len) 172 + goto out_free; 173 + 174 + /* 175 + * There is no way to cancel the running diag X'2C4', the code 176 + * needs to wait unconditionally until the transfer is complete. 177 + */ 178 + wait_for_completion(&diag_ftp_rx_complete); 179 + 180 + #ifdef DEBUG 181 + pr_debug("completed DIAG X'2C4' after %lu ms\n", 182 + (jiffies - start_jiffies) * 1000 / HZ); 183 + pr_debug("status of DIAG X'2C4' is %u, with %lld/%lld bytes\n", 184 + diag_ftp_subcode, ldfpl->transferred, ldfpl->fsize); 185 + #endif 186 + 187 + switch (diag_ftp_subcode) { 188 + case DIAG_FTP_STAT_OK: /* success */ 189 + len = ldfpl->transferred; 190 + if (fsize) 191 + *fsize = ldfpl->fsize; 192 + break; 193 + case DIAG_FTP_STAT_LDNPERM: 194 + len = -EPERM; 195 + break; 196 + case DIAG_FTP_STAT_LDRUNS: 197 + len = -EBUSY; 198 + break; 199 + case DIAG_FTP_STAT_LDFAIL: 200 + len = -ENOENT; /* no such file or media */ 201 + break; 202 + default: 203 + len = -EIO; 204 + break; 205 + } 206 + 207 + out_free: 208 + free_page((unsigned long) ldfpl); 209 + out: 210 + return len; 211 + } 212 + 213 + /** 214 + * diag_ftp_startup() - startup of FTP services, when running on z/VM 215 + * 216 + * Return: 0 on success, else an (negative) error code 217 + */ 218 + int diag_ftp_startup(void) 219 + { 220 + int rc; 221 + 222 + rc = register_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler); 223 + if (rc) 224 + return rc; 225 + 226 + ctl_set_bit(0, 63 - 22); 227 + return 0; 228 + } 229 + 230 + /** 231 + * diag_ftp_shutdown() - shutdown of FTP services, when running on z/VM 232 + */ 233 + void diag_ftp_shutdown(void) 234 + { 235 + ctl_clear_bit(0, 63 - 22); 236 + unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler); 237 + }
+21
drivers/s390/char/diag_ftp.h
··· 1 + /* 2 + * DIAGNOSE X'2C4' instruction based SE/HMC FTP Services, useable on z/VM 3 + * 4 + * Notice that all functions exported here are not reentrant. 5 + * So usage should be exclusive, ensured by the caller (e.g. using a 6 + * mutex). 7 + * 8 + * Copyright IBM Corp. 2013 9 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 10 + */ 11 + 12 + #ifndef __DIAG_FTP_H__ 13 + #define __DIAG_FTP_H__ 14 + 15 + #include "hmcdrv_ftp.h" 16 + 17 + int diag_ftp_startup(void); 18 + void diag_ftp_shutdown(void); 19 + ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize); 20 + 21 + #endif /* __DIAG_FTP_H__ */
+252
drivers/s390/char/hmcdrv_cache.c
··· 1 + /* 2 + * SE/HMC Drive (Read) Cache Functions 3 + * 4 + * Copyright IBM Corp. 2013 5 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 6 + * 7 + */ 8 + 9 + #define KMSG_COMPONENT "hmcdrv" 10 + #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 11 + 12 + #include <linux/kernel.h> 13 + #include <linux/mm.h> 14 + #include <linux/jiffies.h> 15 + 16 + #include "hmcdrv_ftp.h" 17 + #include "hmcdrv_cache.h" 18 + 19 + #define HMCDRV_CACHE_TIMEOUT 30 /* aging timeout in seconds */ 20 + 21 + /** 22 + * struct hmcdrv_cache_entry - file cache (only used on read/dir) 23 + * @id: FTP command ID 24 + * @content: kernel-space buffer, 4k aligned 25 + * @len: size of @content cache (0 if caching disabled) 26 + * @ofs: start of content within file (-1 if no cached content) 27 + * @fname: file name 28 + * @fsize: file size 29 + * @timeout: cache timeout in jiffies 30 + * 31 + * Notice that the first three members (id, fname, fsize) are cached on all 32 + * read/dir requests. But content is cached only under some preconditions. 33 + * Uncached content is signalled by a negative value of @ofs. 34 + */ 35 + struct hmcdrv_cache_entry { 36 + enum hmcdrv_ftp_cmdid id; 37 + char fname[HMCDRV_FTP_FIDENT_MAX]; 38 + size_t fsize; 39 + loff_t ofs; 40 + unsigned long timeout; 41 + void *content; 42 + size_t len; 43 + }; 44 + 45 + static int hmcdrv_cache_order; /* cache allocated page order */ 46 + 47 + static struct hmcdrv_cache_entry hmcdrv_cache_file = { 48 + .fsize = SIZE_MAX, 49 + .ofs = -1, 50 + .len = 0, 51 + .fname = {'\0'} 52 + }; 53 + 54 + /** 55 + * hmcdrv_cache_get() - looks for file data/content in read cache 56 + * @ftp: pointer to FTP command specification 57 + * 58 + * Return: number of bytes read from cache or a negative number if nothing 59 + * in content cache (for the file/cmd specified in @ftp) 60 + */ 61 + static ssize_t hmcdrv_cache_get(const struct hmcdrv_ftp_cmdspec *ftp) 62 + { 63 + loff_t pos; /* position in cache (signed) */ 64 + ssize_t len; 65 + 66 + if ((ftp->id != hmcdrv_cache_file.id) || 67 + strcmp(hmcdrv_cache_file.fname, ftp->fname)) 68 + return -1; 69 + 70 + if (ftp->ofs >= hmcdrv_cache_file.fsize) /* EOF ? */ 71 + return 0; 72 + 73 + if ((hmcdrv_cache_file.ofs < 0) || /* has content? */ 74 + time_after(jiffies, hmcdrv_cache_file.timeout)) 75 + return -1; 76 + 77 + /* there seems to be cached content - calculate the maximum number 78 + * of bytes that can be returned (regarding file size and offset) 79 + */ 80 + len = hmcdrv_cache_file.fsize - ftp->ofs; 81 + 82 + if (len > ftp->len) 83 + len = ftp->len; 84 + 85 + /* check if the requested chunk falls into our cache (which starts 86 + * at offset 'hmcdrv_cache_file.ofs' in the file of interest) 87 + */ 88 + pos = ftp->ofs - hmcdrv_cache_file.ofs; 89 + 90 + if ((pos >= 0) && 91 + ((pos + len) <= hmcdrv_cache_file.len)) { 92 + 93 + memcpy(ftp->buf, 94 + hmcdrv_cache_file.content + pos, 95 + len); 96 + pr_debug("using cached content of '%s', returning %zd/%zd bytes\n", 97 + hmcdrv_cache_file.fname, len, 98 + hmcdrv_cache_file.fsize); 99 + 100 + return len; 101 + } 102 + 103 + return -1; 104 + } 105 + 106 + /** 107 + * hmcdrv_cache_do() - do a HMC drive CD/DVD transfer with cache update 108 + * @ftp: pointer to FTP command specification 109 + * @func: FTP transfer function to be used 110 + * 111 + * Return: number of bytes read/written or a (negative) error code 112 + */ 113 + static ssize_t hmcdrv_cache_do(const struct hmcdrv_ftp_cmdspec *ftp, 114 + hmcdrv_cache_ftpfunc func) 115 + { 116 + ssize_t len; 117 + 118 + /* only cache content if the read/dir cache really exists 119 + * (hmcdrv_cache_file.len > 0), is large enough to handle the 120 + * request (hmcdrv_cache_file.len >= ftp->len) and there is a need 121 + * to do so (ftp->len > 0) 122 + */ 123 + if ((ftp->len > 0) && (hmcdrv_cache_file.len >= ftp->len)) { 124 + 125 + /* because the cache is not located at ftp->buf, we have to 126 + * assemble a new HMC drive FTP cmd specification (pointing 127 + * to our cache, and using the increased size) 128 + */ 129 + struct hmcdrv_ftp_cmdspec cftp = *ftp; /* make a copy */ 130 + cftp.buf = hmcdrv_cache_file.content; /* and update */ 131 + cftp.len = hmcdrv_cache_file.len; /* buffer data */ 132 + 133 + len = func(&cftp, &hmcdrv_cache_file.fsize); /* now do */ 134 + 135 + if (len > 0) { 136 + pr_debug("caching %zd bytes content for '%s'\n", 137 + len, ftp->fname); 138 + 139 + if (len > ftp->len) 140 + len = ftp->len; 141 + 142 + hmcdrv_cache_file.ofs = ftp->ofs; 143 + hmcdrv_cache_file.timeout = jiffies + 144 + HMCDRV_CACHE_TIMEOUT * HZ; 145 + memcpy(ftp->buf, hmcdrv_cache_file.content, len); 146 + } 147 + } else { 148 + len = func(ftp, &hmcdrv_cache_file.fsize); 149 + hmcdrv_cache_file.ofs = -1; /* invalidate content */ 150 + } 151 + 152 + if (len > 0) { 153 + /* cache some file info (FTP command, file name and file 154 + * size) unconditionally 155 + */ 156 + strlcpy(hmcdrv_cache_file.fname, ftp->fname, 157 + HMCDRV_FTP_FIDENT_MAX); 158 + hmcdrv_cache_file.id = ftp->id; 159 + pr_debug("caching cmd %d, file size %zu for '%s'\n", 160 + ftp->id, hmcdrv_cache_file.fsize, ftp->fname); 161 + } 162 + 163 + return len; 164 + } 165 + 166 + /** 167 + * hmcdrv_cache_cmd() - perform a cached HMC drive CD/DVD transfer 168 + * @ftp: pointer to FTP command specification 169 + * @func: FTP transfer function to be used 170 + * 171 + * Attention: Notice that this function is not reentrant - so the caller 172 + * must ensure exclusive execution. 173 + * 174 + * Return: number of bytes read/written or a (negative) error code 175 + */ 176 + ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp, 177 + hmcdrv_cache_ftpfunc func) 178 + { 179 + ssize_t len; 180 + 181 + if ((ftp->id == HMCDRV_FTP_DIR) || /* read cache */ 182 + (ftp->id == HMCDRV_FTP_NLIST) || 183 + (ftp->id == HMCDRV_FTP_GET)) { 184 + 185 + len = hmcdrv_cache_get(ftp); 186 + 187 + if (len >= 0) /* got it from cache ? */ 188 + return len; /* yes */ 189 + 190 + len = hmcdrv_cache_do(ftp, func); 191 + 192 + if (len >= 0) 193 + return len; 194 + 195 + } else { 196 + len = func(ftp, NULL); /* simply do original command */ 197 + } 198 + 199 + /* invalidate the (read) cache in case there was a write operation 200 + * or an error on read/dir 201 + */ 202 + hmcdrv_cache_file.id = HMCDRV_FTP_NOOP; 203 + hmcdrv_cache_file.fsize = LLONG_MAX; 204 + hmcdrv_cache_file.ofs = -1; 205 + 206 + return len; 207 + } 208 + 209 + /** 210 + * hmcdrv_cache_startup() - startup of HMC drive cache 211 + * @cachesize: cache size 212 + * 213 + * Return: 0 on success, else a (negative) error code 214 + */ 215 + int hmcdrv_cache_startup(size_t cachesize) 216 + { 217 + if (cachesize > 0) { /* perform caching ? */ 218 + hmcdrv_cache_order = get_order(cachesize); 219 + hmcdrv_cache_file.content = 220 + (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, 221 + hmcdrv_cache_order); 222 + 223 + if (!hmcdrv_cache_file.content) { 224 + pr_err("Allocating the requested cache size of %zu bytes failed\n", 225 + cachesize); 226 + return -ENOMEM; 227 + } 228 + 229 + pr_debug("content cache enabled, size is %zu bytes\n", 230 + cachesize); 231 + } 232 + 233 + hmcdrv_cache_file.len = cachesize; 234 + return 0; 235 + } 236 + 237 + /** 238 + * hmcdrv_cache_shutdown() - shutdown of HMC drive cache 239 + */ 240 + void hmcdrv_cache_shutdown(void) 241 + { 242 + if (hmcdrv_cache_file.content) { 243 + free_pages((unsigned long) hmcdrv_cache_file.content, 244 + hmcdrv_cache_order); 245 + hmcdrv_cache_file.content = NULL; 246 + } 247 + 248 + hmcdrv_cache_file.id = HMCDRV_FTP_NOOP; 249 + hmcdrv_cache_file.fsize = LLONG_MAX; 250 + hmcdrv_cache_file.ofs = -1; 251 + hmcdrv_cache_file.len = 0; /* no cache */ 252 + }
+24
drivers/s390/char/hmcdrv_cache.h
··· 1 + /* 2 + * SE/HMC Drive (Read) Cache Functions 3 + * 4 + * Copyright IBM Corp. 2013 5 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 6 + */ 7 + 8 + #ifndef __HMCDRV_CACHE_H__ 9 + #define __HMCDRV_CACHE_H__ 10 + 11 + #include <linux/mmzone.h> 12 + #include "hmcdrv_ftp.h" 13 + 14 + #define HMCDRV_CACHE_SIZE_DFLT (MAX_ORDER_NR_PAGES * PAGE_SIZE / 2UL) 15 + 16 + typedef ssize_t (*hmcdrv_cache_ftpfunc)(const struct hmcdrv_ftp_cmdspec *ftp, 17 + size_t *fsize); 18 + 19 + ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp, 20 + hmcdrv_cache_ftpfunc func); 21 + int hmcdrv_cache_startup(size_t cachesize); 22 + void hmcdrv_cache_shutdown(void); 23 + 24 + #endif /* __HMCDRV_CACHE_H__ */
+370
drivers/s390/char/hmcdrv_dev.c
··· 1 + /* 2 + * HMC Drive CD/DVD Device 3 + * 4 + * Copyright IBM Corp. 2013 5 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 6 + * 7 + * This file provides a Linux "misc" character device for access to an 8 + * assigned HMC drive CD/DVD-ROM. It works as follows: First create the 9 + * device by calling hmcdrv_dev_init(). After open() a lseek(fd, 0, 10 + * SEEK_END) indicates that a new FTP command follows (not needed on the 11 + * first command after open). Then write() the FTP command ASCII string 12 + * to it, e.g. "dir /" or "nls <directory>" or "get <filename>". At the 13 + * end read() the response. 14 + */ 15 + 16 + #define KMSG_COMPONENT "hmcdrv" 17 + #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 18 + 19 + #include <linux/kernel.h> 20 + #include <linux/module.h> 21 + #include <linux/slab.h> 22 + #include <linux/fs.h> 23 + #include <linux/cdev.h> 24 + #include <linux/miscdevice.h> 25 + #include <linux/device.h> 26 + #include <linux/capability.h> 27 + #include <linux/delay.h> 28 + #include <linux/uaccess.h> 29 + 30 + #include "hmcdrv_dev.h" 31 + #include "hmcdrv_ftp.h" 32 + 33 + /* If the following macro is defined, then the HMC device creates it's own 34 + * separated device class (and dynamically assigns a major number). If not 35 + * defined then the HMC device is assigned to the "misc" class devices. 36 + * 37 + #define HMCDRV_DEV_CLASS "hmcftp" 38 + */ 39 + 40 + #define HMCDRV_DEV_NAME "hmcdrv" 41 + #define HMCDRV_DEV_BUSY_DELAY 500 /* delay between -EBUSY trials in ms */ 42 + #define HMCDRV_DEV_BUSY_RETRIES 3 /* number of retries on -EBUSY */ 43 + 44 + struct hmcdrv_dev_node { 45 + 46 + #ifdef HMCDRV_DEV_CLASS 47 + struct cdev dev; /* character device structure */ 48 + umode_t mode; /* mode of device node (unused, zero) */ 49 + #else 50 + struct miscdevice dev; /* "misc" device structure */ 51 + #endif 52 + 53 + }; 54 + 55 + static int hmcdrv_dev_open(struct inode *inode, struct file *fp); 56 + static int hmcdrv_dev_release(struct inode *inode, struct file *fp); 57 + static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence); 58 + static ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf, 59 + size_t len, loff_t *pos); 60 + static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf, 61 + size_t len, loff_t *pos); 62 + static ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset, 63 + char __user *buf, size_t len); 64 + 65 + /* 66 + * device operations 67 + */ 68 + static const struct file_operations hmcdrv_dev_fops = { 69 + .open = hmcdrv_dev_open, 70 + .llseek = hmcdrv_dev_seek, 71 + .release = hmcdrv_dev_release, 72 + .read = hmcdrv_dev_read, 73 + .write = hmcdrv_dev_write, 74 + }; 75 + 76 + static struct hmcdrv_dev_node hmcdrv_dev; /* HMC device struct (static) */ 77 + 78 + #ifdef HMCDRV_DEV_CLASS 79 + 80 + static struct class *hmcdrv_dev_class; /* device class pointer */ 81 + static dev_t hmcdrv_dev_no; /* device number (major/minor) */ 82 + 83 + /** 84 + * hmcdrv_dev_name() - provides a naming hint for a device node in /dev 85 + * @dev: device for which the naming/mode hint is 86 + * @mode: file mode for device node created in /dev 87 + * 88 + * See: devtmpfs.c, function devtmpfs_create_node() 89 + * 90 + * Return: recommended device file name in /dev 91 + */ 92 + static char *hmcdrv_dev_name(struct device *dev, umode_t *mode) 93 + { 94 + char *nodename = NULL; 95 + const char *devname = dev_name(dev); /* kernel device name */ 96 + 97 + if (devname) 98 + nodename = kasprintf(GFP_KERNEL, "%s", devname); 99 + 100 + /* on device destroy (rmmod) the mode pointer may be NULL 101 + */ 102 + if (mode) 103 + *mode = hmcdrv_dev.mode; 104 + 105 + return nodename; 106 + } 107 + 108 + #endif /* HMCDRV_DEV_CLASS */ 109 + 110 + /* 111 + * open() 112 + */ 113 + static int hmcdrv_dev_open(struct inode *inode, struct file *fp) 114 + { 115 + int rc; 116 + 117 + /* check for non-blocking access, which is really unsupported 118 + */ 119 + if (fp->f_flags & O_NONBLOCK) 120 + return -EINVAL; 121 + 122 + /* Because it makes no sense to open this device read-only (then a 123 + * FTP command cannot be emitted), we respond with an error. 124 + */ 125 + if ((fp->f_flags & O_ACCMODE) == O_RDONLY) 126 + return -EINVAL; 127 + 128 + /* prevent unloading this module as long as anyone holds the 129 + * device file open - so increment the reference count here 130 + */ 131 + if (!try_module_get(THIS_MODULE)) 132 + return -ENODEV; 133 + 134 + fp->private_data = NULL; /* no command yet */ 135 + rc = hmcdrv_ftp_startup(); 136 + if (rc) 137 + module_put(THIS_MODULE); 138 + 139 + pr_debug("open file '/dev/%s' with return code %d\n", 140 + fp->f_dentry->d_name.name, rc); 141 + return rc; 142 + } 143 + 144 + /* 145 + * release() 146 + */ 147 + static int hmcdrv_dev_release(struct inode *inode, struct file *fp) 148 + { 149 + pr_debug("closing file '/dev/%s'\n", fp->f_dentry->d_name.name); 150 + kfree(fp->private_data); 151 + fp->private_data = NULL; 152 + hmcdrv_ftp_shutdown(); 153 + module_put(THIS_MODULE); 154 + return 0; 155 + } 156 + 157 + /* 158 + * lseek() 159 + */ 160 + static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence) 161 + { 162 + switch (whence) { 163 + case SEEK_CUR: /* relative to current file position */ 164 + pos += fp->f_pos; /* new position stored in 'pos' */ 165 + break; 166 + 167 + case SEEK_SET: /* absolute (relative to beginning of file) */ 168 + break; /* SEEK_SET */ 169 + 170 + /* We use SEEK_END as a special indicator for a SEEK_SET 171 + * (set absolute position), combined with a FTP command 172 + * clear. 173 + */ 174 + case SEEK_END: 175 + if (fp->private_data) { 176 + kfree(fp->private_data); 177 + fp->private_data = NULL; 178 + } 179 + 180 + break; /* SEEK_END */ 181 + 182 + default: /* SEEK_DATA, SEEK_HOLE: unsupported */ 183 + return -EINVAL; 184 + } 185 + 186 + if (pos < 0) 187 + return -EINVAL; 188 + 189 + if (fp->f_pos != pos) 190 + ++fp->f_version; 191 + 192 + fp->f_pos = pos; 193 + return pos; 194 + } 195 + 196 + /* 197 + * transfer (helper function) 198 + */ 199 + static ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset, 200 + char __user *buf, size_t len) 201 + { 202 + ssize_t retlen; 203 + unsigned trials = HMCDRV_DEV_BUSY_RETRIES; 204 + 205 + do { 206 + retlen = hmcdrv_ftp_cmd(cmd, offset, buf, len); 207 + 208 + if (retlen != -EBUSY) 209 + break; 210 + 211 + msleep(HMCDRV_DEV_BUSY_DELAY); 212 + 213 + } while (--trials > 0); 214 + 215 + return retlen; 216 + } 217 + 218 + /* 219 + * read() 220 + */ 221 + static ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf, 222 + size_t len, loff_t *pos) 223 + { 224 + ssize_t retlen; 225 + 226 + if (((fp->f_flags & O_ACCMODE) == O_WRONLY) || 227 + (fp->private_data == NULL)) { /* no FTP cmd defined ? */ 228 + return -EBADF; 229 + } 230 + 231 + retlen = hmcdrv_dev_transfer((char *) fp->private_data, 232 + *pos, ubuf, len); 233 + 234 + pr_debug("read from file '/dev/%s' at %lld returns %zd/%zu\n", 235 + fp->f_dentry->d_name.name, (long long) *pos, retlen, len); 236 + 237 + if (retlen > 0) 238 + *pos += retlen; 239 + 240 + return retlen; 241 + } 242 + 243 + /* 244 + * write() 245 + */ 246 + static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf, 247 + size_t len, loff_t *pos) 248 + { 249 + ssize_t retlen; 250 + 251 + pr_debug("writing file '/dev/%s' at pos. %lld with length %zd\n", 252 + fp->f_dentry->d_name.name, (long long) *pos, len); 253 + 254 + if (!fp->private_data) { /* first expect a cmd write */ 255 + fp->private_data = kmalloc(len + 1, GFP_KERNEL); 256 + 257 + if (!fp->private_data) 258 + return -ENOMEM; 259 + 260 + if (!copy_from_user(fp->private_data, ubuf, len)) { 261 + ((char *)fp->private_data)[len] = '\0'; 262 + return len; 263 + } 264 + 265 + kfree(fp->private_data); 266 + fp->private_data = NULL; 267 + return -EFAULT; 268 + } 269 + 270 + retlen = hmcdrv_dev_transfer((char *) fp->private_data, 271 + *pos, (char __user *) ubuf, len); 272 + if (retlen > 0) 273 + *pos += retlen; 274 + 275 + pr_debug("write to file '/dev/%s' returned %zd\n", 276 + fp->f_dentry->d_name.name, retlen); 277 + 278 + return retlen; 279 + } 280 + 281 + /** 282 + * hmcdrv_dev_init() - creates a HMC drive CD/DVD device 283 + * 284 + * This function creates a HMC drive CD/DVD kernel device and an associated 285 + * device under /dev, using a dynamically allocated major number. 286 + * 287 + * Return: 0 on success, else an error code. 288 + */ 289 + int hmcdrv_dev_init(void) 290 + { 291 + int rc; 292 + 293 + #ifdef HMCDRV_DEV_CLASS 294 + struct device *dev; 295 + 296 + rc = alloc_chrdev_region(&hmcdrv_dev_no, 0, 1, HMCDRV_DEV_NAME); 297 + 298 + if (rc) 299 + goto out_err; 300 + 301 + cdev_init(&hmcdrv_dev.dev, &hmcdrv_dev_fops); 302 + hmcdrv_dev.dev.owner = THIS_MODULE; 303 + rc = cdev_add(&hmcdrv_dev.dev, hmcdrv_dev_no, 1); 304 + 305 + if (rc) 306 + goto out_unreg; 307 + 308 + /* At this point the character device exists in the kernel (see 309 + * /proc/devices), but not under /dev nor /sys/devices/virtual. So 310 + * we have to create an associated class (see /sys/class). 311 + */ 312 + hmcdrv_dev_class = class_create(THIS_MODULE, HMCDRV_DEV_CLASS); 313 + 314 + if (IS_ERR(hmcdrv_dev_class)) { 315 + rc = PTR_ERR(hmcdrv_dev_class); 316 + goto out_devdel; 317 + } 318 + 319 + /* Finally a device node in /dev has to be established (as 'mkdev' 320 + * does from the command line). Notice that assignment of a device 321 + * node name/mode function is optional (only for mode != 0600). 322 + */ 323 + hmcdrv_dev.mode = 0; /* "unset" */ 324 + hmcdrv_dev_class->devnode = hmcdrv_dev_name; 325 + 326 + dev = device_create(hmcdrv_dev_class, NULL, hmcdrv_dev_no, NULL, 327 + "%s", HMCDRV_DEV_NAME); 328 + if (!IS_ERR(dev)) 329 + return 0; 330 + 331 + rc = PTR_ERR(dev); 332 + class_destroy(hmcdrv_dev_class); 333 + hmcdrv_dev_class = NULL; 334 + 335 + out_devdel: 336 + cdev_del(&hmcdrv_dev.dev); 337 + 338 + out_unreg: 339 + unregister_chrdev_region(hmcdrv_dev_no, 1); 340 + 341 + out_err: 342 + 343 + #else /* !HMCDRV_DEV_CLASS */ 344 + hmcdrv_dev.dev.minor = MISC_DYNAMIC_MINOR; 345 + hmcdrv_dev.dev.name = HMCDRV_DEV_NAME; 346 + hmcdrv_dev.dev.fops = &hmcdrv_dev_fops; 347 + hmcdrv_dev.dev.mode = 0; /* finally produces 0600 */ 348 + rc = misc_register(&hmcdrv_dev.dev); 349 + #endif /* HMCDRV_DEV_CLASS */ 350 + 351 + return rc; 352 + } 353 + 354 + /** 355 + * hmcdrv_dev_exit() - destroys a HMC drive CD/DVD device 356 + */ 357 + void hmcdrv_dev_exit(void) 358 + { 359 + #ifdef HMCDRV_DEV_CLASS 360 + if (!IS_ERR_OR_NULL(hmcdrv_dev_class)) { 361 + device_destroy(hmcdrv_dev_class, hmcdrv_dev_no); 362 + class_destroy(hmcdrv_dev_class); 363 + } 364 + 365 + cdev_del(&hmcdrv_dev.dev); 366 + unregister_chrdev_region(hmcdrv_dev_no, 1); 367 + #else /* !HMCDRV_DEV_CLASS */ 368 + misc_deregister(&hmcdrv_dev.dev); 369 + #endif /* HMCDRV_DEV_CLASS */ 370 + }
+14
drivers/s390/char/hmcdrv_dev.h
··· 1 + /* 2 + * SE/HMC Drive FTP Device 3 + * 4 + * Copyright IBM Corp. 2013 5 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 6 + */ 7 + 8 + #ifndef __HMCDRV_DEV_H__ 9 + #define __HMCDRV_DEV_H__ 10 + 11 + int hmcdrv_dev_init(void); 12 + void hmcdrv_dev_exit(void); 13 + 14 + #endif /* __HMCDRV_DEV_H__ */
+343
drivers/s390/char/hmcdrv_ftp.c
··· 1 + /* 2 + * HMC Drive FTP Services 3 + * 4 + * Copyright IBM Corp. 2013 5 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 6 + */ 7 + 8 + #define KMSG_COMPONENT "hmcdrv" 9 + #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/slab.h> 13 + #include <linux/uaccess.h> 14 + #include <linux/export.h> 15 + 16 + #include <linux/ctype.h> 17 + #include <linux/crc16.h> 18 + 19 + #include "hmcdrv_ftp.h" 20 + #include "hmcdrv_cache.h" 21 + #include "sclp_ftp.h" 22 + #include "diag_ftp.h" 23 + 24 + /** 25 + * struct hmcdrv_ftp_ops - HMC drive FTP operations 26 + * @startup: startup function 27 + * @shutdown: shutdown function 28 + * @cmd: FTP transfer function 29 + */ 30 + struct hmcdrv_ftp_ops { 31 + int (*startup)(void); 32 + void (*shutdown)(void); 33 + ssize_t (*transfer)(const struct hmcdrv_ftp_cmdspec *ftp, 34 + size_t *fsize); 35 + }; 36 + 37 + static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len); 38 + static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp); 39 + 40 + static struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */ 41 + static DEFINE_MUTEX(hmcdrv_ftp_mutex); /* mutex for hmcdrv_ftp_funcs */ 42 + static unsigned hmcdrv_ftp_refcnt; /* start/shutdown reference counter */ 43 + 44 + /** 45 + * hmcdrv_ftp_cmd_getid() - determine FTP command ID from a command string 46 + * @cmd: FTP command string (NOT zero-terminated) 47 + * @len: length of FTP command string in @cmd 48 + */ 49 + static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len) 50 + { 51 + /* HMC FTP command descriptor */ 52 + struct hmcdrv_ftp_cmd_desc { 53 + const char *str; /* command string */ 54 + enum hmcdrv_ftp_cmdid cmd; /* associated command as enum */ 55 + }; 56 + 57 + /* Description of all HMC drive FTP commands 58 + * 59 + * Notes: 60 + * 1. Array size should be a prime number. 61 + * 2. Do not change the order of commands in table (because the 62 + * index is determined by CRC % ARRAY_SIZE). 63 + * 3. Original command 'nlist' was renamed, else the CRC would 64 + * collide with 'append' (see point 2). 65 + */ 66 + static const struct hmcdrv_ftp_cmd_desc ftpcmds[7] = { 67 + 68 + {.str = "get", /* [0] get (CRC = 0x68eb) */ 69 + .cmd = HMCDRV_FTP_GET}, 70 + {.str = "dir", /* [1] dir (CRC = 0x6a9e) */ 71 + .cmd = HMCDRV_FTP_DIR}, 72 + {.str = "delete", /* [2] delete (CRC = 0x53ae) */ 73 + .cmd = HMCDRV_FTP_DELETE}, 74 + {.str = "nls", /* [3] nls (CRC = 0xf87c) */ 75 + .cmd = HMCDRV_FTP_NLIST}, 76 + {.str = "put", /* [4] put (CRC = 0xac56) */ 77 + .cmd = HMCDRV_FTP_PUT}, 78 + {.str = "append", /* [5] append (CRC = 0xf56e) */ 79 + .cmd = HMCDRV_FTP_APPEND}, 80 + {.str = NULL} /* [6] unused */ 81 + }; 82 + 83 + const struct hmcdrv_ftp_cmd_desc *pdesc; 84 + 85 + u16 crc = 0xffffU; 86 + 87 + if (len == 0) 88 + return HMCDRV_FTP_NOOP; /* error indiactor */ 89 + 90 + crc = crc16(crc, cmd, len); 91 + pdesc = ftpcmds + (crc % ARRAY_SIZE(ftpcmds)); 92 + pr_debug("FTP command '%s' has CRC 0x%04x, at table pos. %lu\n", 93 + cmd, crc, (crc % ARRAY_SIZE(ftpcmds))); 94 + 95 + if (!pdesc->str || strncmp(pdesc->str, cmd, len)) 96 + return HMCDRV_FTP_NOOP; 97 + 98 + pr_debug("FTP command '%s' found, with ID %d\n", 99 + pdesc->str, pdesc->cmd); 100 + 101 + return pdesc->cmd; 102 + } 103 + 104 + /** 105 + * hmcdrv_ftp_parse() - HMC drive FTP command parser 106 + * @cmd: FTP command string "<cmd> <filename>" 107 + * @ftp: Pointer to FTP command specification buffer (output) 108 + * 109 + * Return: 0 on success, else a (negative) error code 110 + */ 111 + static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp) 112 + { 113 + char *start; 114 + int argc = 0; 115 + 116 + ftp->id = HMCDRV_FTP_NOOP; 117 + ftp->fname = NULL; 118 + 119 + while (*cmd != '\0') { 120 + 121 + while (isspace(*cmd)) 122 + ++cmd; 123 + 124 + if (*cmd == '\0') 125 + break; 126 + 127 + start = cmd; 128 + 129 + switch (argc) { 130 + case 0: /* 1st argument (FTP command) */ 131 + while ((*cmd != '\0') && !isspace(*cmd)) 132 + ++cmd; 133 + ftp->id = hmcdrv_ftp_cmd_getid(start, cmd - start); 134 + break; 135 + case 1: /* 2nd / last argument (rest of line) */ 136 + while ((*cmd != '\0') && !iscntrl(*cmd)) 137 + ++cmd; 138 + ftp->fname = start; 139 + /* fall through */ 140 + default: 141 + *cmd = '\0'; 142 + break; 143 + } /* switch */ 144 + 145 + ++argc; 146 + } /* while */ 147 + 148 + if (!ftp->fname || (ftp->id == HMCDRV_FTP_NOOP)) 149 + return -EINVAL; 150 + 151 + return 0; 152 + } 153 + 154 + /** 155 + * hmcdrv_ftp_do() - perform a HMC drive FTP, with data from kernel-space 156 + * @ftp: pointer to FTP command specification 157 + * 158 + * Return: number of bytes read/written or a negative error code 159 + */ 160 + ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp) 161 + { 162 + ssize_t len; 163 + 164 + mutex_lock(&hmcdrv_ftp_mutex); 165 + 166 + if (hmcdrv_ftp_funcs && hmcdrv_ftp_refcnt) { 167 + pr_debug("starting transfer, cmd %d for '%s' at %lld with %zd bytes\n", 168 + ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); 169 + len = hmcdrv_cache_cmd(ftp, hmcdrv_ftp_funcs->transfer); 170 + } else { 171 + len = -ENXIO; 172 + } 173 + 174 + mutex_unlock(&hmcdrv_ftp_mutex); 175 + return len; 176 + } 177 + EXPORT_SYMBOL(hmcdrv_ftp_do); 178 + 179 + /** 180 + * hmcdrv_ftp_probe() - probe for the HMC drive FTP service 181 + * 182 + * Return: 0 if service is available, else an (negative) error code 183 + */ 184 + int hmcdrv_ftp_probe(void) 185 + { 186 + int rc; 187 + 188 + struct hmcdrv_ftp_cmdspec ftp = { 189 + .id = HMCDRV_FTP_NOOP, 190 + .ofs = 0, 191 + .fname = "", 192 + .len = PAGE_SIZE 193 + }; 194 + 195 + ftp.buf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 196 + 197 + if (!ftp.buf) 198 + return -ENOMEM; 199 + 200 + rc = hmcdrv_ftp_startup(); 201 + 202 + if (rc) 203 + return rc; 204 + 205 + rc = hmcdrv_ftp_do(&ftp); 206 + free_page((unsigned long) ftp.buf); 207 + hmcdrv_ftp_shutdown(); 208 + 209 + switch (rc) { 210 + case -ENOENT: /* no such file/media or currently busy, */ 211 + case -EBUSY: /* but service seems to be available */ 212 + rc = 0; 213 + break; 214 + default: /* leave 'rc' as it is for [0, -EPERM, -E...] */ 215 + if (rc > 0) 216 + rc = 0; /* clear length (success) */ 217 + break; 218 + } /* switch */ 219 + 220 + return rc; 221 + } 222 + EXPORT_SYMBOL(hmcdrv_ftp_probe); 223 + 224 + /** 225 + * hmcdrv_ftp_cmd() - Perform a HMC drive FTP, with data from user-space 226 + * 227 + * @cmd: FTP command string "<cmd> <filename>" 228 + * @offset: file position to read/write 229 + * @buf: user-space buffer for read/written directory/file 230 + * @len: size of @buf (read/dir) or number of bytes to write 231 + * 232 + * This function must not be called before hmcdrv_ftp_startup() was called. 233 + * 234 + * Return: number of bytes read/written or a negative error code 235 + */ 236 + ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset, 237 + char __user *buf, size_t len) 238 + { 239 + int order; 240 + 241 + struct hmcdrv_ftp_cmdspec ftp = {.len = len, .ofs = offset}; 242 + ssize_t retlen = hmcdrv_ftp_parse(cmd, &ftp); 243 + 244 + if (retlen) 245 + return retlen; 246 + 247 + order = get_order(ftp.len); 248 + ftp.buf = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order); 249 + 250 + if (!ftp.buf) 251 + return -ENOMEM; 252 + 253 + switch (ftp.id) { 254 + case HMCDRV_FTP_DIR: 255 + case HMCDRV_FTP_NLIST: 256 + case HMCDRV_FTP_GET: 257 + retlen = hmcdrv_ftp_do(&ftp); 258 + 259 + if ((retlen >= 0) && 260 + copy_to_user(buf, ftp.buf, retlen)) 261 + retlen = -EFAULT; 262 + break; 263 + 264 + case HMCDRV_FTP_PUT: 265 + case HMCDRV_FTP_APPEND: 266 + if (!copy_from_user(ftp.buf, buf, ftp.len)) 267 + retlen = hmcdrv_ftp_do(&ftp); 268 + else 269 + retlen = -EFAULT; 270 + break; 271 + 272 + case HMCDRV_FTP_DELETE: 273 + retlen = hmcdrv_ftp_do(&ftp); 274 + break; 275 + 276 + default: 277 + retlen = -EOPNOTSUPP; 278 + break; 279 + } 280 + 281 + free_pages((unsigned long) ftp.buf, order); 282 + return retlen; 283 + } 284 + 285 + /** 286 + * hmcdrv_ftp_startup() - startup of HMC drive FTP functionality for a 287 + * dedicated (owner) instance 288 + * 289 + * Return: 0 on success, else an (negative) error code 290 + */ 291 + int hmcdrv_ftp_startup(void) 292 + { 293 + static struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = { 294 + .startup = diag_ftp_startup, 295 + .shutdown = diag_ftp_shutdown, 296 + .transfer = diag_ftp_cmd 297 + }; 298 + 299 + static struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = { 300 + .startup = sclp_ftp_startup, 301 + .shutdown = sclp_ftp_shutdown, 302 + .transfer = sclp_ftp_cmd 303 + }; 304 + 305 + int rc = 0; 306 + 307 + mutex_lock(&hmcdrv_ftp_mutex); /* block transfers while start-up */ 308 + 309 + if (hmcdrv_ftp_refcnt == 0) { 310 + if (MACHINE_IS_VM) 311 + hmcdrv_ftp_funcs = &hmcdrv_ftp_zvm; 312 + else if (MACHINE_IS_LPAR || MACHINE_IS_KVM) 313 + hmcdrv_ftp_funcs = &hmcdrv_ftp_lpar; 314 + else 315 + rc = -EOPNOTSUPP; 316 + 317 + if (hmcdrv_ftp_funcs) 318 + rc = hmcdrv_ftp_funcs->startup(); 319 + } 320 + 321 + if (!rc) 322 + ++hmcdrv_ftp_refcnt; 323 + 324 + mutex_unlock(&hmcdrv_ftp_mutex); 325 + return rc; 326 + } 327 + EXPORT_SYMBOL(hmcdrv_ftp_startup); 328 + 329 + /** 330 + * hmcdrv_ftp_shutdown() - shutdown of HMC drive FTP functionality for a 331 + * dedicated (owner) instance 332 + */ 333 + void hmcdrv_ftp_shutdown(void) 334 + { 335 + mutex_lock(&hmcdrv_ftp_mutex); 336 + --hmcdrv_ftp_refcnt; 337 + 338 + if ((hmcdrv_ftp_refcnt == 0) && hmcdrv_ftp_funcs) 339 + hmcdrv_ftp_funcs->shutdown(); 340 + 341 + mutex_unlock(&hmcdrv_ftp_mutex); 342 + } 343 + EXPORT_SYMBOL(hmcdrv_ftp_shutdown);
+63
drivers/s390/char/hmcdrv_ftp.h
··· 1 + /* 2 + * SE/HMC Drive FTP Services 3 + * 4 + * Copyright IBM Corp. 2013 5 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 6 + */ 7 + 8 + #ifndef __HMCDRV_FTP_H__ 9 + #define __HMCDRV_FTP_H__ 10 + 11 + #include <linux/types.h> /* size_t, loff_t */ 12 + 13 + /* 14 + * HMC drive FTP Service max. length of path (w/ EOS) 15 + */ 16 + #define HMCDRV_FTP_FIDENT_MAX 192 17 + 18 + /** 19 + * enum hmcdrv_ftp_cmdid - HMC drive FTP commands 20 + * @HMCDRV_FTP_NOOP: do nothing (only for probing) 21 + * @HMCDRV_FTP_GET: read a file 22 + * @HMCDRV_FTP_PUT: (over-) write a file 23 + * @HMCDRV_FTP_APPEND: append to a file 24 + * @HMCDRV_FTP_DIR: list directory long (ls -l) 25 + * @HMCDRV_FTP_NLIST: list files, no directories (name list) 26 + * @HMCDRV_FTP_DELETE: delete a file 27 + * @HMCDRV_FTP_CANCEL: cancel operation (SCLP/LPAR only) 28 + */ 29 + enum hmcdrv_ftp_cmdid { 30 + HMCDRV_FTP_NOOP = 0, 31 + HMCDRV_FTP_GET = 1, 32 + HMCDRV_FTP_PUT = 2, 33 + HMCDRV_FTP_APPEND = 3, 34 + HMCDRV_FTP_DIR = 4, 35 + HMCDRV_FTP_NLIST = 5, 36 + HMCDRV_FTP_DELETE = 6, 37 + HMCDRV_FTP_CANCEL = 7 38 + }; 39 + 40 + /** 41 + * struct hmcdrv_ftp_cmdspec - FTP command specification 42 + * @id: FTP command ID 43 + * @ofs: offset in file 44 + * @fname: filename (ASCII), null-terminated 45 + * @buf: kernel-space transfer data buffer, 4k aligned 46 + * @len: (max) number of bytes to transfer from/to @buf 47 + */ 48 + struct hmcdrv_ftp_cmdspec { 49 + enum hmcdrv_ftp_cmdid id; 50 + loff_t ofs; 51 + const char *fname; 52 + void __kernel *buf; 53 + size_t len; 54 + }; 55 + 56 + int hmcdrv_ftp_startup(void); 57 + void hmcdrv_ftp_shutdown(void); 58 + int hmcdrv_ftp_probe(void); 59 + ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp); 60 + ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset, 61 + char __user *buf, size_t len); 62 + 63 + #endif /* __HMCDRV_FTP_H__ */
+64
drivers/s390/char/hmcdrv_mod.c
··· 1 + /* 2 + * HMC Drive DVD Module 3 + * 4 + * Copyright IBM Corp. 2013 5 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 6 + */ 7 + 8 + #define KMSG_COMPONENT "hmcdrv" 9 + #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/moduleparam.h> 14 + #include <linux/version.h> 15 + #include <linux/stat.h> 16 + 17 + #include "hmcdrv_ftp.h" 18 + #include "hmcdrv_dev.h" 19 + #include "hmcdrv_cache.h" 20 + 21 + MODULE_LICENSE("GPL"); 22 + MODULE_AUTHOR("Copyright 2013 IBM Corporation"); 23 + MODULE_DESCRIPTION("HMC drive DVD access"); 24 + 25 + /* 26 + * module parameter 'cachesize' 27 + */ 28 + static size_t hmcdrv_mod_cachesize = HMCDRV_CACHE_SIZE_DFLT; 29 + module_param_named(cachesize, hmcdrv_mod_cachesize, ulong, S_IRUGO); 30 + 31 + /** 32 + * hmcdrv_mod_init() - module init function 33 + */ 34 + static int __init hmcdrv_mod_init(void) 35 + { 36 + int rc = hmcdrv_ftp_probe(); /* perform w/o cache */ 37 + 38 + if (rc) 39 + return rc; 40 + 41 + rc = hmcdrv_cache_startup(hmcdrv_mod_cachesize); 42 + 43 + if (rc) 44 + return rc; 45 + 46 + rc = hmcdrv_dev_init(); 47 + 48 + if (rc) 49 + hmcdrv_cache_shutdown(); 50 + 51 + return rc; 52 + } 53 + 54 + /** 55 + * hmcdrv_mod_exit() - module exit function 56 + */ 57 + static void __exit hmcdrv_mod_exit(void) 58 + { 59 + hmcdrv_dev_exit(); 60 + hmcdrv_cache_shutdown(); 61 + } 62 + 63 + module_init(hmcdrv_mod_init); 64 + module_exit(hmcdrv_mod_exit);
+2
drivers/s390/char/sclp.h
··· 19 19 20 20 #define EVTYP_OPCMD 0x01 21 21 #define EVTYP_MSG 0x02 22 + #define EVTYP_DIAG_TEST 0x07 22 23 #define EVTYP_STATECHANGE 0x08 23 24 #define EVTYP_PMSGCMD 0x09 24 25 #define EVTYP_CNTLPROGOPCMD 0x20 ··· 33 32 34 33 #define EVTYP_OPCMD_MASK 0x80000000 35 34 #define EVTYP_MSG_MASK 0x40000000 35 + #define EVTYP_DIAG_TEST_MASK 0x02000000 36 36 #define EVTYP_STATECHANGE_MASK 0x01000000 37 37 #define EVTYP_PMSGCMD_MASK 0x00800000 38 38 #define EVTYP_CTLPROGOPCMD_MASK 0x00000001
+89
drivers/s390/char/sclp_diag.h
··· 1 + /* 2 + * Copyright IBM Corp. 2013 3 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 4 + */ 5 + 6 + #ifndef _SCLP_DIAG_H 7 + #define _SCLP_DIAG_H 8 + 9 + #include <linux/types.h> 10 + 11 + /* return codes for Diagnostic Test FTP Service, as indicated in member 12 + * sclp_diag_ftp::ldflg 13 + */ 14 + #define SCLP_DIAG_FTP_OK 0x80U /* success */ 15 + #define SCLP_DIAG_FTP_LDFAIL 0x01U /* load failed */ 16 + #define SCLP_DIAG_FTP_LDNPERM 0x02U /* not allowed */ 17 + #define SCLP_DIAG_FTP_LDRUNS 0x03U /* LD runs */ 18 + #define SCLP_DIAG_FTP_LDNRUNS 0x04U /* LD does not run */ 19 + 20 + #define SCLP_DIAG_FTP_XPCX 0x80 /* PCX communication code */ 21 + #define SCLP_DIAG_FTP_ROUTE 4 /* routing code for new FTP service */ 22 + 23 + /* 24 + * length of Diagnostic Test FTP Service event buffer 25 + */ 26 + #define SCLP_DIAG_FTP_EVBUF_LEN \ 27 + (offsetof(struct sclp_diag_evbuf, mdd) + \ 28 + sizeof(struct sclp_diag_ftp)) 29 + 30 + /** 31 + * struct sclp_diag_ftp - Diagnostic Test FTP Service model-dependent data 32 + * @pcx: code for PCX communication (should be 0x80) 33 + * @ldflg: load flag (see defines above) 34 + * @cmd: FTP command 35 + * @pgsize: page size (0 = 4kB, 1 = large page size) 36 + * @srcflg: source flag 37 + * @spare: reserved (zeroes) 38 + * @offset: file offset 39 + * @fsize: file size 40 + * @length: buffer size resp. bytes transferred 41 + * @failaddr: failing address 42 + * @bufaddr: buffer address, virtual 43 + * @asce: region or segment table designation 44 + * @fident: file name (ASCII, zero-terminated) 45 + */ 46 + struct sclp_diag_ftp { 47 + u8 pcx; 48 + u8 ldflg; 49 + u8 cmd; 50 + u8 pgsize; 51 + u8 srcflg; 52 + u8 spare; 53 + u64 offset; 54 + u64 fsize; 55 + u64 length; 56 + u64 failaddr; 57 + u64 bufaddr; 58 + u64 asce; 59 + 60 + u8 fident[256]; 61 + } __packed; 62 + 63 + /** 64 + * struct sclp_diag_evbuf - Diagnostic Test (ET7) Event Buffer 65 + * @hdr: event buffer header 66 + * @route: diagnostic route 67 + * @mdd: model-dependent data (@route dependent) 68 + */ 69 + struct sclp_diag_evbuf { 70 + struct evbuf_header hdr; 71 + u16 route; 72 + 73 + union { 74 + struct sclp_diag_ftp ftp; 75 + } mdd; 76 + } __packed; 77 + 78 + /** 79 + * struct sclp_diag_sccb - Diagnostic Test (ET7) SCCB 80 + * @hdr: SCCB header 81 + * @evbuf: event buffer 82 + */ 83 + struct sclp_diag_sccb { 84 + 85 + struct sccb_header hdr; 86 + struct sclp_diag_evbuf evbuf; 87 + } __packed; 88 + 89 + #endif /* _SCLP_DIAG_H */
+275
drivers/s390/char/sclp_ftp.c
··· 1 + /* 2 + * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR 3 + * 4 + * Copyright IBM Corp. 2013 5 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 6 + * 7 + */ 8 + 9 + #define KMSG_COMPONENT "hmcdrv" 10 + #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 11 + 12 + #include <linux/kernel.h> 13 + #include <linux/mm.h> 14 + #include <linux/slab.h> 15 + #include <linux/io.h> 16 + #include <linux/wait.h> 17 + #include <linux/string.h> 18 + #include <linux/jiffies.h> 19 + #include <asm/sysinfo.h> 20 + #include <asm/ebcdic.h> 21 + 22 + #include "sclp.h" 23 + #include "sclp_diag.h" 24 + #include "sclp_ftp.h" 25 + 26 + static DECLARE_COMPLETION(sclp_ftp_rx_complete); 27 + static u8 sclp_ftp_ldflg; 28 + static u64 sclp_ftp_fsize; 29 + static u64 sclp_ftp_length; 30 + 31 + /** 32 + * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback 33 + */ 34 + static void sclp_ftp_txcb(struct sclp_req *req, void *data) 35 + { 36 + struct completion *completion = data; 37 + 38 + #ifdef DEBUG 39 + pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n", 40 + req->sccb, 24, req->sccb); 41 + #endif 42 + complete(completion); 43 + } 44 + 45 + /** 46 + * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback 47 + */ 48 + static void sclp_ftp_rxcb(struct evbuf_header *evbuf) 49 + { 50 + struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf; 51 + 52 + /* 53 + * Check for Diagnostic Test FTP Service 54 + */ 55 + if (evbuf->type != EVTYP_DIAG_TEST || 56 + diag->route != SCLP_DIAG_FTP_ROUTE || 57 + diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX || 58 + evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN) 59 + return; 60 + 61 + #ifdef DEBUG 62 + pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n", 63 + evbuf, 24, evbuf); 64 + #endif 65 + 66 + /* 67 + * Because the event buffer is located in a page which is owned 68 + * by the SCLP core, all data of interest must be copied. The 69 + * error indication is in 'sclp_ftp_ldflg' 70 + */ 71 + sclp_ftp_ldflg = diag->mdd.ftp.ldflg; 72 + sclp_ftp_fsize = diag->mdd.ftp.fsize; 73 + sclp_ftp_length = diag->mdd.ftp.length; 74 + 75 + complete(&sclp_ftp_rx_complete); 76 + } 77 + 78 + /** 79 + * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request 80 + * @ftp: pointer to FTP descriptor 81 + * 82 + * Return: 0 on success, else a (negative) error code 83 + */ 84 + static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) 85 + { 86 + struct completion completion; 87 + struct sclp_diag_sccb *sccb; 88 + struct sclp_req *req; 89 + size_t len; 90 + int rc; 91 + 92 + req = kzalloc(sizeof(*req), GFP_KERNEL); 93 + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 94 + if (!req || !sccb) { 95 + rc = -ENOMEM; 96 + goto out_free; 97 + } 98 + 99 + sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN + 100 + sizeof(struct sccb_header); 101 + sccb->evbuf.hdr.type = EVTYP_DIAG_TEST; 102 + sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN; 103 + sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */ 104 + sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE; 105 + sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX; 106 + sccb->evbuf.mdd.ftp.srcflg = 0; 107 + sccb->evbuf.mdd.ftp.pgsize = 0; 108 + sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE; 109 + sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL; 110 + sccb->evbuf.mdd.ftp.fsize = 0; 111 + sccb->evbuf.mdd.ftp.cmd = ftp->id; 112 + sccb->evbuf.mdd.ftp.offset = ftp->ofs; 113 + sccb->evbuf.mdd.ftp.length = ftp->len; 114 + sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf); 115 + 116 + len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, 117 + HMCDRV_FTP_FIDENT_MAX); 118 + if (len >= HMCDRV_FTP_FIDENT_MAX) { 119 + rc = -EINVAL; 120 + goto out_free; 121 + } 122 + 123 + req->command = SCLP_CMDW_WRITE_EVENT_DATA; 124 + req->sccb = sccb; 125 + req->status = SCLP_REQ_FILLED; 126 + req->callback = sclp_ftp_txcb; 127 + req->callback_data = &completion; 128 + 129 + init_completion(&completion); 130 + 131 + rc = sclp_add_request(req); 132 + if (rc) 133 + goto out_free; 134 + 135 + /* Wait for end of ftp sclp command. */ 136 + wait_for_completion(&completion); 137 + 138 + #ifdef DEBUG 139 + pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n", 140 + sccb->hdr.response_code, sccb->evbuf.hdr.flags); 141 + #endif 142 + 143 + /* 144 + * Check if sclp accepted the request. The data transfer runs 145 + * asynchronously and the completion is indicated with an 146 + * sclp ET7 event. 147 + */ 148 + if (req->status != SCLP_REQ_DONE || 149 + (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */ 150 + (sccb->hdr.response_code & 0xffU) != 0x20U) { 151 + rc = -EIO; 152 + } 153 + 154 + out_free: 155 + free_page((unsigned long) sccb); 156 + kfree(req); 157 + return rc; 158 + } 159 + 160 + /** 161 + * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command 162 + * @ftp: pointer to FTP command specification 163 + * @fsize: return of file size (or NULL if undesirable) 164 + * 165 + * Attention: Notice that this function is not reentrant - so the caller 166 + * must ensure locking. 167 + * 168 + * Return: number of bytes read/written or a (negative) error code 169 + */ 170 + ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) 171 + { 172 + ssize_t len; 173 + #ifdef DEBUG 174 + unsigned long start_jiffies; 175 + 176 + pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n", 177 + ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); 178 + start_jiffies = jiffies; 179 + #endif 180 + 181 + init_completion(&sclp_ftp_rx_complete); 182 + 183 + /* Start ftp sclp command. */ 184 + len = sclp_ftp_et7(ftp); 185 + if (len) 186 + goto out_unlock; 187 + 188 + /* 189 + * There is no way to cancel the sclp ET7 request, the code 190 + * needs to wait unconditionally until the transfer is complete. 191 + */ 192 + wait_for_completion(&sclp_ftp_rx_complete); 193 + 194 + #ifdef DEBUG 195 + pr_debug("completed SCLP (ET7) request after %lu ms (all)\n", 196 + (jiffies - start_jiffies) * 1000 / HZ); 197 + pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n", 198 + sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize); 199 + #endif 200 + 201 + switch (sclp_ftp_ldflg) { 202 + case SCLP_DIAG_FTP_OK: 203 + len = sclp_ftp_length; 204 + if (fsize) 205 + *fsize = sclp_ftp_fsize; 206 + break; 207 + case SCLP_DIAG_FTP_LDNPERM: 208 + len = -EPERM; 209 + break; 210 + case SCLP_DIAG_FTP_LDRUNS: 211 + len = -EBUSY; 212 + break; 213 + case SCLP_DIAG_FTP_LDFAIL: 214 + len = -ENOENT; 215 + break; 216 + default: 217 + len = -EIO; 218 + break; 219 + } 220 + 221 + out_unlock: 222 + return len; 223 + } 224 + 225 + /* 226 + * ET7 event listener 227 + */ 228 + static struct sclp_register sclp_ftp_event = { 229 + .send_mask = EVTYP_DIAG_TEST_MASK, /* want tx events */ 230 + .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */ 231 + .receiver_fn = sclp_ftp_rxcb, /* async callback (rx) */ 232 + .state_change_fn = NULL, 233 + .pm_event_fn = NULL, 234 + }; 235 + 236 + /** 237 + * sclp_ftp_startup() - startup of FTP services, when running on LPAR 238 + */ 239 + int sclp_ftp_startup(void) 240 + { 241 + #ifdef DEBUG 242 + unsigned long info; 243 + #endif 244 + int rc; 245 + 246 + rc = sclp_register(&sclp_ftp_event); 247 + if (rc) 248 + return rc; 249 + 250 + #ifdef DEBUG 251 + info = get_zeroed_page(GFP_KERNEL); 252 + 253 + if (info != 0) { 254 + struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; 255 + 256 + if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */ 257 + info222->name[sizeof(info222->name) - 1] = '\0'; 258 + EBCASC_500(info222->name, sizeof(info222->name) - 1); 259 + pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n", 260 + info222->lpar_number, info222->name); 261 + } 262 + 263 + free_page(info); 264 + } 265 + #endif /* DEBUG */ 266 + return 0; 267 + } 268 + 269 + /** 270 + * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR 271 + */ 272 + void sclp_ftp_shutdown(void) 273 + { 274 + sclp_unregister(&sclp_ftp_event); 275 + }
+21
drivers/s390/char/sclp_ftp.h
··· 1 + /* 2 + * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR 3 + * 4 + * Notice that all functions exported here are not reentrant. 5 + * So usage should be exclusive, ensured by the caller (e.g. using a 6 + * mutex). 7 + * 8 + * Copyright IBM Corp. 2013 9 + * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 10 + */ 11 + 12 + #ifndef __SCLP_FTP_H__ 13 + #define __SCLP_FTP_H__ 14 + 15 + #include "hmcdrv_ftp.h" 16 + 17 + int sclp_ftp_startup(void); 18 + void sclp_ftp_shutdown(void); 19 + ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize); 20 + 21 + #endif /* __SCLP_FTP_H__ */