/** * hvc_iucv_get_private() - Return a struct hvc_iucv_private instance. * @num: The HVC virtual terminal number (vtermno) * * This function returns the struct hvc_iucv_private instance that corresponds * to the HVC virtual terminal number specified as parameter @num.
*/ staticstruct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
{ if (num > hvc_iucv_devices) return NULL; return hvc_iucv_table[num];
}
/** * alloc_tty_buffer() - Return a new struct iucv_tty_buffer element. * @size: Size of the internal buffer used to store data. * @flags: Memory allocation flags passed to mempool. * * This function allocates a new struct iucv_tty_buffer element and, optionally, * allocates an internal data buffer with the specified size @size. * The internal data buffer is always allocated with GFP_DMA which is * required for receiving and sending data with IUCV. * Note: The total message size arises from the internal buffer size and the * members of the iucv_tty_msg structure. * The function returns NULL if memory allocation has failed.
*/ staticstruct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags)
{ struct iucv_tty_buffer *bufp;
/** * hvc_iucv_write() - Receive IUCV message & write data to HVC buffer. * @priv: Pointer to struct hvc_iucv_private * @buf: HVC buffer for writing received terminal data. * @count: HVC buffer size. * @has_more_data: Pointer to an int variable. * * The function picks up pending messages from the input queue and receives * the message data that is then written to the specified buffer @buf. * If the buffer size @count is less than the data message size, the * message is kept on the input queue and @has_more_data is set to 1. * If all message data has been written, the message is removed from * the input queue. * * The function returns the number of bytes written to the terminal, zero if * there are no pending data messages available or if there is no established * IUCV path. * If the IUCV path has been severed, then -EPIPE is returned to cause a * hang up (that is issued by the HVC layer).
*/ static ssize_t hvc_iucv_write(struct hvc_iucv_private *priv,
u8 *buf, size_t count, int *has_more_data)
{ struct iucv_tty_buffer *rb;
ssize_t written; int rc;
/* immediately return if there is no IUCV connection */ if (priv->iucv_state == IUCV_DISCONN) return 0;
/* if the IUCV path has been severed, return -EPIPE to inform the
* HVC layer to hang up the tty device. */ if (priv->iucv_state == IUCV_SEVERED) return -EPIPE;
/* check if there are pending messages */ if (list_empty(&priv->tty_inqueue)) return 0;
/* receive an iucv message and flip data to the tty (ldisc) */
rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list);
written = 0; if (!rb->mbuf) { /* message not yet received ... */ /* allocate mem to store msg data; if no memory is available
* then leave the buffer on the list and re-try later */
rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC | GFP_DMA); if (!rb->mbuf) return -ENOMEM;
rc = __iucv_message_receive(priv->path, &rb->msg, 0,
rb->mbuf, rb->msg.length, NULL); switch (rc) { case 0: /* Successful */ break; case 2: /* No message found */ case 9: /* Message purged */ break; default:
written = -EIO;
} /* remove buffer if an error has occurred or received data
* is not correct */ if (rc || (rb->mbuf->version != MSG_VERSION) ||
(rb->msg.length != MSG_SIZE(rb->mbuf->datalen))) goto out_remove_buffer;
}
case MSG_TYPE_WINSIZE: if (rb->mbuf->datalen != sizeof(struct winsize)) break; /* The caller must ensure that the hvc is locked, which
* is the case when called from hvc_iucv_get_chars() */
__hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); break;
case MSG_TYPE_ERROR: /* ignored ... */ case MSG_TYPE_TERMENV: /* ignored ... */ case MSG_TYPE_TERMIOS: /* ignored ... */ break;
}
/** * hvc_iucv_get_chars() - HVC get_chars operation. * @vtermno: HVC virtual terminal number. * @buf: Pointer to a buffer to store data * @count: Size of buffer available for writing * * The HVC thread calls this method to read characters from the back-end. * If an IUCV communication path has been established, pending IUCV messages * are received and data is copied into buffer @buf up to @count bytes. * * Locking: The routine gets called under an irqsave() spinlock; and * the routine locks the struct hvc_iucv_private->lock to call * helper functions.
*/ static ssize_t hvc_iucv_get_chars(uint32_t vtermno, u8 *buf, size_t count)
{ struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno);
ssize_t written; int has_more_data;
/* if there are still messages on the queue... schedule another run */ if (has_more_data)
hvc_kick();
return written;
}
/** * hvc_iucv_queue() - Buffer terminal data for sending. * @priv: Pointer to struct hvc_iucv_private instance. * @buf: Buffer containing data to send. * @count: Size of buffer and amount of data to send. * * The function queues data for sending. To actually send the buffered data, * a work queue function is scheduled (with QUEUE_SNDBUF_DELAY). * The function returns the number of data bytes that has been buffered. * * If the device is not connected, data is ignored and the function returns * @count. * If the buffer is full, the function returns 0. * If an existing IUCV communicaton path has been severed, -EPIPE is returned * (that can be passed to HVC layer to cause a tty hangup).
*/ static ssize_t hvc_iucv_queue(struct hvc_iucv_private *priv, const u8 *buf,
size_t count)
{
size_t len;
if (priv->iucv_state == IUCV_DISCONN) return count; /* ignore data */
if (priv->iucv_state == IUCV_SEVERED) return -EPIPE;
len = min_t(size_t, count, SNDBUF_SIZE - priv->sndbuf_len); if (!len) return 0;
if (priv->iucv_state == IUCV_CONNECTED)
schedule_delayed_work(&priv->sndbuf_work, QUEUE_SNDBUF_DELAY);
return len;
}
/** * hvc_iucv_send() - Send an IUCV message containing terminal data. * @priv: Pointer to struct hvc_iucv_private instance. * * If an IUCV communication path has been established, the buffered output data * is sent via an IUCV message and the number of bytes sent is returned. * Returns 0 if there is no established IUCV communication path or * -EPIPE if an existing IUCV communicaton path has been severed.
*/ staticint hvc_iucv_send(struct hvc_iucv_private *priv)
{ struct iucv_tty_buffer *sb; int rc, len;
if (priv->iucv_state == IUCV_SEVERED) return -EPIPE;
if (priv->iucv_state == IUCV_DISCONN) return -EIO;
if (!priv->sndbuf_len) return 0;
/* allocate internal buffer to store msg data and also compute total
* message length */
sb = alloc_tty_buffer(priv->sndbuf_len, GFP_ATOMIC); if (!sb) return -ENOMEM;
rc = __iucv_message_send(priv->path, &sb->msg, 0, 0,
(void *) sb->mbuf, sb->msg.length); if (rc) { /* drop the message here; however we might want to handle
* 0x03 (msg limit reached) by trying again... */
list_del(&sb->list);
destroy_tty_buffer(sb);
}
len = priv->sndbuf_len;
priv->sndbuf_len = 0;
return len;
}
/** * hvc_iucv_sndbuf_work() - Send buffered data over IUCV * @work: Work structure. * * This work queue function sends buffered output data over IUCV and, * if not all buffered data could be sent, reschedules itself.
*/ staticvoid hvc_iucv_sndbuf_work(struct work_struct *work)
{ struct hvc_iucv_private *priv;
/** * hvc_iucv_put_chars() - HVC put_chars operation. * @vtermno: HVC virtual terminal number. * @buf: Pointer to an buffer to read data from * @count: Size of buffer available for reading * * The HVC thread calls this method to write characters to the back-end. * The function calls hvc_iucv_queue() to queue terminal data for sending. * * Locking: The method gets called under an irqsave() spinlock; and * locks struct hvc_iucv_private->lock.
*/ static ssize_t hvc_iucv_put_chars(uint32_t vtermno, const u8 *buf, size_t count)
{ struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); int queued;
/** * hvc_iucv_notifier_add() - HVC notifier for opening a TTY for the first time. * @hp: Pointer to the HVC device (struct hvc_struct) * @id: Additional data (originally passed to hvc_alloc): the index of an struct * hvc_iucv_private instance. * * The function sets the tty state to TTY_OPENED for the struct hvc_iucv_private * instance that is derived from @id. Always returns 0. * * Locking: struct hvc_iucv_private->lock, spin_lock_bh
*/ staticint hvc_iucv_notifier_add(struct hvc_struct *hp, int id)
{ struct hvc_iucv_private *priv;
priv = hvc_iucv_get_private(id); if (!priv) return 0;
/** * tty_outqueue_empty() - Test if the tty outq is empty * @priv: Pointer to struct hvc_iucv_private instance.
*/ staticinlineint tty_outqueue_empty(struct hvc_iucv_private *priv)
{ int rc;
/** * flush_sndbuf_sync() - Flush send buffer and wait for completion * @priv: Pointer to struct hvc_iucv_private instance. * * The routine cancels a pending sndbuf work, calls hvc_iucv_send() * to flush any buffered terminal output data and waits for completion.
*/ staticvoid flush_sndbuf_sync(struct hvc_iucv_private *priv)
{ int sync_wait;
cancel_delayed_work_sync(&priv->sndbuf_work);
spin_lock_bh(&priv->lock);
hvc_iucv_send(priv); /* force sending buffered data */
sync_wait = !list_empty(&priv->tty_outqueue); /* anything queued ? */
spin_unlock_bh(&priv->lock);
if (sync_wait)
wait_event_timeout(priv->sndbuf_waitq,
tty_outqueue_empty(priv), HZ/10);
}
/** * hvc_iucv_hangup() - Sever IUCV path and schedule hvc tty hang up * @priv: Pointer to hvc_iucv_private structure * * This routine severs an existing IUCV communication path and hangs * up the underlying HVC terminal device. * The hang-up occurs only if an IUCV communication path is established; * otherwise there is no need to hang up the terminal device. * * The IUCV HVC hang-up is separated into two steps: * 1. After the IUCV path has been severed, the iucv_state is set to * IUCV_SEVERED. * 2. Later, when the HVC thread calls hvc_iucv_get_chars(), the * IUCV_SEVERED state causes the tty hang-up in the HVC layer. * * If the tty has not yet been opened, clean up the hvc_iucv_private * structure to allow re-connects. * If the tty has been opened, let get_chars() return -EPIPE to signal * the HVC layer to hang up the tty and, if so, wake up the HVC thread * to call get_chars()... * * Special notes on hanging up a HVC terminal instantiated as console: * Hang-up: 1. do_tty_hangup() replaces file ops (= hung_up_tty_fops) * 2. do_tty_hangup() calls tty->ops->close() for console_filp * => no hangup notifier is called by HVC (default) * 2. hvc_close() returns because of tty_hung_up_p(filp) * => no delete notifier is called! * Finally, the back-end is not being notified, thus, the tty session is * kept active (TTY_OPEN) to be ready for re-connects. * * Locking: spin_lock(&priv->lock) w/o disabling bh
*/ staticvoid hvc_iucv_hangup(struct hvc_iucv_private *priv)
{ struct iucv_path *path;
path = NULL;
spin_lock(&priv->lock); if (priv->iucv_state == IUCV_CONNECTED) {
path = priv->path;
priv->path = NULL;
priv->iucv_state = IUCV_SEVERED; if (priv->tty_state == TTY_CLOSED)
hvc_iucv_cleanup(priv); else /* console is special (see above) */ if (priv->is_console) {
hvc_iucv_cleanup(priv);
priv->tty_state = TTY_OPENED;
} else
hvc_kick();
}
spin_unlock(&priv->lock);
/* finally sever path (outside of priv->lock due to lock ordering) */ if (path) {
iucv_path_sever(path, NULL);
iucv_path_free(path);
}
}
/** * hvc_iucv_notifier_hangup() - HVC notifier for TTY hangups. * @hp: Pointer to the HVC device (struct hvc_struct) * @id: Additional data (originally passed to hvc_alloc): * the index of an struct hvc_iucv_private instance. * * This routine notifies the HVC back-end that a tty hangup (carrier loss, * virtual or otherwise) has occurred. * The z/VM IUCV HVC device driver ignores virtual hangups (vhangup()) * to keep an existing IUCV communication path established. * (Background: vhangup() is called from user space (by getty or login) to * disable writing to the tty by other applications). * If the tty has been opened and an established IUCV path has been severed * (we caused the tty hangup), the function calls hvc_iucv_cleanup(). * * Locking: struct hvc_iucv_private->lock
*/ staticvoid hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
{ struct hvc_iucv_private *priv;
priv = hvc_iucv_get_private(id); if (!priv) return;
flush_sndbuf_sync(priv);
spin_lock_bh(&priv->lock); /* NOTE: If the hangup was scheduled by ourself (from the iucv * path_servered callback [IUCV_SEVERED]), we have to clean up * our structure and to set state to TTY_CLOSED. * If the tty was hung up otherwise (e.g. vhangup()), then we * ignore this hangup and keep an established IUCV path open... * (...the reason is that we are not able to connect back to the
* client if we disconnect on hang up) */
priv->tty_state = TTY_CLOSED;
if (priv->iucv_state == IUCV_SEVERED)
hvc_iucv_cleanup(priv);
spin_unlock_bh(&priv->lock);
}
/** * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS * @hp: Pointer the HVC device (struct hvc_struct) * @active: True to raise or false to lower DTR/RTS lines * * This routine notifies the HVC back-end to raise or lower DTR/RTS * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to * drop the IUCV connection (similar to hang up the modem).
*/ staticvoid hvc_iucv_dtr_rts(struct hvc_struct *hp, bool active)
{ struct hvc_iucv_private *priv; struct iucv_path *path;
/* Raising the DTR/RTS is ignored as IUCV connections can be * established at any times.
*/ if (active) return;
priv = hvc_iucv_get_private(hp->vtermno); if (!priv) return;
/* Lowering the DTR/RTS lines disconnects an established IUCV * connection.
*/
flush_sndbuf_sync(priv);
spin_lock_bh(&priv->lock);
path = priv->path; /* save reference to IUCV path */
priv->path = NULL;
priv->iucv_state = IUCV_DISCONN;
spin_unlock_bh(&priv->lock);
/* Sever IUCV path outside of priv->lock due to lock ordering of:
* priv->lock <--> iucv_table_lock */ if (path) {
iucv_path_sever(path, NULL);
iucv_path_free(path);
}
}
/** * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. * @hp: Pointer to the HVC device (struct hvc_struct) * @id: Additional data (originally passed to hvc_alloc): * the index of an struct hvc_iucv_private instance. * * This routine notifies the HVC back-end that the last tty device fd has been * closed. The function cleans up tty resources. The clean-up of the IUCV * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios * control setting. * * Locking: struct hvc_iucv_private->lock
*/ staticvoid hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
{ struct hvc_iucv_private *priv;
priv = hvc_iucv_get_private(id); if (!priv) return;
/** * hvc_iucv_filter_connreq() - Filter connection request based on z/VM user ID * @ipvmid: Originating z/VM user ID (right padded with blanks) * * Returns 0 if the z/VM user ID that is specified with @ipvmid is permitted to * connect, otherwise non-zero.
*/ staticint hvc_iucv_filter_connreq(u8 ipvmid[8])
{ constchar *wildcard, *filter_entry;
size_t i, len;
/* Note: default policy is ACCEPT if no filter is set */ if (!hvc_iucv_filter_size) return 0;
for (i = 0; i < hvc_iucv_filter_size; i++) {
filter_entry = hvc_iucv_filter + (8 * i);
/* If a filter entry contains the filter wildcard character, * reduce the length to match the leading portion of the user * ID only (wildcard match). Characters following the wildcard * are ignored.
*/
wildcard = strnchr(filter_entry, 8, FILTER_WILDCARD_CHAR);
len = (wildcard) ? wildcard - filter_entry : 8; if (0 == memcmp(ipvmid, filter_entry, len)) return 0;
} return 1;
}
/** * hvc_iucv_path_pending() - IUCV handler to process a connection request. * @path: Pending path (struct iucv_path) * @ipvmid: z/VM system identifier of originator * @ipuser: User specified data for this path * (AF_IUCV: port/service name and originator port) * * The function uses the @ipuser data to determine if the pending path belongs * to a terminal managed by this device driver. * If the path belongs to this driver, ensure that the terminal is not accessed * multiple times (only one connection to a terminal is allowed). * If the terminal is not yet connected, the pending path is accepted and is * associated to the appropriate struct hvc_iucv_private instance. * * Returns 0 if @path belongs to a terminal managed by the this device driver; * otherwise returns -ENODEV in order to dispatch this path to other handlers. * * Locking: struct hvc_iucv_private->lock
*/ staticint hvc_iucv_path_pending(struct iucv_path *path, u8 *ipvmid,
u8 *ipuser)
{ struct hvc_iucv_private *priv, *tmp;
u8 wildcard[9] = "lnxhvc "; int i, rc, find_unused;
u8 nuser_data[16];
u8 vm_user_id[9];
/* First, check if the pending path request is managed by this * IUCV handler: * - find a disconnected device if ipuser contains the wildcard * - find the device that matches the terminal ID in ipuser
*/
priv = NULL; for (i = 0; i < hvc_iucv_devices; i++) {
tmp = hvc_iucv_table[i]; if (!tmp) continue;
if (find_unused) {
spin_lock(&tmp->lock); if (tmp->iucv_state == IUCV_DISCONN)
priv = tmp;
spin_unlock(&tmp->lock);
} elseif (!memcmp(tmp->srv_name, ipuser, 8))
priv = tmp; if (priv) break;
} if (!priv) return -ENODEV;
/* Enforce that ipvmid is allowed to connect to us */
read_lock(&hvc_iucv_filter_lock);
rc = hvc_iucv_filter_connreq(ipvmid);
read_unlock(&hvc_iucv_filter_lock); if (rc) {
iucv_path_sever(path, ipuser);
iucv_path_free(path);
memcpy(vm_user_id, ipvmid, 8);
vm_user_id[8] = 0;
pr_info("A connection request from z/VM user ID %s " "was refused\n", vm_user_id); return 0;
}
spin_lock(&priv->lock);
/* If the terminal is already connected or being severed, then sever * this path to enforce that there is only ONE established communication
* path per terminal. */ if (priv->iucv_state != IUCV_DISCONN) {
iucv_path_sever(path, ipuser);
iucv_path_free(path); goto out_path_handled;
}
/** * hvc_iucv_path_severed() - IUCV handler to process a path sever. * @path: Pending path (struct iucv_path) * @ipuser: User specified data for this path * (AF_IUCV: port/service name and originator port) * * This function calls the hvc_iucv_hangup() function for the * respective IUCV HVC terminal. * * Locking: struct hvc_iucv_private->lock
*/ staticvoid hvc_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
{ struct hvc_iucv_private *priv = path->private;
hvc_iucv_hangup(priv);
}
/** * hvc_iucv_msg_pending() - IUCV handler to process an incoming IUCV message. * @path: Pending path (struct iucv_path) * @msg: Pointer to the IUCV message * * The function puts an incoming message on the input queue for later * processing (by hvc_iucv_get_chars() / hvc_iucv_write()). * If the tty has not yet been opened, the message is rejected. * * Locking: struct hvc_iucv_private->lock
*/ staticvoid hvc_iucv_msg_pending(struct iucv_path *path, struct iucv_message *msg)
{ struct hvc_iucv_private *priv = path->private; struct iucv_tty_buffer *rb;
/* reject messages that exceed max size of iucv_tty_msg->datalen */ if (msg->length > MSG_SIZE(MSG_MAX_DATALEN)) {
iucv_message_reject(path, msg); return;
}
spin_lock(&priv->lock);
/* reject messages if tty has not yet been opened */ if (priv->tty_state == TTY_CLOSED) {
iucv_message_reject(path, msg); goto unlock_return;
}
/* allocate tty buffer to save iucv msg only */
rb = alloc_tty_buffer(0, GFP_ATOMIC); if (!rb) {
iucv_message_reject(path, msg); goto unlock_return; /* -ENOMEM */
}
rb->msg = *msg;
list_add_tail(&rb->list, &priv->tty_inqueue);
hvc_kick(); /* wake up hvc thread */
unlock_return:
spin_unlock(&priv->lock);
}
/** * hvc_iucv_msg_complete() - IUCV handler to process message completion * @path: Pending path (struct iucv_path) * @msg: Pointer to the IUCV message * * The function is called upon completion of message delivery to remove the * message from the outqueue. Additional delivery information can be found * msg->audit: rejected messages (0x040000 (IPADRJCT)), and * purged messages (0x010000 (IPADPGNR)). * * Locking: struct hvc_iucv_private->lock
*/ staticvoid hvc_iucv_msg_complete(struct iucv_path *path, struct iucv_message *msg)
{ struct hvc_iucv_private *priv = path->private; struct iucv_tty_buffer *ent, *next;
LIST_HEAD(list_remove);
/** * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance * @id: hvc_iucv_table index * @is_console: Flag if the instance is used as Linux console * * This function allocates a new hvc_iucv_private structure and stores * the instance in hvc_iucv_table at index @id. * Returns 0 on success; otherwise non-zero.
*/ staticint __init hvc_iucv_alloc(int id, unsignedint is_console)
{ struct hvc_iucv_private *priv; char name[9]; int rc;
priv = kzalloc(sizeof(struct hvc_iucv_private), GFP_KERNEL); if (!priv) return -ENOMEM;
/** * hvc_iucv_parse_filter() - Parse filter for a single z/VM user ID * @filter: String containing a comma-separated list of z/VM user IDs * @dest: Location where to store the parsed z/VM user ID
*/ staticconstchar *hvc_iucv_parse_filter(constchar *filter, char *dest)
{ constchar *nextdelim, *residual;
size_t len;
nextdelim = strchr(filter, ','); if (nextdelim) {
len = nextdelim - filter;
residual = nextdelim + 1;
} else {
len = strlen(filter);
residual = filter + len;
}
if (len == 0) return ERR_PTR(-EINVAL);
/* check for '\n' (if called from sysfs) */ if (filter[len - 1] == '\n')
len--;
/* prohibit filter entries containing the wildcard character only */ if (len == 1 && *filter == FILTER_WILDCARD_CHAR) return ERR_PTR(-EINVAL);
if (len > 8) return ERR_PTR(-EINVAL);
/* pad with blanks and save upper case version of user ID */
memset(dest, ' ', 8); while (len--)
dest[len] = toupper(filter[len]); return residual;
}
/** * hvc_iucv_setup_filter() - Set up z/VM user ID filter * @filter: String consisting of a comma-separated list of z/VM user IDs * * The function parses the @filter string and creates an array containing * the list of z/VM user ID filter entries. * Return code 0 means success, -EINVAL if the filter is syntactically * incorrect, -ENOMEM if there was not enough memory to allocate the * filter list array, or -ENOSPC if too many z/VM user IDs have been specified.
*/ staticint hvc_iucv_setup_filter(constchar *val)
{ constchar *residual; int err;
size_t size, count; void *array, *old_filter;
/** * param_set_vmidfilter() - Set z/VM user ID filter parameter * @val: String consisting of a comma-separated list of z/VM user IDs * @kp: Kernel parameter pointing to hvc_iucv_filter array * * The function sets up the z/VM user ID filter specified as comma-separated * list of user IDs in @val. * Note: If it is called early in the boot process, @val is stored and * parsed later in hvc_iucv_init().
*/ staticint param_set_vmidfilter(constchar *val, conststruct kernel_param *kp)
{ int rc;
if (!machine_is_vm() || !hvc_iucv_devices) return -ENODEV;
/** * param_get_vmidfilter() - Get z/VM user ID filter * @buffer: Buffer to store z/VM user ID filter, * (buffer size assumption PAGE_SIZE) * @kp: Kernel parameter pointing to the hvc_iucv_filter array * * The function stores the filter as a comma-separated list of z/VM user IDs * in @buffer. Typically, sysfs routines call this function for attr show.
*/ staticint param_get_vmidfilter(char *buffer, conststruct kernel_param *kp)
{ int rc;
size_t index, len; void *start, *end;
if (!machine_is_vm() || !hvc_iucv_devices) return -ENODEV;
rc = 0;
read_lock_bh(&hvc_iucv_filter_lock); for (index = 0; index < hvc_iucv_filter_size; index++) {
start = hvc_iucv_filter + (8 * index);
end = memchr(start, ' ', 8);
len = (end) ? end - start : 8;
memcpy(buffer + rc, start, len);
rc += len;
buffer[rc++] = ',';
}
read_unlock_bh(&hvc_iucv_filter_lock); if (rc)
buffer[--rc] = '\0'; /* replace last comma and update rc */ return rc;
}
if (!machine_is_vm()) {
pr_notice("The z/VM IUCV HVC device driver cannot " "be used without z/VM\n");
rc = -ENODEV; goto out_error;
}
if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) {
pr_err("%lu is not a valid value for the hvc_iucv= " "kernel parameter\n", hvc_iucv_devices);
rc = -EINVAL; goto out_error;
}
/* parse hvc_iucv_allow string and create z/VM user ID filter list */ if (hvc_iucv_filter_string) {
rc = hvc_iucv_setup_filter(hvc_iucv_filter_string); switch (rc) { case 0: break; case -ENOMEM:
pr_err("Allocating memory failed with " "reason code=%d\n", 3); goto out_error; case -EINVAL:
pr_err("hvc_iucv_allow= does not specify a valid " "z/VM user ID list\n"); goto out_error; case -ENOSPC:
pr_err("hvc_iucv_allow= specifies too many " "z/VM user IDs\n"); goto out_error; default: goto out_error;
}
}
out_error_hvc: for (i = 0; i < hvc_iucv_devices; i++) if (hvc_iucv_table[i])
hvc_iucv_destroy(hvc_iucv_table[i]);
out_error_memory:
mempool_destroy(hvc_iucv_mempool);
kmem_cache_destroy(hvc_iucv_buffer_cache);
out_error:
kfree(hvc_iucv_filter);
hvc_iucv_devices = 0; /* ensure that we do not provide any device */ return rc;
}
/** * hvc_iucv_config() - Parsing of hvc_iucv= kernel command line parameter * @val: Parameter value (numeric)
*/ staticint __init hvc_iucv_config(char *val)
{ if (kstrtoul(val, 10, &hvc_iucv_devices))
pr_warn("hvc_iucv= invalid parameter value '%s'\n", val); return 1;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.